"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeGenerator = void 0;
class CodeGenerator {
    constructor() {
        this.indentLevel = 0;
        this.indentSize = 2;
        this.importCounter = 0;
        // Mapping of Tajik built-in functions to JavaScript equivalents
        this.builtinMappings = new Map([
            // Console functions
            ['чоп', 'console'],
            ['сабт', 'log'],
            ['хато', 'error'],
            ['огоҳӣ', 'warn'],
            ['маълумот', 'info'],
            // Error handling
            ['Хато', 'Error'],
            // Array methods
            ['рӯйхат', 'Array'],
            ['илова', 'push'],
            ['пуш', 'push'],
            ['баровардан', 'pop'],
            ['дарозӣ', 'length'],
            ['харита', 'map'],
            ['филтр', 'filter'],
            ['кофтан', 'find'],
            ['буридан', 'slice'], // Tajik word for slice/cut
            // String methods
            ['сатр_методҳо', 'String'],
            ['дарозии_сатр', 'length'],
            ['пайвастан', 'concat'],
            ['ҷойивазкунӣ', 'replace'],
            ['ҷудокунӣ', 'split'],
            // Object methods
            ['объект', 'Object'],
            ['калидҳо', 'keys'],
            ['қиматҳо', 'values'],
            // Math
            ['математика', 'Math'],
            // Control flow
            ['шикастан', 'break'],
            ['давом', 'continue'],
            ['кӯшиш', 'try'],
            // Async/Promise
            ['ваъда', 'Promise'],
            ['Ваъда', 'Promise'],
            ['гирифтан', 'catch'],
            ['ниҳоят', 'finally'],
            ['партофтан', 'throw'],
            // Async
            ['ҳамзамон', 'async'],
            ['интизор', 'await'],
            ['ваъда', 'Promise'],
            // Note: 'хато' is handled specially in generateIdentifier
        ]);
    }
    generate(ast) {
        return this.generateProgram(ast);
    }
    generateProgram(node) {
        const statements = node.body
            .map(stmt => this.generateStatement(stmt))
            .filter(stmt => stmt.length > 0);
        return statements.join('\n');
    }
    // eslint-disable-next-line complexity
    generateStatement(node) {
        switch (node.type) {
            case 'ImportDeclaration':
                return this.generateImportDeclaration(node);
            case 'ExportDeclaration':
                return this.generateExportDeclaration(node);
            case 'VariableDeclaration':
                return this.generateVariableDeclaration(node);
            case 'FunctionDeclaration':
                return this.generateFunctionDeclaration(node);
            case 'BlockStatement':
                return this.generateBlockStatement(node);
            case 'ReturnStatement':
                return this.generateReturnStatement(node);
            case 'IfStatement':
                return this.generateIfStatement(node);
            case 'WhileStatement':
                return this.generateWhileStatement(node);
            case 'ForStatement':
                return this.generateForStatement(node);
            case 'ExpressionStatement':
                return this.generateExpressionStatement(node);
            case 'TryStatement':
                return this.generateTryStatement(node);
            case 'ThrowStatement':
                return this.generateThrowStatement(node);
            case 'InterfaceDeclaration':
                return this.generateInterfaceDeclaration(node);
            case 'TypeAlias':
                return this.generateTypeAlias(node);
            case 'ClassDeclaration':
                return this.generateClassDeclaration(node);
            case 'SwitchStatement':
                return this.generateSwitchStatement(node);
            case 'BreakStatement':
                return this.indent('break;');
            case 'ContinueStatement':
                return this.indent('continue;');
            default:
                throw new Error(`Unknown statement type: ${node.type}`);
        }
    }
    generateVariableDeclaration(node) {
        const kind = node.kind === 'СОБИТ' ? 'const' : 'let';
        const pattern = this.generatePattern(node.identifier);
        const init = node.init ? ` = ${this.generateExpression(node.init)}` : '';
        return this.indent(`${kind} ${pattern}${init};`);
    }
    generateFunctionDeclaration(node) {
        const async = node.async ? 'async ' : '';
        const name = this.generateIdentifier(node.name);
        // Handle new Parameter type or legacy Identifier type
        const params = node.params
            .map(param => {
            if (param.type === 'Parameter') {
                const p = param;
                return this.generateIdentifier(p.name);
            }
            else {
                // Legacy Identifier type
                return this.generateIdentifier(param.name);
            }
        })
            .join(', ');
        const body = this.generateBlockStatement(node.body);
        return this.indent(`${async}function ${name}(${params}) ${body}`);
    }
    generateBlockStatement(node) {
        if (node.body.length === 0) {
            return '{}';
        }
        this.indentLevel++;
        const statements = node.body
            .map(stmt => this.generateStatement(stmt))
            .filter(stmt => stmt.length > 0);
        this.indentLevel--;
        return `{\n${statements.join('\n')}\n${this.getIndent()}}`;
    }
    generateReturnStatement(node) {
        const argument = node.argument ? ` ${this.generateExpression(node.argument)}` : '';
        return this.indent(`return${argument};`);
    }
    generateIfStatement(node) {
        const test = this.generateExpression(node.test);
        const consequent = this.generateStatement(node.consequent);
        let result = this.indent(`if (${test}) `);
        if (node.consequent.type === 'BlockStatement') {
            result += consequent.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${consequent}\n${this.getIndent()}}`;
        }
        if (node.alternate) {
            result += ' else ';
            if (node.alternate.type === 'BlockStatement') {
                result += this.generateStatement(node.alternate).replace(this.getIndent(), '');
            }
            else if (node.alternate.type === 'IfStatement') {
                result += this.generateStatement(node.alternate).replace(this.getIndent(), '');
            }
            else {
                result += `{\n${this.generateStatement(node.alternate)}\n${this.getIndent()}}`;
            }
        }
        return result;
    }
    generateWhileStatement(node) {
        const test = this.generateExpression(node.test);
        const body = this.generateStatement(node.body);
        let result = this.indent(`while (${test}) `);
        if (node.body.type === 'BlockStatement') {
            result += body.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${body}\n${this.getIndent()}}`;
        }
        return result;
    }
    generateForStatement(node) {
        const init = node.init ? this.generateStatement(node.init).trim().replace(/;$/, '') : '';
        const test = node.test ? this.generateExpression(node.test) : '';
        const update = node.update ? this.generateExpression(node.update) : '';
        const body = this.generateStatement(node.body);
        let result = this.indent(`for (${init}; ${test}; ${update}) `);
        if (node.body.type === 'BlockStatement') {
            result += body.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${body}\n${this.getIndent()}}`;
        }
        return result;
    }
    generateExpressionStatement(node) {
        const expr = node.expression;
        // Special handling: Skip orphaned method signatures that should not generate executable code
        // This happens when interface method signatures escape the interface context due to parsing issues
        if (expr.type === 'CallExpression') {
            const callExpr = expr;
            // Check if this looks like an orphaned method signature (function call with single parameter
            // that looks like it should be a type parameter)
            if (callExpr.callee.type === 'Identifier' &&
                callExpr.arguments.length === 1 &&
                callExpr.arguments[0].type === 'Identifier') {
                const calleeName = callExpr.callee.name;
                const argName = callExpr.arguments[0].name;
                // Skip if this looks like a method signature pattern
                // Common patterns: танзим_кардан(нави_қимат), ғайр_танзим(параметр), etc.
                if (calleeName.includes('_') ||
                    argName.includes('_') ||
                    calleeName.length > 8 ||
                    argName.length > 8) {
                    return ''; // Skip generating this statement
                }
            }
        }
        return this.indent(`${this.generateExpression(node.expression)};`);
    }
    // eslint-disable-next-line complexity
    generateExpression(node) {
        // Use a more direct delegation approach
        const simpleExpressions = [
            'Identifier',
            'Literal',
            'TemplateLiteral',
            'ThisExpression',
            'Super',
        ];
        if (simpleExpressions.includes(node.type)) {
            return this.generateSimpleExpression(node);
        }
        const operatorExpressions = ['BinaryExpression', 'UnaryExpression', 'UpdateExpression'];
        if (operatorExpressions.includes(node.type)) {
            return this.generateOperatorExpression(node);
        }
        const callExpressions = ['CallExpression', 'AssignmentExpression', 'MemberExpression'];
        if (callExpressions.includes(node.type)) {
            return this.generateCallAssignmentExpression(node);
        }
        const structuralExpressions = ['ArrayExpression', 'ObjectExpression', 'SpreadElement'];
        if (structuralExpressions.includes(node.type)) {
            return this.generateStructuralExpression(node);
        }
        const specialExpressions = ['AwaitExpression', 'NewExpression', 'ImportExpression'];
        if (specialExpressions.includes(node.type)) {
            return this.generateSpecialExpression(node);
        }
        return this.handleUnknownExpression(node);
    }
    generateSimpleExpression(node) {
        switch (node.type) {
            case 'Identifier':
                return this.generateIdentifier(node);
            case 'Literal':
                return this.generateLiteral(node);
            case 'TemplateLiteral':
                return this.generateTemplateLiteral(node);
            case 'ThisExpression':
                return 'this';
            case 'Super':
                return 'super';
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateOperatorExpression(node) {
        switch (node.type) {
            case 'BinaryExpression':
                return this.generateBinaryExpression(node);
            case 'UnaryExpression':
                return this.generateUnaryExpression(node);
            case 'UpdateExpression':
                return this.generateUpdateExpression(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateCallAssignmentExpression(node) {
        switch (node.type) {
            case 'CallExpression':
                return this.generateCallExpression(node);
            case 'AssignmentExpression':
                return this.generateAssignmentExpression(node);
            case 'MemberExpression':
                return this.generateMemberExpression(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateStructuralExpression(node) {
        switch (node.type) {
            case 'ArrayExpression':
                return this.generateArrayExpression(node);
            case 'ObjectExpression':
                return this.generateObjectExpression(node);
            case 'SpreadElement':
                return this.generateSpreadElement(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateSpecialExpression(node) {
        switch (node.type) {
            case 'AwaitExpression':
                return this.generateAwaitExpression(node);
            case 'NewExpression':
                return this.generateNewExpression(node);
            case 'ImportExpression':
                return this.generateImportExpression(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateImportExpression(node) {
        // Dynamic import: ворид(specifier) -> import(specifier)
        let source = this.generateExpression(node.source);
        // Handle .som extension conversion for dynamic imports
        if (source.includes('.som')) {
            source = source.replace(/\.som/g, '.js');
        }
        else if (source.match(/^["']\.\.?\/[^"']*["']$/) && !source.includes('.js')) {
            // For relative imports without extension, add .js
            source = source.replace(/["']$/, '.js"').replace(/^'/, '"');
        }
        return `import(${source})`;
    }
    handleUnknownExpression(node) {
        throw new Error(`Unknown expression type: ${node.type}`);
    }
    generateImportDeclaration(node) {
        const specifiers = node.specifiers;
        let source = this.generateLiteral(node.source);
        // Module resolution: convert .som extensions to .js
        if (source.includes('.som')) {
            source = source.replace(/\.som"/g, '.js"').replace(/\.som'/g, ".js'");
        }
        const results = [];
        const tmpVar = `__somon_import_${this.importCounter++}`;
        results.push(this.indent(`const ${tmpVar} = require(${source});`));
        // Handle default imports
        const defaultImports = specifiers.filter(s => s.type === 'ImportDefaultSpecifier');
        if (defaultImports.length > 0) {
            const localName = defaultImports[0].local.name;
            results.push(this.indent(`const ${localName} = ${tmpVar}.default ?? ${tmpVar};`));
        }
        const namespaceImport = specifiers.find(s => s.type === 'ImportNamespaceSpecifier');
        if (namespaceImport) {
            results.push(this.indent(`const ${namespaceImport.local.name} = ${tmpVar};`));
        }
        // Handle named imports
        const namedImports = specifiers.filter(s => s.type === 'ImportSpecifier');
        if (namedImports.length > 0) {
            const destructuring = namedImports
                .map(spec => {
                const imported = spec.imported.name;
                const local = spec.local.name;
                return imported === local ? imported : `${imported}: ${local}`;
            })
                .join(', ');
            results.push(this.indent(`const { ${destructuring} } = ${tmpVar};`));
        }
        return results.join('\n');
    }
    generateExportDeclaration(node) {
        if (node.declaration) {
            const declaration = this.generateStatement(node.declaration);
            // Extract the name from the declaration for CommonJS export
            let exportName = '';
            if (node.declaration.type === 'FunctionDeclaration') {
                const funcDecl = node.declaration;
                exportName = funcDecl.name.name;
            }
            else if (node.declaration.type === 'VariableDeclaration') {
                const varDecl = node.declaration;
                exportName = varDecl.identifier.name;
            }
            else if (node.declaration.type === 'ClassDeclaration') {
                const classDecl = node.declaration;
                exportName = classDecl.name.name;
            }
            // Generate CommonJS export
            // For default exports, we need to be careful not to override named exports
            const commonjsExport = node.default
                ? `module.exports.default = ${exportName};`
                : `module.exports.${exportName} = ${exportName};`;
            return declaration + '\n' + this.indent(commonjsExport);
        }
        return '';
    }
    generateIdentifier(node) {
        // Only map specific built-in identifiers, not general variable names
        // This prevents variable names like 'рӯйхат' from being mapped to 'Array'
        // Map built-in literals
        if (node.name === 'беқимат') {
            return 'undefined';
        }
        // Special case: don't map 'хато' when it's used as Error constructor
        if (node.name === 'хато') {
            // This is a bit of a hack - we need context to know if it's console.error or Error
            // For now, we'll assume standalone 'хато' means Error, and 'чоп.хато' means console.error
            return 'Error';
        }
        // Handle Хато (capitalized) as Error constructor
        if (node.name === 'Хато') {
            return 'Error';
        }
        // Don't map variable names that could conflict with JS built-ins
        // Only map in specific contexts (handled in generateMemberExpression)
        return node.name;
    }
    generateLiteral(node) {
        if (typeof node.value === 'string') {
            // Properly escape string literals
            const escaped = node.value
                .replace(/\\/g, '\\\\') // Escape backslashes first
                .replace(/"/g, '\\"') // Escape quotes
                .replace(/\n/g, '\\n') // Escape newlines
                .replace(/\t/g, '\\t') // Escape tabs
                .replace(/\r/g, '\\r'); // Escape carriage returns
            return `"${escaped}"`;
        }
        if (node.value === null) {
            return 'null';
        }
        return String(node.value);
    }
    generateTemplateLiteral(node) {
        let result = '`';
        for (let i = 0; i < node.quasis.length; i++) {
            const quasi = node.quasis[i];
            // Add the text part
            result += quasi.value.raw;
            // Add the expression part if it exists
            if (i < node.expressions.length) {
                result += '${';
                result += this.generateExpression(node.expressions[i]);
                result += '}';
            }
        }
        result += '`';
        return result;
    }
    generateBinaryExpression(node) {
        const left = this.generateExpression(node.left);
        const right = this.generateExpression(node.right);
        // Handle operator precedence with parentheses when needed
        const needsParens = this.needsParentheses(node);
        const expr = `${left} ${node.operator} ${right}`;
        return needsParens ? `(${expr})` : expr;
    }
    generateUnaryExpression(node) {
        const argument = this.generateExpression(node.argument);
        return `${node.operator}${argument}`;
    }
    generateUpdateExpression(node) {
        const argument = this.generateExpression(node.argument);
        if (node.prefix) {
            return `${node.operator}${argument}`;
        }
        else {
            return `${argument}${node.operator}`;
        }
    }
    generateCallExpression(node) {
        let callee = this.generateExpression(node.callee);
        // Special handling for нишондиҳӣ function
        if (callee === 'нишондиҳӣ') {
            callee = 'console.log';
        }
        const args = node.arguments.map(arg => this.generateExpression(arg)).join(', ');
        return `${callee}(${args})`;
    }
    generateAssignmentExpression(node) {
        const left = this.generateExpression(node.left);
        const right = this.generateExpression(node.right);
        return `${left} ${node.operator} ${right}`;
    }
    generateMemberExpression(node) {
        let object = this.generateExpression(node.object);
        let property = this.generateExpression(node.property);
        // Apply built-in mappings for object names only for specific built-ins
        let objectMapped = false;
        if (node.object.type === 'Identifier') {
            const objectName = node.object.name;
            // Only map specific built-in objects, not user variables
            const builtinObjects = ['чоп', 'математика', 'рӯйхат'];
            if (builtinObjects.includes(objectName)) {
                const mappedObject = this.builtinMappings.get(objectName);
                if (mappedObject) {
                    object = mappedObject;
                    objectMapped = true;
                }
            }
        }
        // Apply built-in mappings for property names if the object was mapped OR for common array/string methods
        if (!node.computed && node.property.type === 'Identifier') {
            const propertyName = node.property.name;
            const mappedProperty = this.builtinMappings.get(propertyName);
            // Always map common array and string methods
            const commonMethods = [
                'пуш',
                'илова',
                'баровардан',
                'дарозӣ',
                'харита',
                'филтр',
                'кофтан',
                'пайвастан',
                'ҷойивазкунӣ',
                'ҷудокунӣ',
                'буридан', // slice
            ];
            if (mappedProperty && (objectMapped || commonMethods.includes(propertyName))) {
                property = mappedProperty;
            }
        }
        // Special case: чоп.хато should become console.error
        if (object === 'console' && property === 'Error') {
            property = 'error';
        }
        if (node.computed) {
            return `${object}[${property}]`;
        }
        else {
            return `${object}.${property}`;
        }
    }
    generateArrayExpression(node) {
        const elements = node.elements.map(element => this.generateExpression(element));
        return `[${elements.join(', ')}]`;
    }
    generateObjectExpression(node) {
        const properties = node.properties
            .map((prop) => {
            const key = prop.computed
                ? `[${this.generateExpression(prop.key)}]`
                : this.generateExpression(prop.key);
            const value = this.generateExpression(prop.value);
            return `${key}: ${value}`;
        })
            .join(', ');
        return `{${properties}}`;
    }
    generateTryStatement(node) {
        let result = this.indent('try ') + this.generateBlockStatement(node.block).replace(this.getIndent(), '');
        if (node.handler) {
            result += ' catch ';
            if (node.handler.param) {
                result += `(${this.generateIdentifier(node.handler.param)}) `;
            }
            else {
                result += '(error) ';
            }
            result += this.generateBlockStatement(node.handler.body).replace(this.getIndent(), '');
        }
        if (node.finalizer) {
            result += ' finally ';
            result += this.generateBlockStatement(node.finalizer).replace(this.getIndent(), '');
        }
        return result;
    }
    generateThrowStatement(node) {
        const argument = this.generateExpression(node.argument);
        return this.indent(`throw ${argument};`);
    }
    generateAwaitExpression(node) {
        const argument = this.generateExpression(node.argument);
        return `await ${argument}`;
    }
    generateNewExpression(node) {
        const callee = this.generateExpression(node.callee);
        const args = node.arguments
            ? node.arguments.map((arg) => this.generateExpression(arg)).join(', ')
            : '';
        return `new ${callee}(${args})`;
    }
    generateInterfaceDeclaration(node) {
        // Interfaces are TypeScript-only constructs, so we generate a comment in JavaScript
        // They should NOT generate any executable code at all
        const name = this.generateIdentifier(node.name);
        return this.indent(`// Interface: ${name}`);
    }
    generateTypeAlias(node) {
        // Type aliases are TypeScript-only constructs, so we generate a comment in JavaScript
        const name = this.generateIdentifier(node.name);
        return this.indent(`// Type alias: ${name}`);
    }
    generateClassDeclaration(node) {
        const className = this.generateIdentifier(node.name);
        const extendsClause = node.superClass
            ? ` extends ${this.generateIdentifier(node.superClass)}`
            : '';
        const abstractModifier = node.abstract
            ? 'abstract '
            : '';
        let classBody = '';
        // Generate class members
        if (node.body && node.body.body) {
            const members = node.body.body
                .map(member => {
                switch (member.type) {
                    case 'MethodDefinition':
                        return this.generateMethodDefinition(member);
                    case 'PropertyDefinition':
                        return this.generatePropertyDefinition(member);
                    default:
                        return '';
                }
            })
                .filter((member) => member.length > 0);
            if (members.length > 0) {
                this.indentLevel++;
                classBody = '\n' + members.join('\n') + '\n' + this.getIndent();
                this.indentLevel--;
            }
        }
        return this.indent(`${abstractModifier}class ${className}${extendsClause} {${classBody}}`);
    }
    generateMethodDefinition(node) {
        const methodName = node.kind === 'constructor' ? 'constructor' : this.generateIdentifier(node.key);
        const isStatic = node.static ? 'static ' : '';
        const isAbstract = node.abstract
            ? 'abstract '
            : '';
        // Generate parameters
        const params = node.value.params
            ? node.value.params.map(param => this.generateIdentifier(param.name)).join(', ')
            : '';
        // Generate method body or abstract signature
        if (node.abstract) {
            // Abstract methods have no body
            return this.indent(`${isAbstract}${isStatic}${methodName}(${params});`);
        }
        else {
            // Regular methods have a body
            const body = this.generateBlockStatement(node.value.body);
            return this.indent(`${isStatic}${methodName}(${params}) ${body}`);
        }
    }
    generatePropertyDefinition(node) {
        const propertyName = this.generateIdentifier(node.key);
        const isStatic = node.static ? 'static ' : '';
        const initializer = node.value ? ` = ${this.generateExpression(node.value)}` : '';
        return this.indent(`${isStatic}${propertyName}${initializer};`);
    }
    needsParentheses(node) {
        // Simple precedence check - in a full implementation, this would be more sophisticated
        // For now, always return false to avoid complexity
        node; // Use the parameter to avoid unused warning
        // For now, we'll be conservative and add parentheses for nested binary expressions
        return node.left.type === 'BinaryExpression' || node.right.type === 'BinaryExpression';
    }
    generateSwitchStatement(node) {
        const discriminant = this.generateExpression(node.discriminant);
        this.indentLevel++;
        const cases = node.cases
            .map((switchCase) => {
            if (!switchCase.test) {
                const consequent = switchCase.consequent
                    .map(stmt => this.generateStatement(stmt))
                    .join('\n');
                return this.indent(`default:\n${consequent}`);
            }
            else {
                const test = this.generateExpression(switchCase.test);
                const consequent = switchCase.consequent
                    .map(stmt => this.generateStatement(stmt))
                    .join('\n');
                return this.indent(`case ${test}:\n${consequent}`);
            }
        })
            .join('\n');
        this.indentLevel--;
        return this.indent(`switch (${discriminant}) {\n${cases}\n${this.getIndent()}}`);
    }
    // Pattern generation methods
    generatePattern(node) {
        switch (node.type) {
            case 'Identifier':
                return this.generateIdentifier(node);
            case 'ArrayPattern':
                return this.generateArrayPattern(node);
            case 'ObjectPattern':
                return this.generateObjectPattern(node);
            default:
                throw new Error('Unknown pattern type');
        }
    }
    generateArrayPattern(node) {
        const elements = node.elements
            .map(element => {
            if (element === null) {
                return '';
            }
            else if (element.type === 'SpreadElement') {
                return this.generateSpreadElement(element);
            }
            else {
                return this.generatePattern(element);
            }
        })
            .join(', ');
        return `[${elements}]`;
    }
    generateObjectPattern(node) {
        const properties = node.properties
            .map(prop => {
            if (prop.type === 'SpreadElement') {
                return this.generateSpreadElement(prop);
            }
            else {
                return this.generatePropertyPattern(prop);
            }
        })
            .join(', ');
        return `{${properties}}`;
    }
    generatePropertyPattern(node) {
        const key = node.computed
            ? `[${this.generateExpression(node.key)}]`
            : this.generateIdentifier(node.key);
        if (node.key.type === 'Identifier' &&
            node.value.type === 'Identifier' &&
            node.key.name === node.value.name) {
            return key;
        }
        else {
            const value = this.generatePattern(node.value);
            return `${key}: ${value}`;
        }
    }
    generateSpreadElement(node) {
        const argument = this.generateExpression(node.argument);
        return `...${argument}`;
    }
    indent(text) {
        return this.getIndent() + text;
    }
    getIndent() {
        return ' '.repeat(this.indentLevel * this.indentSize);
    }
}
exports.CodeGenerator = CodeGenerator;
//# sourceMappingURL=codegen.js.map