var moduleDeps = require("./module-deps");
var fs = require("fs");
var send = require("send");

exports.transform = function(code, options) {
    if (/^"disable compress"/.test(code))
        return code;
    
    if (options.amd && moduleDeps.isCJS(code)) {
        code = "define(function(require, exports, module) {" + code + "\n})";
    }
    
    if (options.mode == "ts" || options.mode == "es5") {
        var ts = require("typescript");
        return ts.transpileModule(code, {
            compilerOptions: {
                downlevelIteration: true,
                suppressExcessPropertyErrors: true,
                removeComments: true,
                newLine: "lf",
                module: ts.ModuleKind.CommonJS,
                sourceMap: !!options.srcPath,
                inlineSourceMap: true
            },
            fileName: options.srcPath
        }).outputText;
    }
    return code;
};


var cache = Object.create(null);
exports.sendFile = function(req, res, next) {
    var path = req.params.path;
    var transform = req.params.transform;
    var filePath = moduleDeps.resolveModulePath(path, req.pathConfig.pathMap);
    
    if (!/\.(js|ts)$/.test(filePath) || /(browserified|\.min|test\d)\.js$/.test(filePath)) {
        return send(req, filePath.substr(req.pathConfig.root.length))
            .root(req.pathConfig.root)
            .on('error', next)
            .pipe(res);
    }
    
    function resolvePath(callback) {
        fs.stat(filePath, function(err, stat) {
            if (err && err.code == "ENOENT") {
                var tsPath = filePath.replace(/\.js$/, ".ts");
                if (tsPath != filePath) {
                    return fs.stat(tsPath, function(err, stat) {
                        callback(err, stat, tsPath);
                    });
                }
            }
            callback(err, stat);
        });
    }
    
    resolvePath(function(err, stat, srcPath) {
        if (err) return next(err);
        var mtime = stat.mtime.valueOf();
        var etag = 'W/"' + stat.size.toString(16) + "-" + mtime.toString(16) + '"';
        
        var noneMatch = req.headers && req.headers['if-none-match'];
        
        if (noneMatch && noneMatch == etag) {
            res.statusCode = 304;
            res.end();
            return;
        }
        
        var key = transform + "|" + path;
        if (cache[key] && cache[key].etag == etag)
            return sendResponse(cache[key].value);
        res.setHeader("ETag", etag);
        cache[key] = null;
        fs.readFile(srcPath || filePath, "utf8", function(err, value) {
            if (err)
                return next(err);
            var t = Date.now();
            try {
                value = exports.transform(value, {
                    path: filePath,
                    mode: srcPath ? "ts" : transform,
                    amd: true,
                    srcPath: srcPath ? path.replace(/\.js/, ".ts") : ""
                });
            } catch (e) {
                return next(e);
            }
            cache[key] = { value: value, etag: etag };
            var delta = Date.now() - t;
            res.setHeader('Server-Timing', 'transform=' + delta);
            sendResponse(value);
        });
        
        function sendResponse(value) {
            res.setHeader('Content-Type', 'application/javascript');
            
            res.setHeader('Cache-Control', 'public, max-age=0');
            res.setHeader('Last-Modified', stat.mtime.toUTCString());
            res.end(value);
        }
    });
};

