'use strict';

pandora.UI = (function() {

    var self = {}, that = {};

    self.previousUI = {};

    that.encode = function(val) {
        return val.replace(/\./g, '\\.');
    };

    that.getPrevious = function(key) {
        // fixme: probably unneeded by now
        return !key ? self.previousUI : self.previousUI[key];
    };

    that.reset = function() {
        pandora.api.resetUI({}, function() {
            pandora.user.ui = pandora.site.user.ui;
            pandora.user.ui._list = pandora.getListState(
                pandora.user.ui.find
            );
            pandora.user.ui._filterState = pandora.getFilterState(
                pandora.user.ui.find
            );
            pandora.user.ui._documentFilterState = pandora.getDocumentFilterState(
                pandora.user.ui.findDocuments
            );
            pandora.user.ui._findState = pandora.getFindState(
                pandora.user.ui.find
            );
            pandora.user.ui._collection = pandora.getCollectionState(
                pandora.user.ui.findDocuments
            );
            pandora.user.ui._findDocumentsState = pandora.getFindDocumentsState(
                pandora.user.ui.findDocuments
            );
            Ox.Theme(pandora.user.ui.theme);
            pandora.$ui.appPanel.reload();
        });
    };

    // sets pandora.user.ui.key to val
    // key foo.bar.baz sets pandora.user.ui.foo.bar.baz
    // val null removes a key
    that.set = function(/* {key: val}[, flag] or key, val[, flag] */) {

        var add = {},
            args,
            collection,
            collectionView,
            collectionSettings = pandora.site.collectionSettings,
            editSettings = pandora.site.editSettings,
            item,
            list,
            listSettings = pandora.site.listSettings,
            listView,
            set = {},
            textSettings = pandora.site.textSettings,
            trigger = {},
            triggerEvents;

        if (Ox.isObject(arguments[0])) {
            args = arguments[0];
            triggerEvents = Ox.isUndefined(arguments[1]) ? true : arguments[1];
        } else {
            args = Ox.makeObject([arguments[0], arguments[1]]);
            triggerEvents = Ox.isUndefined(arguments[2]) ? true : arguments[1];
        }

        Ox.Log('UI', 'SET', JSON.stringify(args));

        self.previousUI = Ox.clone(pandora.user.ui, true);
        self.previousUI._list = pandora.getListState(self.previousUI.find);

        if (args.section == 'texts') {
            trigger.section = args.section;
            trigger.text = args.text;
        } else if (args.section == 'edits') {
            trigger.section = args.section;
            trigger.edit = args.edit;
        } else if (pandora.user.ui.section == 'documents' || args.section == 'documents') {
            if ('findDocuments' in args) {
                // the challenge here is that find may change list,
                // and list may then change listSort and listView,
                // which we don't want to trigger, since find triggers
                // (values we put in add will be changed, but won't trigger)
                collection = pandora.getCollectionState(args.findDocuments);
                pandora.user.ui._collection = collection;
                pandora.user.ui._documentFilterState = pandora.getDocumentFilterState(args.findDocuments);
                pandora.user.ui._findDocumentsState = pandora.getFindDocumentsState(args.findDocuments);
                if (pandora.$ui.appPanel && !pandora.stayInItemView) {
                    // if we're not on page load, and if find isn't a context change
                    // caused by an edit, then switch from item view to list view
                    args['document'] = '';
                }
                if (collection != self.previousUI._collection) {
                    Ox.Log('UI', 'FIND HAS CHANGED COLLECTION', self.previousUI._collection, '>', collection);
                    // if find has changed collection
                    Ox.forEach(collectionSettings, function(collectionSetting, setting) {
                        // then for each setting that corresponds to a collection setting
                        if (!Ox.isUndefined(args[setting])) {
                            add[setting] = args[setting];
                        } else if (
                            !pandora.user.ui.collections[collection]
                            || Ox.isUndefined(pandora.user.ui.collections[collection][collectionSetting])
                        ) {
                            // either add the default setting
                            add[setting] = pandora.site.user.ui[setting];
                        } else {
                            // or the existing collection setting
                            add[setting] = pandora.user.ui.collections[collection][collectionSetting];
                        }
                    });
                }
            } else {
                collection = self.previousUI._collection;
            }
            if (!pandora.user.ui.collections[collection]) {
                add['collections.' + that.encode(collection)] = {};
            }
            Ox.forEach(collectionSettings, function(collectionSetting, setting) {
                // for each setting that corresponds to a collection setting
                // set that collection setting to
                var key = 'collections.' + that.encode(collection) + '.' + collectionSetting;
                if (setting in args) {
                    // the setting passed to UI.set
                    add[key] = args[setting];
                } else if (setting in add) {
                    // or the setting changed via find
                    add[key] = add[setting];
                } else if (!pandora.user.ui.collections[collection]) {
                    // or the default setting
                    add[key] = pandora.site.user.ui[setting];
                }
            });
            // set nested lisColumnWidth updates
            Ox.forEach(args, function(value, key) {
                if (Ox.startsWith(key, 'collectionColumnWidth.')) {
                    key = 'collections.' + that.encode(collection) + '.columnWidth.'
                        + key.slice('collectionColumnWidth.'.length);
                    if (!(key in add)) {
                        add[key] = value;
                    }
                }
            });

            if (args.document) {
                // when switching to an item, update list selection
                add['collectionSelection'] = [args.document];
                add['collections.' + that.encode(collection) + '.selection'] = [args.document];
            }
        } else {
            if ('find' in args) {
                // the challenge here is that find may change list,
                // and list may then change listSort and listView,
                // which we don't want to trigger, since find triggers
                // (values we put in add will be changed, but won't trigger)
                list = pandora.getListState(args.find);
                pandora.user.ui._list = list;
                pandora.user.ui._filterState = pandora.getFilterState(args.find);
                pandora.user.ui._findState = pandora.getFindState(args.find);
                if (pandora.$ui.appPanel && !pandora.stayInItemView) {
                    // if we're not on page load, and if find isn't a context change
                    // caused by an edit, then switch from item view to list view
                    args['item'] = '';
                }
                if (list != self.previousUI._list) {
                    Ox.Log('UI', 'FIND HAS CHANGED LIST')
                    // if find has changed list
                    Ox.forEach(listSettings, function(listSetting, setting) {
                        // then for each setting that corresponds to a list setting
                        if (!Ox.isUndefined(args[setting])) {
                            add[setting] = args[setting];
                        } else if (
                            !pandora.user.ui.lists[list]
                            || Ox.isUndefined(pandora.user.ui.lists[list][listSetting])
                        ) {
                            // either add the default setting
                            add[setting] = pandora.site.user.ui[setting];
                        } else {
                            // or the existing list setting
                            add[setting] = pandora.user.ui.lists[list][listSetting];
                        }
                    });
                }
                add.itemFind = pandora.getItemFind(args.find);
            } else {
                list = self.previousUI._list;
            }
            // it is important to check for find first, so that
            // if find changes list, list is correct here
            item = args.item || pandora.user.ui.item;
            listView = add.listView || args.listView;

            if (listView) {
                if (pandora.isClipView(listView)) {
                    // when switching to a clip view, clear list selection
                    // (but don't trigger an additional event)
                    add.listSelection = [];
                } else if (['text', 'position'].indexOf(
                    pandora.user.ui.listSort[0].key
                ) > -1) {
                    // when switching to a non-clip view, with a sort key
                    // that only exists in clip view, reset sort to default
                    args.listSort = pandora.site.user.ui.listSort;
                }
            }

            if (!pandora.user.ui.lists[list]) {
                add['lists.' + that.encode(list)] = {};
            }
            Ox.forEach(listSettings, function(listSetting, setting) {
                // for each setting that corresponds to a list setting
                // set that list setting to
                var key = 'lists.' + that.encode(list) + '.' + listSetting;
                if (setting in args) {
                    // the setting passed to UI.set
                    add[key] = args[setting];
                } else if (setting in add) {
                    // or the setting changed via find
                    add[key] = add[setting];
                } else if (!pandora.user.ui.lists[list]) {
                    // or the default setting
                    add[key] = pandora.site.user.ui[setting];
                }
            });
            // set nested lisColumnWidth updates
            Ox.forEach(args, function(value, key) {
                if (Ox.startsWith(key, 'listColumnWidth.')) {
                    key = 'lists.' + that.encode(list) + '.columnWidth.'
                        + key.slice('listColumnWidth.'.length);
                    if (!(key in add)) {
                        add[key] = value;
                    }
                }
            });

            if (args.item) {
                // when switching to an item, update list selection
                add['listSelection'] = [args.item];
                add['lists.' + that.encode(list) + '.selection'] = [args.item];
                if (
                    !args.itemView
                    && ['timeline', 'player', 'editor'].indexOf(
                        pandora.user.ui.itemView
                    ) > -1
                    && !pandora.user.ui.videoPoints[item]
                    && !args['videoPoints.' + item]
                ) {
                    // if the item view doesn't change, remains a video view,
                    // video points don't exist yet, and won't be set,
                    // add default video points
                    add['videoPoints.' + item] = {
                        annotation: '',
                        'in': 0,
                        out: 0,
                        position: 0
                    };
                }
                if (args['videoPoints.' + item] && (
                    !pandora.user.ui.item
                    || pandora.user.ui.itemView != 'editor'
                )) {
                    pandora._dontSelectResult = true;
                }
            }

            if (['timeline', 'player', 'editor'].indexOf(args.itemView) > -1) {
                // when switching to a video view, add it as default video view
                args.videoView = args.itemView;
                if (
                    !pandora.user.ui.videoPoints[item]
                    && !args['videoPoints.' + item]
                ) {
                    // if video points don't exist yet, and won't be set,
                    // add default video points
                    add['videoPoints.' + item] = {
                        annotation: '',
                        'in': 0,
                        out: 0,
                        position: 0
                    };
                }
            }
        }

        if (args.edit) {
            // add local edit settings
            if (!pandora.user.ui.edits[args.edit]) {
                add['edits.' + that.encode(args.edit)] = {};
            }
            Ox.forEach(editSettings, function(value, key) {
                var editsKey = 'edits.' + that.encode(args.edit) + '.' + key;
                add[editsKey] = editsKey in args ? args[editsKey]
                    : (pandora.user.ui.edits[args.edit] && !Ox.isUndefined(
                        pandora.user.ui.edits[args.edit][key]
                    )) ? pandora.user.ui.edits[args.edit][key]
                    : value;
            });
        }

        Ox.forEach({
            editSelection: 'selection',
            editSort: 'sort',
            editView: 'view'
        }, function(editSetting, setting) {
            var key = 'edits.' + that.encode(
                args.edit || pandora.user.ui.edit
            ) + '.' + editSetting;
            if (setting in args) {
                // add local edit setting
                add[key] = args[setting];
            } else if (setting in add) {
                // add local edit setting
                add[key] = add[setting];
            } else if (key in add) {
                // add global edit setting
                add[setting] = add[key];
            }
        });

        if (args.text) {
            add['texts.' + that.encode(args.text)] = Ox.map(textSettings, function(value, key) {
                var textsKey = 'texts.' + that.encode(args.text),
                    textsSubKey = textsKey + '.' + key;
                return textsKey in args && key in args[textsKey] ? args[textsKey][key]
                    : textsSubKey in args ? args[textSubKey]
                    : (pandora.user.ui.texts[args.text] && !Ox.isUndefined(
                        pandora.user.ui.texts[args.text][key]
                    )) ? pandora.user.ui.texts[args.text][key]
                    : value;
            });
        }

        // items in args trigger events, items in add do not
        [args, add].forEach(function(obj, isAdd) {
            Ox.forEach(obj, function(val, key) {
                // make sure to not split at escaped dots ('\.')
                var keys = key.replace(/\\\./g, '\n').split('.').map(function(key) {
                        return key.replace(/\n/g, '.')
                    }),
                    part,
                    ui = pandora.user.ui;
                while (keys.length > 1) {
                    part = part ? part + '.' + keys[0] : keys[0];
                    if (Ox.isUndefined(ui[keys[0]])) {
                        ui[keys[0]] = {};
                        set[part] = {};
                    }
                    ui = ui[keys.shift()];
                }
                if (!Ox.isEqual(ui[keys[0]], val)) {
                    if (val === null) {
                        delete ui[keys[0]];
                    } else {
                        ui[keys[0]] = val;
                    }
                    set[key] = val;
                    if (!isAdd) {
                        trigger[key] = val;
                    }
                }
            });
        });
        if (Ox.len(set) && !pandora.isEmbedURL() && !pandora.isPrintURL()) {
            pandora.api.setUI(set);
        }
        triggerEvents && Ox.forEach(trigger, function(val, key) {
            Ox.Log('UI', 'TRIGGER ', key, val);
            Ox.forEach(pandora.$ui, function($element) {
                if (Ox.UI.isElement($element)) {
                    $element.triggerEvent('pandora_' + key.toLowerCase(), {
                        value: val,
                        previousValue: self.previousUI[key]
                    });
                }
            });
        });

        pandora.URL.update(Object.keys(
            !pandora.$ui.appPanel ? args : trigger
        ));

    };

    return that;

}());