var https = require("https")
  , http = require("http")
  , url = require("url")
  , fs = require("fs");

var Path = require("path");
var spawn = require("child_process").spawn;
var async = require("asyncjs");
var rootDir = __dirname + "/../lib/ace/";

var deps = {
    csslint: {
        path: "mode/css/csslint.js",
        url: "https://raw.github.com/stubbornella/csslint/master/release/csslint-node.js",
        needsFixup: true
    }, 
    requirejs: {
        path: "../../demo/kitchen-sink/require.js",
        url: "https://raw.github.com/jrburke/requirejs/master/require.js",
        needsFixup: false
    },
    luaparse: {
        path: "mode/lua/luaparse.js",
        url: "https://raw.github.com/oxyc/luaparse/master/luaparse.js",
        needsFixup: true,
        postProcess: function(src) {
            return src.replace(
                /\(function\s*\(root,\s*name,\s*factory\)\s*{[\s\S]*?}\(this,\s*'luaparse',/,
                "(function (root, name, factory) {\n   factory(exports)\n}(this, 'luaparse',"
            )
        }
    },
    jshint: {
        path: "mode/javascript/jshint.js",
        url: "http://jshint.com/get/jshint-2.1.9.js",
        fetch: function(_, cb) {
            //run("npm install jshint@latest", function() {
                var base = Path.join(__dirname, "/node_modules/jshint/")
                var path = Path.join(base, "/src/stable/jshint")
                run('browserify ' + path + ' -i console-browserify -r jshint', function(err, src) {
                    src = replaceConsoleBrowserify(src);
                    var version = JSON.parse(fs.readFileSync(base + "package.json")).version
                    src = [ "// " + version,
                        "var JSHINT;",
                        "(function () {",
                        "require = null;",
                        src,
                        "JSHINT = require('jshint').JSHINT;",
                        "}());"
                    ].join("\n")
                    cb(err, src);
                })
            //});
        },
        needsFixup: true,
        postProcess: function(src) {
            src = src.replace(/(\],|\{)((?:\d+|"\w+"):\[)/g, "$1\n$2")
                .replace(/^(\},)(\{[^{}\[\]]*?\}\])/gm, "$1\n$2")

            src = src.replace(
                /"Expected a conditional expression and instead saw an assignment."/g,
                '"Assignment in conditional expression"'
            );

            src = src.replace(/\brequire\(["']|\(require,|\(require\)/g, function(r){
                return r.replace("require", "req");
            })

            src = src.replace(/var defaultMaxListeners = 10;/, function(a) {return a.replace("10", "200")});

            src = src.replace(/var JSHINT;\s*\(function[\s()]+\{\s*/, "")
                .replace(/JSHINT = .*\n\s*\}\(\)\);\s*/, "");
            src += '\n'
                + 'function req() {return require.apply(this, arguments)}\n'
                + 'module.exports = req("jshint");\n';
            return src;
        }
    }, 
    emmet: {
        path: "ext/emmet core.js",
        url: [
            "https://raw.github.com/sergeche/emmet-sublime/master/emmet/emmet-app.js",
            "https://raw.github.com/sergeche/emmet-sublime/master/emmet/snippets.json"
        ],
        postProcess: function(data) {
            return data[0]
                .replace("define(emmet)", "define('emmet', [], emmet)")
                .replace(/(emmet.define\('bootstrap'.*)[\s\S]*$/, function(_, x) {
                    return x + "\n" +
                        "var snippets = " + data[1] + ";\n" +
                        "var res = require('resources');\n" +
                        "var userData = res.getVocabulary('user') || {};\n" +
                        "res.setVocabulary(require('utils').deepMerge(userData, snippets), 'user');\n" +
                        "});";
            });
        }
    },
    coffee: {
        fetch: function(){
            var rootHref = "https://raw.github.com/jashkenas/coffee-script/master/";
            var path = "mode/coffee/";

            var subDir = "lib/coffee-script/";
            var deps = [
                "helpers.js",
                "lexer.js",
                "nodes.js",
                "parser.js",
                "rewriter.js",
                "scope.js"
            ].map(function(x) {
                return {
                    name: x,
                    href: rootHref + subDir + x,
                    path: rootDir + path + x
                };
            });
            deps.push({name:"LICENSE", href: rootHref + "LICENSE"});

            var downloads = {}, counter = 0;

            deps.forEach(function(x) {
                download(x.href, function(err, data) {
                    counter++;
                    downloads[x.name] = data;
                    if (counter == deps.length)
                        allDone();
                });
            });
            function allDone() {
                deps.pop();
                var license = downloads.LICENSE.split('\n');
                license = "/**\n * " + license.join("\n * ") + "\n */";

                deps.forEach(function(x) {
                    var data = downloads[x.name];
                    console.log(x.name);
                    console.log(!data);
                    if (!data)
                        return;
                    if (x.name == "parser.js") {
                        data = data.replace("var parser = (function(){", "")
                            .replace(/\nreturn (new Parser)[\s\S]*$/, "\n\nmodule.exports = $1;\n\n");
                    } else {
                        data = data.replace("(function() {", "")
                            .replace(/\}\).call\(this\);\s*$/, "");
                    }
                    data = license
                        + "\n\n"
                        + "define(function(require, exports, module) {\n"
                        + data
                        + "\n});";

                    fs.writeFile(x.path, data, "utf-8", function(err){
                        if (err) throw err;
                        console.log("File " + x.name + " saved.");
                        console.warn("mode/coffee/coffee-script file needs to updated manually");
                        console.warn("mode/coffee/parser.js: parseError function needs to be modified");
                    });
                });
            }
        }
    }
};

var download = function(href, callback) {
    if (Array.isArray(href))
        return async.map(href, download, callback);

    var options = url.parse(href);
    var protocol = options.protocol === "https:" ? https : http;
    console.log("connecting to " + options.host + " " + options.path);
    protocol.get(options, function(res) {
        var data = "";
        res.setEncoding("utf-8");

        res.on("data", function(chunk){
            data += chunk;
        });

        res.on("end", function(){
            callback(null, data);
        });
    });
};

var getDep = function(dep) {
    if (!dep.fetch)
        dep.fetch = download
    dep.fetch(dep.url, function(err, data) {
        if (dep.postProcess)
            data = dep.postProcess(data);
        if (dep.needsFixup)
            data = "define(function(require, exports, module) {\n"
                + data
                + "\n});";

        fs.writeFile(rootDir + dep.path, data, "utf-8", function(err){
            if (err) throw err;
            console.log("File " + dep.path + " saved.");
        });
    });
};

function run(cmd, cb) {
	var proc = process.platform == "win32"
        ? spawn("cmd", ["/c", cmd], {cwd: __dirname})
        : spawn("bash", ["-c", cmd], {cwd: __dirname});

    var result = "", err = "";
	proc.stderr.setEncoding("utf8");    
	proc.stderr.on('data', function (data) {
        err += data;
	});

	proc.stdout.setEncoding("utf8");
	proc.stdout.on('data', function (data) {
        result += data;
	});

    proc.on('exit', done);
    proc.on('close', done);
    function done(code) {
        if (code !== 0) {
            console.error(cmd + '::: process exited with code :::' + code);
            console.error(err)
        }
        cb(err, result)
    }
}

function unquote(str) {
    return str.replace(/\\(.)/g, function(x, a) {
        return a == "n" ? "\n" 
            : a == "t" ? "\t" 
            : a == "r" ? "\r"
            : a
    });
}

function replaceConsoleBrowserify(str) {
    var simpleConsole = function(req,module,exports){
        ["log", "info", "warn", "error", 
        "time","timeEnd", "trace", "dir", "assert"
        ].forEach(function(x) {exports[x] = nop;});
        function nop() {}
    }
    var m = /"console-browserify":(\d+)/.exec(str);
    name = m && m[1];
    if (name) {
        str = str.replace(
            "{1:[",
            "{" + name + ":[" + simpleConsole + ",{}],\n1:["
        )
    }
    return str;
}


getDep(deps.jshint)