var patchTemplate = ";(" +function() {
    // automatically generated by architect-build
    /*global main*/
    var moduleMap = require("./module-map.json");
    var Module = require("module");
    var root = __dirname;
    var path = require('path');
    Module._resolveFilename_orig = Module._resolveFilename;
    function relPath(filename) {
        return path.relative(root, filename + "").replace(/\\/g, "/");
    }
    Module._resolveFilename = function(id, parent) {    
        var parentId = parent && relPath(parent.filename);
        var key = /^[.][.\/]/.test(id)
            ? relPath(path.join(path.dirname(parentId), id))
            : id + "|" + parentId;
        
        if (moduleMap[key])
            return path.join(root, moduleMap[key]);
        var realPath = Module._resolveFilename_orig(id, parent);
        // if (realPath != id) console.log(id, key);
        return realPath;
    };
    main.onResolvePlugins = function(plugins, base) {
        var resolvedConfig = moduleMap["!config"];
        plugins.forEach(function(plugin, index) {
            // Shortcut where string is used for plugin without any options.
            if (typeof plugin === "string") 
                plugin = plugins[index] = { packagePath: plugin };
            plugin.disableUnpackagedClient = true;
            // The plugin is a package on the disk.  We need to load it.
            if (plugin.hasOwnProperty("packagePath") && !plugin.hasOwnProperty("setup")) {
                try {
                    var defaults = resolvedConfig[plugin.packagePath];
                    plugin.packagePath = path.join(root, defaults.packagePath);
                    plugin.consumes = defaults.consumes;
                    plugin.provides = defaults.provides;
                    plugin.setup = require(plugin.packagePath);
                } catch(e) {}
            }
        });
    };
} + ")();";

//////////////////////////////////////////////////////////////////////////

var Module = require("module");
var path = require("path");
var fs = require("fs");
var convertPath = require("./copy").convertPath;
var loaded, moduleMap, root;
var ignore;
// loader modules that have non static require
var LOADERS = {
    "amd-loader": 1,
    "architect": 1,
    "server.js": 1,
    "vfs-local": 1,
    "architect-build/build": 1,
};

function addModule(id, parent) {
    if (ignore && ignore(id)) return;
    if (!parent) {
        parent = {
            relPath: id,
            id: id,
            filename: root,
            paths: [root] // Module._nodeModulePaths(path.dirname(filename))
        };
    }
    var filename, text;
    if (id.indexOf("!") != -1) {
        
    }
    try {
        filename = Module._resolveFilename(id, parent);
    } catch(e) {
        console.error(id);
    }
    if (filename == parent.filename)
        return;
    var key = /^[.][.\/]/.test(id)
        ? relPath(path.join(path.dirname(parent.id), id))
        : id + "|" + parent.relPath;
        
    if (!/[\\\/]/.test(filename) || moduleMap[key] !== undefined)
        return;
    try {
        text = fs.readFileSync(filename, "utf8")
            .replace(/^\s*\/\/.+|^\s*\/\*[\s\S]*?\*\//gm, "");
    } catch(e) {
        moduleMap[key] = "";
        return;
    }
    var re = /require\s*(?:\.\s*resolve\s*)?\(([^\)]+)\)/g, m;
    var children = [];
    while ((m = re.exec(text))) {
        var name = m[1].trim();
        if (!/^(['"])([^'"]*)\1$/.test(name)) {
            m = /^(['"])([^'"]+)\1\s*[+]\s*\w+(\s*[+]\s*['"]\.js['"])?$/.exec(name);
            if (m) {
                var dirname = m[2];
                if (dirname.slice(-1) !== "/")
                    dirname = path.dirname(dirname) + "/";
                var dirpath = path.join(path.dirname(filename), dirname);
                try {
                    var files = fs.readdirSync(dirpath);
                    files.forEach(function(f) {
                        var ext = f.substr(f.lastIndexOf("."));
                        if (ext != ".js") return;
                        if (!m[3]) f = f.slice(0, -ext.length);
                        children.push(dirname + f);
                    });
                    console.warn("adding drectory ", dirpath, "for", key);
                    console.warn("----| " + m[0]);
                    continue;
                } catch(e) {}
            }
            var shortKey = key.replace(/\|[^|]+$/, "");
            if (!LOADERS[shortKey])
                console.error("non static require ", name, "from", shortKey);
        } else {
            children.push(name.slice(1, -1));
        }
    }
    if (/\.json$/.test(filename))
        children = [];
    
    var module = {
        relPath: relPath(filename),
        id: filename,
        filename: filename,
        paths: Module._nodeModulePaths(path.dirname(filename))
    };
    
    loaded[filename] = module;
    moduleMap[key] = module.relPath;
    children.forEach(function(id) {
        addModule(id, module);
    });
}

function relPath(filename) {
    return path.relative(root, filename).replace(/\\/g, "/");
}

function build(base, args, cb) {
    root = path.resolve(base);
    args.push("--_getConfig", "true");
    loaded = Object.create(null);
    moduleMap = Object.create(null);

    var server = require(root + "/server");
    server.onResolvePlugins = function(plugins, base) {
        base = relPath(base);
        plugins.forEach(function(plugin, index) {
            if (typeof plugin === "string") 
                plugin = plugins[index] = { packagePath: plugin };
            plugin.packagePath_orig = plugin.packagePath;
        });
    };
    server(args, null, function(err, config, configPath) {
        addModule("server.js");
        addModule(configPath);
        config.forEach(function(plugin) {
            if (plugin.packagePath)
                addModule(relPath(plugin.packagePath));
        });
        var resolvedConfig = moduleMap["!config"] = {};
        config.forEach(function(plugin) {
            if (plugin.packagePath && plugin.packagePath_orig) {
                resolvedConfig[plugin.packagePath_orig] = {
                    packagePath: relPath(Module._resolveFilename(plugin.packagePath)),
                    consumes: plugin.consumes,
                    provides: plugin.provides
                };
            }
        });
        function unique(array) {
            var map = Object.create(null);
            array.forEach(function(x) {map[x] = 1});
            return Object.keys(map);
        }
        var roots = unique(Object.keys(loaded).map(function(x) {
            x = x.replace(/([\\\/](?:plugins|node_modules)[\\\/][^\\\/]+).*?$/, "$1");
            return relPath(x);
        }));
        
        // console.log(loaded)
        
        cb && cb(null, {
            roots: roots,
            moduleMap: moduleMap,
            loaded: loaded
        });
    });
}

/*
root = "/newclient"
dest = "/build/webkitbuilds/app.nw"
var args = ["local","-s", "local"];
*/
module.exports = function(options, cb) {
    var dest = options.dest && convertPath(options.dest);
    var root = convertPath(options.root);
    var args = options.args;
    ignore = options.ignore && function(id) {
        return options.ignore.test(id);
    };
    build(root, args, function(err, result) {
        if (dest) {
            var serverSource = fs.readFileSync(root + "/server.js", "utf8");
            serverSource = serverSource.replace(/^(#.*|"use strict";?|\s)*/, function(a) {
                return a.trim() + "\n\n" + patchTemplate + "\n\n";
            });
            fs.writeFileSync(dest + "/server.js", serverSource, "utf8");
            fs.writeFileSync(dest + "/module-map.json", JSON.stringify(result.moduleMap), "utf8");
        }
        
        cb(null, result);
    });
};

