/*
 * Copyright (C) 2006, hatena ( http://www.hatena.ne.jp/ ).
 *
 * This program is dual-licensed free software
 * you can redistribute it and/or modify it under the terms of the MIT License or the Academic Free License v2.1.
 */

function qw (str) {
	return str.split(/\s+/);
}


try {
    if (typeof(MochiKit.Base) == 'undefined') {
        throw '';
    }
} catch (e) {
    throw 'KeyTypeListener depends on MochiKit.Base!';
}

try {
    if (typeof(MochiKit.Signal) == 'undefined') {
        throw '';
    }
} catch (e) {
    throw 'KeyTypeListener depends on MochiKit.Signal!';
}

if (typeof(KeyTypeListener) == 'undefined') {
    KeyTypeListener = {};
}

update(MochiKit.Signal.Event.prototype, {
    detailedKeyString: function() {
        if (!this.key().string.match(/^KEY_(.*)/))
            return;

        var m = this.modifier();
        var key = RegExp.$1;
        var upcase = m.shift && key.length == 1;
            key = key['to' + (upcase ? 'Upper' : 'Lower') + 'Case']();
        var modifiers = ifilter(partial(operator.ne, key), upcase ? qw('ctrl alt meta') : qw('ctrl alt shift meta'));

       //log(extend(list(ifilter(propertyOf(m), modifiers)), [key]).join('-'));
        return extend(list(ifilter(propertyOf(m), modifiers)), [key]).join('-');
    }
});

KeyTypeListener.VERSION = "1.0";
KeyTypeListener.NAME = "KeyTypeListener";

if (typeof(KeyTypeListener.Util) == 'undefined') {
    KeyTypeListener.Util = {};
}

MochiKit.Base.update(KeyTypeListener.Util, {
    appliedChain: partial(reduce, function(value, func) { return isUndefinedOrNull(value) ? value : func(value) }),

    propertyOf: function(obj) {
        return function(p) { return obj[p] };
    },

    fcall: function(func) {
        return func.call(extend(null, arguments, 1));
    },

    thruWith: function(func) {
        return function(value) { func.apply(this, arguments); return value };
    },

    deleteValue: function(value, arr) {
        var ary = [];
        map(function(a) { if(compare(value, a) != 0) ary.push(a) }, arr);
        return ary;
    },

    lastPartial: function(func) {
        var args = m.extend(null, arguments, 1);
        return function() { 
            func.apply(null, flattenArguments(arguments, args));
        };
    },

    EXPORT_TAGS: {
        ':all': ['appliedChain', 'propertyOf', 'fcall', 'thruWith', 'lastPartial', 'deleteValue']
    }
});

MochiKit.Base._exportSymbols(this, KeyTypeListener.Util);

update(KeyTypeListener, {
    KEYTYPE: window.opera? 'onkeypress' : 'onkeydown',

    keyListener: function(keybinds) {
        return function(e) {
            appliedChain(
              [propertyOf(keybinds), thruWith(method(e, 'stop')), partial(this.keyChain, e), fcall], 
              this.lastkey(e)
            )
        };
    },

    keyChain: function(e, callbacks) {
        return function() {
          for (var i = 0, len = callbacks.length; i < len; i++) {
              if( false === callbacks[i](e))
                  break;
          }
        };
    },
    
    lastkey: function(e) {
        return window.opera? e.key().string : e.detailedKeyString();
    },
    
    keybinds: {
    },

    elementKeybinds: {
    },

    getKeybinds: function(key, func, element) {
        if (arguments.length <= 2)
            element = document;

        if( this.keybinds && this.keybinds[element] && this.keybinds[element][key] )
            return this.keybinds[element][key];
    },

    addKeybind: function(key, func, element/*= document */) {
        if (arguments.length <= 2)
            element = document;

        if (typeof this.keybinds[element] == 'undefined') {
            this.keybinds[element] = {};
            connect(element, this.KEYTYPE, this, this.keyListener(this.keybinds[element]));
        }
        if ( !isArrayLike(this.keybinds[element][key]) ) {
            this.keybinds[element][key] = [];
        }

        this.keybinds[element][key].push(func);

        return func;
    },

    removeKeybind: function(key, func, element/*= document */) {
        if (arguments.length <= 2)
            element = document;
        this.keybinds[element][key] = deleteValue(func, this.keybinds[element][key]);
    },

    removeKeybindAll: function(key, element/*= document */) {
        if (arguments.length <= 1)
            element = document;
        this.keybinds[element][key] = [];
    },

    cancelKeybind: function(element) {
        connect(element, this.KEYTYPE, methodcaller('stopPropagation'));
    },

    EXPORT_TAGS: {
        ':all': ['addKeybind', 'removeKeybind', 'cancelKeybind']
    }
});

bindMethods(KeyTypeListener);
nameFunctions(KeyTypeListener);

MochiKit.Base._exportSymbols(this, KeyTypeListener);

/* exemple

addKeybind('a', function(ev) {
    p(ev);
    return false;
});

addKeybind('a', partial(p,'_a') );
var c = addKeybind('c', partial(p, ('_c')) );
var d = addKeybind('d', partial(p, ('_d')) );
removeKeybind('d', d);
addKeybind('c', partial(p,'_c+text') , $('text'));
removeKeybind('c', c);

*/
