// vim: et:ts=4:sw=4:sts=4:ft=js

/*@
Ox.Filter <f:Ox.Element> Filter Element
    () ->              <f> Filter Element
    (options) ->       <f> Filter Element
    (options, self) -> <f> Filter Element
    options <o> Options object
        findKeys <a|[]> keys
        query <o> query object with conditions, operator
        sortKeys <a|[]> keys to sort by
        viewKeys <a|[]> visible keys
    self <o>    Shared private variable
@*/

Ox.Filter = function(options, self) {

    var self = self || {},
        that = new Ox.Element({}, self)
            .defaults({
                findKeys: [],
                query: {
                    conditions: [],
                    operator: '&'
                },
                sortKeys: [],
                viewKeys: []
            })
            .options(options || {});

    Ox.print('Ox.Filter self.options', self.options)

    $.extend(self, {
        conditionOperators: {
            date: [
                {id: '', title: 'is'},
                {id: '!', title: 'is not'},
                {id: '<', title: 'is before'},
                {id: '>', title: 'is after'},
                {id: '>&<', title: 'is between'},
                {id: '<|>', title: 'is not between'}
            ],
            list: [
                {id: '', title: 'is'},
                {id: '!', title: 'is not'}
            ],
            number: [
                {id: '', title: 'is'},
                {id: '!', title: 'is not'},
                {id: '<', title: 'is less than'},
                {id: '>', title: 'is greater than'},
                {id: '>&<', title: 'is between'},
                {id: '<|>', title: 'is not between'}
            ],
            string: [
                {id: '=', title: 'is'},
                {id: '!=', title: 'is not'},
                {id: '^', title: 'begins with'},
                {id: '$', title: 'ends with'},
                {id: '', title: 'contains'},
                {id: '!', title: 'does not contain'}
            ],
            text: [
                {id: '', title: 'contains'},
                {id: '!', title: 'does not contain'}
            ]
        },
        operators: [
            {id: '&', title: 'all'},
            {id: '|', title: 'any'}
        ]
    });

    if (!self.options.query.conditions.length) {
        self.options.query.conditions = [{
            key: self.options.findKeys[0].id,
            value: '',
            operator: self.conditionOperators[
                getConditionType(self.options.findKeys[0].type)
            ][0].id
        }];
    }

    self.$operator = new Ox.FormElementGroup({
        elements: [
            new Ox.Label({
                title: 'Match',
                overlap: 'right',
                width: 48
            }),
            new Ox.FormElementGroup({
                elements: [
                    new Ox.Select({
                            items: self.operators,
                            width: 48
                        })
                        .bindEvent({
                            change: changeOperator
                        }),
                    new Ox.Label({
                        overlap: 'left',
                        title: 'of the following conditions',
                        width: 160
                    })
                ],
                float: 'right',
                width: 208
            })
        ],
        float: 'left',
    });

    self.$buttons = [];
    self.$conditions = $.map(self.options.query.conditions, function(condition, i) {
        return constructCondition(condition, i);
    });

    self.$limit = new Ox.InputGroup({
        inputs: [
            new Ox.Checkbox({
                width: 16
            }),
            new Ox.FormElementGroup({
                elements: [
                    new Ox.Input({
                        width: 56
                    }),
                    new Ox.Select({
                        items: [
                            {id: 'items', title: 'items'},
                            {},
                            {id: 'hours', title: 'hours'},
                            {id: 'days', title: 'days'},
                            {},
                            {id: 'GB', title: 'GB'}
                        ],
                        overlap: 'left',
                        width: 64
                    })
                ],
                float: 'right',
                width: 120
            }),
            new Ox.Select({
                items: self.options.sortKeys,
                width: 128
            }),
            new Ox.FormElementGroup({
                elements: [
                    new Ox.Select({
                        items: [
                            {id: 'ascending', title: 'ascending'},
                            {id: 'descending', title: 'descending'}
                        ],
                        width: 96
                    }),
                    new Ox.Label({
                        overlap: 'left',
                        title: 'order',
                        width: 72
                    })
                ],
                float: 'right',
                width: 168
            })
        ],
        separators: [
            {title: 'Limit to', width: 56},
            {title: 'sorted by', width: 64},
            {title: 'in', width: 32}
        ]
    });

    self.$view = new Ox.InputGroup({
        inputs: [
            new Ox.Checkbox({
                width: 16
            }),
            new Ox.Select({
                items: self.options.viewKeys,
                width: 128
            })
        ],
        separators: [
            {title: 'By default, view', width: 112}
        ]
    });

    self.$save = new Ox.InputGroup({
        inputs: [
            new Ox.Checkbox({
                width: 16
            }),
            new Ox.Input({
                id: 'list',
                width: 128
            })
        ],
        separators: [
            {title: 'Save as Smart List', width: 112}
        ]
    });

    self.$items = $.merge($.merge([self.$operator], self.$conditions), [self.$limit, self.$view, self.$save]);

    self.$form = new Ox.Form({
        items: self.$items
    });
    that.$element = self.$form.$element;

    function addCondition(pos) {
        var key = self.options.findKeys[0];
        self.options.query.conditions.splice(pos, 0, {
            key: key.id,
            value: '',
            operator: self.conditionOperators[key.type][0].id
        });
        self.$conditions.splice(pos, 0, constructCondition({}, pos));
        updateConditions();
        self.$form.addItem(pos + 1, self.$conditions[pos]);
    }

    function addGroup(pos) {
        self.$form.addItem(pos + 1, constructGroup(pos))
    }

    function changeConditionKey(pos, key) {
        Ox.print('changeConditionKey', pos, key);
        var oldOperator = self.options.query.conditions[pos].operator,
            oldType = Ox.getObjectById(
                self.options.findKeys, self.options.query.conditions[pos].key
            ).type,
            newType = Ox.getObjectById(
                self.options.findKeys, key
            ).type,
            oldConditionType = getConditionType(oldType),
            newConditionType = getConditionType(newType);
            changeConditionType = oldConditionType != newConditionType;
        Ox.print('old new', oldConditionType, newConditionType)
        self.options.query.conditions[pos].key = key;
        if (changeConditionType) {
            self.$conditions[pos].replaceElement(1, constructConditionOperator(pos, oldOperator));
        }
    }

    function changeConditionOperator(pos, operator) {
        self.options.query.conditions[pos].operator = operator;
    }

    function changeOperator(event, data) {
        self.options.query.operator = data.selected[0].id;
    }

    function constructCondition(condition, pos) {
        var $condition;
        return $condition = new Ox.FormElementGroup({
            elements: [
                new Ox.Select({
                        items: $.map(self.options.findKeys, function(key) {
                            return {
                                id: key.id,
                                title: key.title
                            };
                        }),
                        //items: $.extend({}, self.options.findKeys), // fixme: Ox.Menu messes with keys
                        overlap: 'right',
                        width: 128
                    })
                    .bindEvent({
                        change: function(event, data) {
                            Ox.print('event', event)
                            changeConditionKey($condition.data('position'), data.selected[0].id);
                        }
                    }),
                constructConditionOperator(pos),
                new Ox.Input({
                    width: 256
                }),
                new Ox.Button({
                        disabled: self.options.query.conditions.length == 1,
                        id: 'remove',
                        title: 'remove',
                        type: 'image'
                    })
                    .css({margin: '0 4px 0 8px'})
                    .bindEvent({
                        click: function() {
                            removeCondition($condition.data('position'));
                        }
                    }),
                new Ox.Button({
                        id: 'add',
                        title: 'add',
                        type: 'image'
                    })
                    .css({margin: '0 4px 0 4px'})
                    .bindEvent({
                        click: function() {
                            Ox.print('add', $(this).parent().parent().data('position'))
                            addCondition($condition.data('position') + 1)
                        }
                    }),
                new Ox.Button({
                        id: 'addgroup',
                        title: 'more',
                        type: 'image'
                    })
                    .css({margin: '0 0 0 4px'})
                    .bindEvent({
                        click: function() {
                            addGroup($condition.data('position') + 1)
                        }
                    })
            ]
        })
        .data({position: pos});
    }

    function constructConditionOperator(pos, selected) {
        return new Ox.Select({
            items: $.map(self.conditionOperators[getConditionType(
                Ox.getObjectById(
                    self.options.findKeys,
                    self.options.query.conditions[pos].key
                ).type
            )], function(operator) {
                return {
                    checked: operator.id == selected, // fixme: should be "selected", not "checked"
                    id: operator.operator,
                    title: operator.title
                };
            }),
            overlap: 'right',
            width: 128
        })
        .bindEvent({
            change: function(event, data) {
                changeConditionOperator(/*$condition.data('position')*/ pos, data.selected[0].id)
            }
        });
    }

    function constructGroup() {
        // fixme: duplicated
        return new Ox.FormElementGroup({
            elements: [
                new Ox.Label({
                    title: self.options.operator == '&' ? 'and' : 'or',
                    overlap: 'right',
                    width: 48
                }),
                new Ox.FormElementGroup({
                    elements: [
                        new Ox.Select({
                                items: $.map(self.operators, function(operator) {
                                    Ox.print('!!!!', {
                                        checked: operator.id != self.options.operator,
                                        id: operator.id,
                                        title: operator.title
                                    });
                                    return {
                                        //checked: operator.id != self.options.operator,
                                        id: operator.id,
                                        title: operator.title
                                    }
                                }),
                                width: 48
                            })
                            .bindEvent({
                                change: changeOperator
                            }),
                        new Ox.Label({
                            overlap: 'left',
                            title: 'of the following conditions',
                            width: 160
                        })
                    ],
                    float: 'right',
                    width: 208
                })
            ],
            float: 'left',
        });
    }

    function getConditionType(type) {
        type = Ox.isArray(type) ? type[0] : type;
        if (['float', 'integer', 'year'].indexOf(type) > -1) {
            type = 'number';
        }
        return type;
    }

    function removeCondition(pos) {
        self.options.query.conditions.splice(pos, 1);
        self.$conditions.splice(pos, 1);
        updateConditions();
        self.$form.removeItem(pos + 1);
    }

    function updateConditions() {
        self.$conditions.forEach(function(condition, pos) {
            condition.data({position: pos});
        });
        self.$conditions[0].options('elements')[3].options({
            disabled: self.options.query.conditions.length == 1
        });
    }

    return that;

};
