(function () {
    'use strict';
    elo.namespace('api.ix');
    /**
     * @class api.ix.SordUtils
     * 
     * This class provides indexserver helper functions regarding Sord objects.
     * 
     * @singleton    
     */
    api.ix.SordUtils = {
        /**
         * @property {Object} SELECTOR_MINIMAL
         * Constant de.elo.ix.client.SordZ selector to return only id and short description of a sord object.
         */
        //SELECTOR_MINIMAL: elo.CONST.SORD.mbMin,

        /**
         * @property {Object} SELECTOR_INDEX
         * Constant de.elo.ix.client.SordZ selector to return the full index information of a sord object.
         */
        //SELECTOR_INDEX: elo.CONST.SORD.mbAllIndex,
        /**
         * @property {Object} SELECTOR_COMPLETE
         * Constant de.elo.ix.client.SordZ selector to return the sord object including document information and urls.
         */
        //SELECTOR_COMPLETE: elo.CONST.SORD.mbAll,
        /**
         * Returns the value of the sord given by the key.
         * 
         * @param {Object} sord de.elo.ix.client.Sord object
         * @param {String} key Value name to return, this can be a standard sord member or a group name for a index line.
         * @param {Number|String|Object} defValue The default value that should returned if there is no value for the given key in the sord.
         * @return {Number|String|Object} The value.
         */
        get: function (sord, key, defValue) {
            elo.helpers.ScriptUtils.checkTypes([[Object, de.elo.ix.client.Sord], String], 'api.ix.SordUtils.get')(arguments);
            var i, upperKey;
            if (sord[key] !== undefined) {
                if (sord[key] !== null) {
                    return sord[key];
                } else {
                    return defValue;
                }
            }

            if (sord.objKeys) {
                upperKey = key.toUpperCase();
                for (i = 0; i < sord.objKeys.length; i += 1) {
                    if (sord.objKeys[i].name === upperKey) {
                        if (sord.objKeys[i].data && sord.objKeys[i].data.length === 0) {
                            return defValue;
                        } else {
                            return sord.objKeys[i].data.join('¶');
                        }
                    }
                }
            }
            return defValue;
        },
        /**
         * Sets the given value as parameter given by the key into the given sord object.
         * @param {Object} sord de.elo.ix.client.Sord object
         * @param {String} key The member or group name the value should be written to.
         * @param {Number|String|Object} value The value
         * @param {Boolean} force True to create the ObjKey if it does not exist 
         * in the sord object (defaults to false). Only use this if the sord 
         * object was created by new de.elo.ix.client.Sord() and not loaded from ix.
         */
        set: function (sord, key, value, force) {
            elo.helpers.ScriptUtils.checkTypes([[Object, de.elo.ix.client.Sord], String], 'api.ix.SordUtils.set')(arguments);
            var upperKey, i, objKey;
            if (value !== null && value !== undefined && value.constructor === Date) {
                value = api.ix.DateUtils.dateToIso(value);
            }
            if (sord[key] !== undefined) {
                sord[key] = value;
            } else {
                if (!sord.objKeys) {
                    sord.objKeys = [];
                }
                upperKey = key.toUpperCase();

                for (i = 0; i < sord.objKeys.length; i += 1) {
                    if (sord.objKeys[i].name === upperKey) {
                        objKey = sord.objKeys[i];
                        break;
                    }
                }
                if (!objKey && force) {
                    objKey = new de.elo.ix.client.ObjKey();
                    objKey.name = upperKey;
                    sord.objKeys.push(objKey);
                }
                if (objKey) {
                    if (value === null || value === undefined) {
                        objKey.data = [];
                    } else if (value.constructor === Array) {
                        objKey.data = value;
                    } else if (value.constructor !== String) {
                        objKey.data = [];
                    } else {
                        objKey.data = value.split('¶');
                    }
                } else {
                    elo.helpers.Console.throwError("api.ix.SordUtils.set: Could not find key " + key + " in the given sord object.");
                }
            }
        },
        /**
         * Checks if the sord is a folder.
         * @param {Object} sord de.elo.ix.client.Sord object
         * @return {Boolean} true iff the sord is a folder or the archive root node.
         */
        isFolder: function (sord) {
            elo.helpers.ScriptUtils.checkTypes([[Object, de.elo.ix.client.Sord]], 'api.ix.SordUtils.isFolder')(arguments);

            return sord && (sord.type < elo.CONST.SORD.LBT_DOCUMENT || sord.type === elo.CONST.SORD.LBT_ARCHIVE);
        },
        /**
         * Async method to retrieve an existing sord object from the archive by its id.
         * @param {String} id Guid or object id of the sord.
         * @param {Object} opts Configuration object. Possible values are:
         * <ul>
         * <li>lock: (Boolean) true to lock the sord in the archive (Default is false).</li>
         * <li>selector: (Object) The de.elo.ix.client.SordZ selector what values should be returned (Default is api.ix.SordUtils.SELECTOR_MINIMAL).</li>
         * </ul>
         * @param {Function} success Success callback function the sord will be given as a parameter.
         * @param {Function} failure Failure callback function the error object will be given as parameter.
         * @return {Object} The requested de.elo.ix.client.Sord as parameter of the success function..
         */
        checkout: function (id, opts, success, failure) {
            elo.helpers.ScriptUtils.checkTypes([null, null, Function, Function], 'api.ix.SordUtils.checkout')(arguments);

            var me = this, cb = new de.elo.ix.client.AsyncCallback(function (ei) {
                if (success) {
                    success(ei.sord);
                }
            }, failure), lock, selector;

            opts = opts || {};
            if (!opts.selector || opts.selector.constructor !== Object) {
                opts.selector = me.SELECTOR_MINIMAL;
            }

            if (opts.lock === undefined) {
                lock = false;
            } else {
                lock = !!opts.lock;
            }

            selector = new de.elo.ix.client.EditInfoZ(0, opts.selector);
            elo.IX.ix().checkoutSord(id, selector, lock ? elo.CONST.LOCK.YES : elo.CONST.LOCK.NO, cb);
        },
        /**
         * Async method to write a sord back into the archive.
         * The method will return an error if the sord does not exist.
         * 
         * @param {Object} sord The de.elo.ix.client.Sord to write.
         * @param {Object} opts Configuration object. Possible values are:
         * <ul>
         * <li>unlock: (Boolean) true to unlock the sord in the archive (Default is true).</li>
         * <li>selector: (Object) The de.elo.ix.client.SordZ selector what values should be written (Default is api.ix.SordUtils.SELECTOR_COMPLETE).</li>
         * </ul>
         * @param {Function} success Success callback function.
         * @param {Function} failure Failure callback function the error object will be given as parameter.
         * @return {Number} The id of the written sord object.
         */
        checkin: function (sord, opts, success, failure) {
            elo.helpers.ScriptUtils.checkTypes([[Object, de.elo.ix.client.Sord], null, Function, Function], 'api.ix.SordUtils.checkin')(arguments);

            var me = this, cb = new de.elo.ix.client.AsyncCallback(function (id) {
                if (success) {
                    success(id);
                }
            }, failure), unlock;
            opts = opts || {};
            if (!opts.selector || opts.selector.constructor !== Object) {
                opts.selector = me.SELECTOR_COMPLETE;
            }

            if (opts.unlock === undefined) {
                unlock = true;
            } else {
                unlock = !!opts.unlock;
            }

            elo.IX.ix().checkinSord(sord, opts.selector, unlock ? elo.CONST.LOCK.YES : elo.CONST.LOCK.NO, cb);
        },
        /**
         * Async method to create an archive path.
         * 
         * @param {Object} obj The object containing the path information:
         * <ul>
         * <li>parent: (String) The parent where the path starts (path is needed additional).</li>
         * <li>path: (String) Pilcrow separated path of names starting from parent or archive root.</li>
         * </ul>
         * @param {Object} opts The options of how to create missing folders of the path:
         * <ul>
         * <li>mask: (String) The id or name of the mask to create the folders with.</li>
         * </ul>
         * @param {Function} success
         * @param {Function} failure
         * @return {Number} Objid of the last entry in the path.
         */
        createPath: function (obj, opts, success, failure) {
            elo.helpers.ScriptUtils.checkTypes([Object, Object, Function, Function], 'api.ix.SordUtils.createPath')(arguments);

            if (!obj.path) {
                elo.helpers.Console.throwError("api.ix.SordUtils.createPath: No path given in the first object.split");
            }

            var generatePath = function (parent) {
                var items = obj.path.split("¶"),
                        sords = [], i, sord, maskId, maskName, cb2;

                if (opts.mask === undefined) {
                    maskId = 0;
                    maskName = null;
                } else {
                    maskId = parseInt(opts.mask, 10);
                    if (isNaN(maskId)) {
                        maskId = null;
                        maskName = opts.mask;
                    } else {
                        maskName = null;
                    }
                }

                for (i = 0; i < items.length; i += 1) {
                    sord = new de.elo.ix.client.Sord();
                    sord.name = items[i];
                    if (maskId) {
                        sord.mask = maskId;
                    }
                    if (maskName) {
                        sord.maskName = maskName;
                    }
                    sords[i] = sord;
                }

                cb2 = new de.elo.ix.client.AsyncCallback(function (ids) {
                    if (success) {
                        success(ids[ids.length - 1]);
                    }
                }, failure);
                elo.IX.ix().checkinSordPath(parent, sords, elo.CONST.SORD.mbAll, cb2);
            }, split = obj.path.split("¶");

            if (!split[0] || !split[split.length - 1]) {
                elo.helpers.Console.throwError("api.ix.SordUtils.createPath: Path is incorrect. The path must be separated by ¶, start and end with an name (e.g. test¶path¶end).");
            }

            if (obj.parent) {
                generatePath(obj.parent);
            } else {
                generatePath("1");
            }
        },
        /**
         * Given an actual version string, this helper method will compute the
         * next version by applying the ELO client standard functionality.
         * 
         * @param {String} lastVersion
         * @return {String}
         */
        computeNextVersion: function (lastVersion) {
            // see programming guid version number in dev wiki
            // http://srvpxwiki01vm/xwiki/bin/view/Quellcode/Versionsnummern+von+Dokumenten
            var nextVersion = NaN, lastIdx = 0, newIdx, subVersion, supVersion;

            if (!lastVersion) {
                return '1';
            }
            if (lastVersion.constructor === Number) {
                lastVersion = lastVersion.toString();
            }


            newIdx = lastVersion.lastIndexOf(',');
            if (newIdx > lastIdx) {
                lastIdx = newIdx + 1;
            }
            newIdx = lastVersion.lastIndexOf('.');
            if (newIdx > lastIdx) {
                lastIdx = newIdx + 1;
            }
            newIdx = lastVersion.lastIndexOf(' ');
            if (newIdx > lastIdx) {
                lastIdx = newIdx + 1;
            }

            if (lastIdx > 0) {
                subVersion = lastVersion.substring(lastIdx, lastVersion.length);
                supVersion = lastVersion.substring(0, lastIdx);
            } else {
                supVersion = lastVersion;
            }
            if (subVersion) {
                nextVersion = parseInt(subVersion, 10);
            } else {
                nextVersion = parseInt(supVersion, 10);
            }
            if (!isNaN(nextVersion)) {
                nextVersion = nextVersion + 1;
            }
            if (isNaN(nextVersion)) {
                return lastVersion;
            } else if (subVersion) {
                return supVersion + nextVersion;
            } else {
                return nextVersion.toString();
            }
        },
        /**
         * 
         * @param {Array} keywords An array of Indexserver Keyword objects (retrieved by checkoutKeywords).
         * @return {Array} A flattened array of usable keyword objects.
         */
        flattenKeywordList: function (keywords) {
            var list = [],
                    fillList = function (list, children) {
                        var i;
                        for (i = 0; i < children.length; i += 1) {
                            if (children[i].enabled) {
                                list.push(children[i]);
                            }
                            if (children[i].children && children[i].children.length > 0) {
                                fillList(list, children[i].children);
                            }
                        }
                    };
            fillList(list, keywords);
            return list;
        },
        /**
         * 
         * @param {Object} opts An config object containing the following information:
         * <ul>
         * <li>parentId: (String) The id of the parent folder.</li>
         * <li>maskId: (String) The id of mask.</li>
         * <li>prepareSord: (Function) A function which gets the sord before it will be checked in.</li>
         * <li>returnSord: (Boolean) True to get the whole sord as parameter of the success function instead of the id.</li>
         * </ul>
         * @param {Function} success
         * @param {Function} failure
         */
        create: function (opts, success, failure) {
            elo.helpers.ScriptUtils.checkTypes([Object], 'api.ix.SordUtils.create')(arguments);

            api.IX.ix().createSord(opts.parentId, opts.maskId, api.CONST.EDIT_INFO.mbSord, new de.elo.ix.client.AsyncCallback(function (ei) {
                if (opts.prepareSord) {
                    opts.prepareSord(ei.sord);
                }
                api.ix.SordUtils.checkin(ei.sord, null, function (id) {
                    if (opts.returnSord) {
                        ei.sord.id = id;
                    }
                    success(opts.returnSord ? ei.sord : id);
                }, failure);
            }, failure));
        }
    };
    if (elo.CONST_REGISTRY && elo.CONST_REGISTRY.register) {
        elo.CONST_REGISTRY.register(api.ix.SordUtils, 'SELECTOR_MINIMAL', ['SORD', 'mbMin']);
        elo.CONST_REGISTRY.register(api.ix.SordUtils, 'SELECTOR_INDEX', ['SORD', 'mbAllIndex']);
        elo.CONST_REGISTRY.register(api.ix.SordUtils, 'SELECTOR_COMPLETE', ['SORD', 'mbAll']);
    } else {
        elo.helpers.Console.error("elo.CONST_REGISTRY not available when register constants (SordUtils).");
    }
}());