// LICENSE_CODE ZON
'use strict'; /*jslint node:true, es6:false*/ // ios
require('./config.js');
var node_module = require('module');
var fs = require('fs');
var E = exports;

E.patches = {
    'tick/lib/profile.js': 'node-tick-processor.patch',
    'request/request.js': 'request.patch',
    'requirejs/bin/r.js': 'requirejs.patch',
    'locker-server/index.js': 'locker-server.patch',
    'locker-server/lib/Lock.js': 'locker-server.patch',
    'locker-server/lib/LockQueueManager.js': 'locker-server.patch',
    'xmlhttprequest/lib/XMLHttpRequest.js': 'xmlhttprequest.patch',
    'express-latency/lib/latency.js': 'express-latency.patch',
    'connect-mongo/lib/connect-mongo.js': 'connect-mongo.patch',
    'native-dns/lib/utils.js': 'native-dns.patch',
    'native-dns/dns.js': 'native-dns.patch',
    'native-dns/node_modules/native-dns-packet/packet.js': 'native-dns.patch',
    'simplecgi/simplecgi.js': 'simplecgi.patch',
    'ecstatic/lib/ecstatic.js': 'ecstatic.patch',
    'ecstatic/lib/ecstatic/showdir.js': 'ecstatic.patch',
    'http-proxy/lib/http-proxy/common.js': 'http_proxy.patch',
    'http-proxy/lib/http-proxy/passes/web-incoming.js': 'http_proxy.patch',
    'csslint/node_modules/parserlib/lib/node-parserlib.js':
        'csslint-parserlib.patch',
    'jshint/src/jshint.js': 'jshint.patch',
    'eslint/lib/rules/no-extra-parens.js': 'no-extra-parens.patch',
    'diskusage/index.js': 'diskusage.patch',
    'mocha/lib/mocha.js': 'mocha.patch',
    'mocha/bin/_mocha': 'mocha.patch',
    'mocha/lib/runner.js': 'mocha.patch',
    'ws/lib/BufferUtil.js': 'ws.patch',
    'ws/lib/Validation.js': 'ws.patch',
    'ws/lib/WebSocket.js': 'ws.patch',
};
var jake_files = ['lib/jake.js', 'lib/loader.js', 'lib/publish_task.js',
    'lib/program.js', 'lib/package_task.js', 'lib/rule.js', 'lib/test_task.js',
    'lib/namespace.js', 'lib/task/task.js', 'lib/utils/index.js',
    'lib/utils/logger.js'];
jake_files.forEach(function(filename){
    E.patches['jake/'+filename] = 'jake.patch'; });

var syspath_re = /^.*?(hola_server|xvfs|lib)\/node_modules\/(.*)$/g;
var npm_re = new RegExp('^(\\/var\\/cache\\/znpm(\\d+)?)\\/'+
    '(.*)-(\\d+\\.\\d+\\.\\d+)([^\\d\/][^\\/]*)?\\/');
if (!global.is_libpatched)
{
    global.is_libpatched = true;
    var original_compile = node_module.prototype._compile;
    node_module.prototype._compile = function(code, filename){
        var index = filename.replace(/\\/g, '/').replace(syspath_re, '$2')
        .replace(npm_re, '$3/');
        if (E.patches[index])
        {
            if (typeof E.patches[index]=='string')
                code = apply_patch(code, index, E.patches[index]);
            else if (typeof E.patches[index]=='function')
                code = E.patches[index](code, index);
            if (!code)
                throw new Error('Failed patching '+filename);
        }
        return original_compile.call(this, code, filename);
    };
}
/* patch helper functions */
var patch_cache = {};
function get_patch(changed_file, patch_filename){
    var patches = patch_cache[patch_filename];
    if (!patches)
    {
        var patch_text = fs.readFileSync(patch_filename, 'utf8');
        if (/^\*{15}/m.test(patch_text))
        {
            process.stderr.write('*** libpatch.js '+patch_filename
                +': incorrect diff format: got `diff -c` context diff, '
                +'expected `diff -u` unified diff.');
            process.exit(1);
        }
        patches = patch_text.split(/(?=^--- .+\n\+\+\+ )/m)
        .filter(function(patch){ return patch.startsWith('---'); })
        .map(function(patch){
            var a = patch.indexOf('\n'), b = patch.indexOf('\n', a + 1);
            return [
                patch.substring(0, a).replace(/^--- /, ''),
                patch.substring(a + 1, b).replace(/^\+\+\+ /, ''),
                patch.substring(b + 1).replace(/diff.*-u.*/, ''),
            ];
        });
        patch_cache[patch_filename] = patches;
    }
    var patch = patches.filter(function(patch){
        return patch[0].startsWith(changed_file)
            || patch[1].startsWith(changed_file);
    });
    if (!(patch = patch[0]))
        throw new Error('No patch for '+changed_file);
    return patch[2];
}

function apply_patch(code, changed_file, patch_filename){
    var patch = get_patch(changed_file, __dirname+'/'+patch_filename);
    return require('diff').applyPatch(code, patch);
}
E.get_applied_patches = function(){ return Object.keys(patch_cache); };
