/**
 *$Id: hash.js 70 2008-09-25 15:01:48Z vkarpov $
 *
 * Функции для работы с объектами
 */
PuskFramework.hash = new function()
{
    var pf = PuskFramework;
     /**
     * Функция возвращает индекс элемента в хэше
     *
     * @param {Object} hash массив, в котором будет произведен поиск
     * @param {Object} value элемент для поиска
     * @return {Integer} индекс элемента, соответствующего аргументу
     */
    this.indexOf = function(hash, value)
    {
        if (typeof(hash) != 'object') return -1;
        for(var i in hash)
        {
            if (hash[i] == value) { return i; }
        }
        return -1;
    };

    /**
     * Реализация цикла foreach для объектов
     *
     * @param {Object} hash хеш для прохода
     * @param {Object} iterator функция, выполняющаяся над каждым элементом
     * @param {Object} context контекст вызова функции
     */
    this.forEach = function(hash, iterator, context)
    {
        if (typeof(hash) == 'object' && pf.$type(iterator) == "function")
        {
            for(var i in hash)
            {
                iterator.call(context, hash[i], i, hash);
            }
        }
    };

    /**
     * Создает несвязанную копию объекта
     *
     * @param {Object} hash
     * @return {Object}
     */
    this.clone = function(hash)
    {
        if (typeof(hash) != 'object') { return hash; }
        var newHash = hash.constructor();
        for (var key in hash) {
            newHash[key] = pf.hash.clone(hash[key]);
        }
        return newHash;
    };

    /**
     * Возвращает хеш, отфильтрованный по условию (функции)
     *
     * @param {Object} hash исходный массив
     * @param {Object} callback функция фильтрации
     * @param {Object} context контекст вызова функции
     * @return {Object}
     */
    this.filter = function(hash, callback, context)
    {
        if (typeof(hash) != 'object' || typeof(callback) != "function") return hash;
        context = context || null;
        var val, result = {};
        for (var i in hash)
        {
            val = hash[i];
            if (callback.call(context, val, i, this)) { result[i] = val; }
        }
        return result;
    };

    /**
     * Осуществляет слияние двух и боле объектов в один
     *
     * @param {Object} hash1
     * @param {Object} hash2
     * @return {Object}
     */
    this.merge = function(hash1, hash2 /* [, hash3, ...] */)
    {
        var mix = {};
        for (var i = 0, len=arguments.length; i < len; i++)
        {
            for (var property in arguments[i])
            {
                var ap = arguments[i][property];
                var mp = mix[property];
                if (mp && pf.$type(ap) == 'object' && pf.$type(mp) == 'object')
                {
                    mix[property] = this.merge(mp, ap);
                }
                else
                {
                    mix[property] = ap;
                }
            }
        }
        return mix;
    };

    /**
     * Возвращает длину хеша, можно исключить из списка свойств функции
     *
     * @param {Object} hash
     * @param {Bool} skipFunctions исключить функции
     * @return {Integer}
     */
    this.getLength = function(hash, skipFunctions)
    {
        if (typeof(hash) != 'object') { return 0; }
        var size = 0;
        for (var key in hash) if (!skipFunctions || typeof hash[key] != 'function') { size++; }
        return size;
    };

    /**
     * Возвращает массив с ключами хеша
     *
     * @param {Object} hash объект
     * @param {Object} skipFunctions пропустить функции
     * @return {Array}
     */
    this.getKeys = function(hash, skipFunctions)
    {
        if (typeof(hash) != 'object') { return [] }
        var keys = [];
        for (var key in hash) if (!skipFunctions || typeof hash[key] != 'function') { keys.push(key); }
        return keys;
    };

    /**
     * Возвращает массив с значениями хеша
     *
     * @param {Object} hash объект
     * @param {Object} skipFunctions пропустить функции
     * @return {Array}
     */
    this.getValues = function(hash, skipFunctions)
    {
        var values = [];
        for (var key in hash) if (!skipFunctions || typeof hash[key] != 'function') { values.push(hash[key]); }
        return values;
    };

    /**
     * Сериализует хеш в GET строку. Есть возможность задать префикс для имен.
     *
     * @param {Object} hash объект
     * @param {Object} prefix префикс, например data
     */
    this.serialize = function(hash, prefix)
    {
        if (prefix == null) { prefix = ''; }

        if (hash instanceof Object)
        {
            var qs = [];
            for(var property in hash)
            {
                var v = hash[property];
                if (v === null) v = '';
                if ( ((v.constructor||{}).prototype||{})[property]) { continue }
                var curPrefix = prefix ? prefix+'['+encodeURIComponent(property)+']' : encodeURIComponent(property);
                if (v instanceof Object)
                {
                    qs.push( pf.hash.serialize(v, curPrefix) );
                } else {
                    qs.push( curPrefix + "=" + encodeURIComponent(v) );
                }
            }
        } else {
            var qs = [hash];
        }
        return qs.join('&');
    };

    /**
    * Сравнить два объекта по содержимому
    */
    this.compare = function(first, second)
    {
        if (!first || !second) return false;
        if (!pf.arr.compare(pf.hash.getKeys(first), pf.hash.getKeys(second))) return false;
        for(var p in first)
        {
            if (typeof second[p] !== typeof first[p]) return false;

            if(first[p] instanceof Object)
            {
                switch (pf.$type(first[p]))
                {
                    case 'function':
                        if (first[p] != second[p]) return false;
                        break;
                    case 'array':
                        if (!pf.arr.compare(first[p], second[p])) return false;
                        break;
                    default:
                        if (!pf.hash.compare(first[p], second[p])) return false;
                        break;
                };
                continue;
            }

            if (first[p] !== second[p]) return false;
        }
        return true;
    };

    this.flatten = function(hash, prefix)
    {
        if (prefix == null) { prefix = ''; }

        if (hash instanceof Object)
        {
            var qs = {};
            for(var property in hash)
            {
                var v = hash[property];
                if ( ((v.constructor||{}).prototype||{})[property]) { continue }
                var curPrefix = prefix ? prefix+'['+property+']' : property;
                if (v instanceof Object)
                {
                    var tmp = (pf.hash.flatten(v, curPrefix));
                    for (var n in tmp)
                    {
                        qs[n] = tmp[n];
                    }
                } else {
                    qs[curPrefix] = v;
                }
            }
        } else {
            var qs = hash ? [hash] : [];
        }
        return qs;
    };
};
