Components.utils.import("resource://hatenabookmark/modules/00-utils.jsm");
loadPrecedingModules.call(this);

/*
 * utils 内部では、
 * 頭に _ のついてないローカル変数はすべて EXPORT の対象となる
 */

function newURI(uriSpec, originCharset, baseURI) {
    if (typeof baseURI === "string")
        baseURI = IOService.newURI(baseURI, null, null);
    return IOService.newURI(uriSpec, originCharset, baseURI);
}

/*
 * %s, %d, %f のみサポート
 */
var _SPRINTF_HASH = {
    '%s': String,
    '%d': parseInt,
    '%f': parseFloat,
};

var sprintf = function (str) {
    let args = Array.slice(arguments, 1);
    return str.replace(/%[sdf]/g, function(m) _SPRINTF_HASH[m](args.shift()));
};

/*
 * グローバル関数としてエクスポートはしないけど、あったら便利な関数など
 */
var keys = (obj => Object.keys(obj));
var values = (obj => Object.keys(obj).map(key => obj[key]));

var getHistoryNodeByURL = function getHistoryNodeByURL(url) {
    let query = HistoryService.getNewQuery();
    query.uri = IOService.newURI(url, null, null);
    let options = HistoryService.getNewQueryOptions();
    options.queryType = options.QUERY_TYPE_HISTORY;
    // options.resultType = options.RESULTS_AS_VISIT;
    options.resultType = options.RESULTS_AS_URI;
    let res = HistoryService.executeQuery(query, options);
    let root = res.root;
    try {
        root.containerOpen = true;
        for (let i = 0; i < root.childCount; i++) {
            let node = root.getChild(i);
            return node;
        }
    } finally {
        root.containerOpen = false;
    }
    return;
}


var async = {};

/*
 * sleep 的な処理
 */
async.wait = function(wait, flush) {
    if (typeof flush == 'undefined') flush = true;

    let endTime = Date.now() + wait;
    let mainThread = ThreadManager.mainThread;
    let c = 0;
    do {
        c++;
        mainThread.processNextEvent(flush);
    } while ( (flush && mainThread.hasPendingEvents()) || Date.now() < endTime );
    return c;
};


/*
 * net
 */
var net = {};

net.makeQuery =  function net_makeQuery (data) {
    let pairs = [];
    let regexp = /%20/g;
    let toString = Object.prototype.toString;
    for (let k in data) {
        if (typeof data[k] == 'undefined') continue;
        let n = encodeURIComponent(k);
        let v = data[k];
        if (toString.call(v) === '[object Array]') {
            pairs.push(v.map(function (c) {
                return n + '=' + encodeURIComponent(c).replace(regexp, '+');
            }).join('&'));
        } else {
            pairs.push(n + '=' + encodeURIComponent(v).replace(regexp, '+'));
        }
    }
    return pairs.join('&');
}

net.sync_get = function net__sync_get(url, query, method) {
    if (!method) method == 'GET';
    if (method == 'GET' && query) {
        let q = this.makeQuery(query);
        if (q) {
            url += '?' + q;
        }
    }
    let Y = function(func) {
        let g = func(function(t) {
            try { g.send(t) } catch (e) {};
        });
        return g;
    };
    let xhr;
    let gen = Y(function(next) {
        xhr = new XMLHttpRequest();
        xhr.mozBackgroundRequest = true;
        xhr.open('GET', url, false);
        if (method == 'POST') {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send(this.makeQuery(query));
        } else {
            xhr.send(null);
        }
        yield xhr;
    });

    return gen.next();
}

net._http = function net__http (url, callback, errorback, async, query, headers, method) {
    var user = shared.get('User').user;
    if (/^https?:\/\/(?:[\w-]+\.)+hatena.ne.jp(?=[:\/]|$)/.test(url) &&
        user && !headers)
        headers = { Cookie: 'rk=' + user.rk };

    let xhr = new XMLHttpRequest();
    xhr.mozBackgroundRequest = true;
    if (async) {
       xhr.onreadystatechange = function() {
           if (xhr.readyState == 4) {
               if (xhr.status == 200) {
                   if (typeof callback == 'function')
                       callback(xhr);
               } else {
                   if (typeof errorback == 'function')
                       errorback(xhr);
               }
           }
       }
    }
    if (method == 'GET') {
        let q = this.makeQuery(query);
        if (q) {
            url += '?' + q;
        }
    }
    xhr.open(method, url, async);

    for (let [field, value] in Iterator(headers || {}))
        xhr.setRequestHeader(field, value);

    if (method == 'POST') {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send(this.makeQuery(query));
    } else {
        xhr.send(null);
        if (!async) {
            if (typeof callback == 'function') {
                callback(xhr);
            }
        }
    }
    return xhr;
}

net.get = function net_get (url, callback, errorback, async, query, headers)
    this._http(url, callback, errorback, async, query, headers, 'GET');

net.post = function net_post (url, callback, errorback, async, query, headers)
    this._http(url, callback, errorback, async, query, headers, 'POST');


/*
 * parseShortcut function copy from XUL/Migemo
 */
var parseShortcut = function parseShortcut(aShortcut) {
    var accelKey = IS_MAC ? 'meta' : 'ctrl' ;
    aShortcut = aShortcut.replace(/accel/gi, accelKey);

    var keys = aShortcut.split('+');

    var keyCode = keys[keys.length-1].replace(/ /g, '_').toUpperCase();
    var key     = keyCode;

    var sotredKeyCode = (keyCode.length == 1 || keyCode == 'SPACE' || !keyCode) ? '' : 'VK_'+keyCode ;
    key = sotredKeyCode ? '' : keyCode ;

    return {
        key      : key,
        charCode : (key ? key.charCodeAt(0) : '' ),
        keyCode  : sotredKeyCode,
        altKey   : /alt/i.test(aShortcut),
        ctrlKey  : /ctrl|control/i.test(aShortcut),
        metaKey  : /meta/i.test(aShortcut),
        shiftKey : /shift/i.test(aShortcut),
        string   : aShortcut,
        modified : false
    };
}


var EXPORTED_SYMBOLS = Object.keys(this).filter(name => name[0] !== "_" && name !== "EXPORTED_SYMBOLS");
