var OU = OU || {};

/**
 * options - just for options
 *
 */

/**
 *
 * @type {{construct}}
 */
OU.cfg = (function() {
    /**
     *
     * @param cfg
     * @param data
     * @param parent
     * @constructor
     */
    var Cfg = function(cfg, data, parent) {
        this.id = '';
        this.label = '';
        this.data = data;
        this.parent = parent || null;
        this.root = this;
        this.options = [];
        this.children = []; //holds children Cfg

        OU.utils.extendDeep(this, cfg);
        //console.log(this);

        if (this.parent) {
            this.root = this.parent.root || this.parent;
        }

        this.options.splice(0).forEach(function(opt) {
            this.add(opt);
        }.bind(this));
    };

    Cfg.prototype = {
        /**
         * add options to Cfg
         * @param {object} obj
         * @returns {Cfg|Option}
         */
        add: function(obj) {
            var child;

            if (obj.options) {
                child = new Cfg(obj, this.data[obj.id], this);
            }
            else {
                child = new Option(obj, this.data[obj.id], this);
            }

            this.options.push(child);
            this.options[child.id] = child;

            return child;
        },

        /**
         * set descendant options inputs to associated data if present
         */
        set: function() {
            this.options.forEach(function(obj) {
                if (obj instanceof Cfg) {
                    obj.set();
                }
                else {
                    obj.setInput(this.data[obj.id]);
                }
            }.bind(this));
        },

        //TODO look at optimising...
        get: function(path) {
            var result = null;

            path = Array.isArray(path) ? path :  path.split(':');

            //console.log(path[0], this);

            this.options.some(function(obj) {
                if (path[0] == obj.id) {
                    path.shift();
                    result = (path.length === 0) ? obj : obj.get(path);
                    return true;
                }
                else {
                    if (obj instanceof Cfg) {
                        result = obj.get(path);
                    }
                }
            });

            return result;
        },

        setConditions: function() {
            this.options.forEach(function(obj) {
                if (obj instanceof Cfg) {
                    obj.setConditions();
                }
                else {
                    obj.handleConditions();
                }
            });
        },

        getRoot: function() {
            var root = this;

            while (root) {
                root = root.parent;
            }

            return root;
        },

        /**
         * reset to default values
         */
        reset: function() {
            this.options.forEach(function(opt) {
                opt.set(opt.default);
                opt.setData(opt.default);
            });

            this.children.forEach(function(cfg) {
                cfg.reset();
            });
        }
    };

    var Option = function(opt, data, parent) {
        this.id = '';
        this.type = ''; //string, number etc.
        this.default = data; //initial value
        this.value = data;
        this.label = '';
        this.el = null;
        this.parent = parent; //parent cfg object
        this.input = null;
        this.conditional = null;

        OU.utils.extendDeep(this, opt);

        if (!this.type) {
            this.type = (typeof this.default);
        }

        //normalise input data so we're always dealing with an array
        this.input = Array.isArray(this.input) ? this.input : [this.input];

        //set this as handler for change event
        this.input.forEach(function(data) {
            data.change = this;
        }.bind(this));

        OU.input.create(this.input);
    };

    Option.prototype = {
        init: function() {

        },

        handleConditions: function() {
            if (!Array.isArray(this.conditional)) {
                return;
            }

            var opt;

            this.conditional.forEach(function(condition) {
                //opt = this.getOption(condition[0]);
                opt = this.parent.root.get(condition[0]);

                if (!opt) {
                    return;
                }

                if (this.value == condition[1]) {
                    opt.show();
                }
                else {
                    opt.hide();
                }
            }.bind(this));
        },

        getOption: function(name) {
            var obj = this.parent,
                opt;

            //TODO colon delimited name for nested/namespace search (pens:yellow)

            while (obj) {
                opt = obj.get(name);

                if (opt) {
                    return opt;
                }

                obj = obj.parent;
            }

            return null;
        },

        /**
         * sets the input element
         * @param {string|number|boolean} value
         * @returns {*}
         */
        setInput: function(value) {
            var el = this.input[0].el;

            if (typeof value == 'undefined') {
                return;
            }

            switch (this.input.type) {
                case 'radio':
                    this.input.group.querySelector('input[value="' + value + '"]').checked = true;
                    break;
                case 'select':
                    el.querySelector('option[value="' + value + '"]').selected = true;
                    break;
                case 'text':
                case 'textarea':
                    el.value = value;
                    break;
                case 'checkbox':
                    el.checked = value;
            }

            this.value = value;
            this.handleConditions();
        },

        getInput: function(index) {
            return this.input[index || 0].el;
        },

        getInputValue: function(el) {
            switch (this.input.type) {
                case 'select':
                case 'radio':
                case 'text':
                case 'textarea':
                    return el.value;
                case 'checkbox':
                    return !!el.checked;
            }
        },

        getInputData: function(el) {
            return this.input[el.getAttribute('data-input-group-index') || 0];
        },

        /**
         * updates the object property that this represents
         * @param {Element} target
         */
        setData: function(value, target) {
            var prev = this.parent.data[this.id];

            value = this.convertType(this.parent.data[this.id], value);

            if (this.parent.data) {
                this.parent.data[this.id] = value;
            }

            if (typeof this.update == 'function') {
                if (this.update(value, prev, target) === false) {
                    return;
                }
            }

            if (typeof this.parent.update == 'function') {
                this.parent.update.call(this, value, prev, target);
            }

            console.log(value, prev, this.parent.data);

            this.value = value;
            this.handleConditions();
        },

        convertType: function(prev, curr) {
            switch (typeof prev) {
                case 'string':
                    return curr.toString();
                case 'number':
                    return parseInt(curr, 10);
                case 'boolean':
                    return !!curr;
                default:
                    return curr;
            }
        },

        enable: function() {
            this.input.forEach(function(input) {
                input.el.removeAttribute('disabled');
            });
        },

        disable: function() {
            this.input.forEach(function(input) {
                input.el.disabled = true;
            });
        },

        //TODO hide/show need work - what about labels??
        show: function() {
            this.input.group.style.display = 'block';
        },

        hide: function() {
            this.input.group.style.display = 'none';
        },

        handleEvent: function(e) {
            switch (e.type) {
                case 'change':
                    e.stopPropagation();
                    this.setData(this.getInputValue(e.target), e.target);
            }
        }
    };

    return {
        construct: function(cfg, data) {
            return new Cfg(cfg, data);
        }
    };
})();