/*---------------------------------------------------------
 * OpenERP web_calendar
 *---------------------------------------------------------*/

(function() {
    // Monkey patch dhtml scheduler in order to fix a bug.
    if (this.scheduler) {
        // It manually implements some kind of dbl click event
        // bubbling but fails to do it properly.
        var old_scheduler_dblclick = scheduler._on_dbl_click;
        scheduler._on_dbl_click = function(e, src) {
            if (src && !src.className) {
                return;
            } else {
                old_scheduler_dblclick.apply(this, arguments);
            }
        };

        // It uses the date+time format, leading to month range being between
        // 1/5 08:53 - 1/6 08:52 instead of 1/5 00:00 - 31/5 23:59
        scheduler._click.dhx_cal_today_button = function() {
            if (scheduler.callEvent("onBeforeTodayDisplayed", [])) {
                scheduler.setCurrentView( scheduler.date.date_part(new Date()) );
            }
        };
    }
}());

openerp.web_calendar = function(instance) {
var _t = instance.web._t,
   _lt = instance.web._lt;
var QWeb = instance.web.qweb;
instance.web.views.add('calendar', 'instance.web_calendar.CalendarView');
instance.web_calendar.CalendarView = instance.web.View.extend({
    template: "CalendarView",
    display_name: _lt('Calendar'),
// Dhtmlx scheduler ?
    init: function(parent, dataset, view_id, options) {
        var self = this;
        this._super(parent);
        this.ready = $.Deferred();
        this.set_default_options(options);
        this.dataset = dataset;
        this.model = dataset.model;
        this.fields_view = {};
        this.view_id = view_id;
        this.view_type = 'calendar';
        this.has_been_loaded = $.Deferred();
        this.dataset_events = [];
        this.COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
             '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
             '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
             '#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900' ];
        this.color_map = {};
        this.last_search = [];
        this.range_start = null;
        this.range_stop = null;
        this.update_range_dates(Date.today());
        this.selected_filters = [];
        this.is_slow_open = false;
    },
    view_loading: function(r) {
        return this.load_calendar(r);
    },
    destroy: function() {
        scheduler.clearAll();
        this._super();
    },
    load_calendar: function(data) {
        this.fields_view = data;
        this.$el.addClass(this.fields_view.arch.attrs['class']);
        this.calendar_fields = {};
        this.ids = this.dataset.ids;
        this.color_values = [];
        this.info_fields = [];

        this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
        this.view_id = this.fields_view.view_id;

        // mode, one of month, week or day
        this.mode = this.fields_view.arch.attrs.mode;

        // date_start is mandatory, date_delay and date_stop are optional
        this.date_start = this.fields_view.arch.attrs.date_start;
        this.date_delay = this.fields_view.arch.attrs.date_delay;
        this.date_stop = this.fields_view.arch.attrs.date_stop;

        this.day_length = this.fields_view.arch.attrs.day_length || 8;
        this.color_field = this.fields_view.arch.attrs.color;
        this.color_string = this.fields_view.fields[this.color_field] ?
            this.fields_view.fields[this.color_field].string : _t("Filter");

        if (this.color_field && this.selected_filters.length === 0) {
            var default_filter;
            if ((default_filter = this.dataset.context['calendar_default_' + this.color_field])) {
                this.selected_filters.push(default_filter + '');
            }
        }
        this.fields =  this.fields_view.fields;

        if (!this.date_start) {
            throw new Error(_t("Calendar view has not defined 'date_start' attribute."));
        }

        //* Calendar Fields *
        this.calendar_fields.date_start = {'name': this.date_start, 'kind': this.fields[this.date_start].type};

        if (this.date_delay) {
            if (this.fields[this.date_delay].type != 'float') {
                throw new Error(_t("Calendar view has a 'date_delay' type != float"));
            }
            this.calendar_fields.date_delay = {'name': this.date_delay, 'kind': this.fields[this.date_delay].type};
        }
        if (this.date_stop) {
            this.calendar_fields.date_stop = {'name': this.date_stop, 'kind': this.fields[this.date_stop].type};
        }

        for (var fld = 0; fld < this.fields_view.arch.children.length; fld++) {
            this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
        }

        this.init_scheduler();

        if (!this.sidebar && this.options.$sidebar) {
            this.sidebar = new instance.web_calendar.Sidebar(this);
            this.has_been_loaded.then(this.sidebar.appendTo(this.$el.find('.oe_calendar_sidebar_container')));
        }
        this.trigger('calendar_view_loaded', data);
        return this.has_been_loaded.resolve();
    },
    init_scheduler: function() {
        var self = this;
        scheduler.clearAll();
        if (this.fields[this.date_start]['type'] == 'time') {
            scheduler.config.xml_date = "%H:%M:%S";
        } else {
            scheduler.config.xml_date = "%Y-%m-%d %H:%i";
        }
        scheduler.config.api_date = "%Y-%m-%d %H:%i";
        scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
        scheduler.config.start_on_monday = Date.CultureInfo.firstDayOfWeek !== 0; //Sunday = Sunday, Others = Monday
        scheduler.config.time_step = 30;
        scheduler.config.scroll_hour = 8;
        scheduler.config.drag_resize = true;
        scheduler.config.drag_create = true;
        scheduler.config.mark_now = true;
        scheduler.config.day_date = '%l %j';
        scheduler.config.details_on_create = false;

        scheduler.locale = {
            date:{
                month_full: Date.CultureInfo.monthNames,
                month_short: Date.CultureInfo.abbreviatedMonthNames,
                day_full: Date.CultureInfo.dayNames,
                day_short: Date.CultureInfo.abbreviatedDayNames
            },
            labels:{
                dhx_cal_today_button: _t("Today"),
                day_tab: _t("Day"),
                week_tab: _t("Week"),
                month_tab: _t("Month"),
                new_event: _t("New event"),
                icon_save: _t("Save"),
                icon_cancel: _t("Cancel"),
                icon_details: _t("Details"),
                icon_edit: _t("Edit"),
                icon_delete: _t("Delete"),
                confirm_closing: "",//Your changes will be lost, are your sure ?
                confirm_deleting: _t("Event will be deleted permanently, are you sure?"),
                section_description: _t("Description"),
                section_time: _t("Time period"),
                full_day: _t("Full day"),

                /*recurring events*/
                confirm_recurring: _t("Do you want to edit the whole set of repeated events?"),
                section_recurring: _t("Repeat event"),
                button_recurring: _t("Disabled"),
                button_recurring_open: _t("Enabled"),

                /*agenda view extension*/
                agenda_tab: _t("Agenda"),
                date: _t("Date"),
                description: _t("Description"),

                /*year view extension*/
                year_tab: _t("Year"),

                /* week agenda extension */
                week_agenda_tab: _t("Agenda")
            }
        };

        scheduler.init(this.$el.find('.oe_calendar')[0], null, this.mode || 'month');
        scheduler.detachAllEvents();
        scheduler.attachEvent('onViewChange', this.proxy('view_changed'));
        scheduler.attachEvent('onEventChanged', this.proxy('quick_save'));
        scheduler.attachEvent('onEventDeleted', this.proxy('delete_event'));
        scheduler.attachEvent('onEventAdded', function(event_id, event_obj) {
            var fn = event_obj._force_slow_create ? 'slow_create' : 'quick_create';
            self[fn].apply(self, arguments);
        });
        scheduler.attachEvent('onClick', function(event_id, mouse_event) {
            if (!self.$el.find('.dhx_cal_editor').length && self.current_mode() === 'month') {
                self.open_event(event_id);
            } else {
                return true;
            }
        });
        scheduler.attachEvent('onDblClick', function(event_id, mouse_event) {
            if (!self.$el.find('.dhx_cal_editor').length) {
                self.open_event(event_id);
            }
        });
        scheduler.attachEvent('onEmptyClick', function(start_date, mouse_event) {
            scheduler._loading = false; // Dirty workaround for a dhtmleditor bug I couln't track
            if (!self.$el.find('.dhx_cal_editor').length) {
                var end_date = new Date(start_date);
                end_date.addHours(1);
                scheduler.addEvent({
                    start_date: start_date,
                    end_date: end_date,
                    _force_slow_create: true,
                });
            }
        });
        scheduler.attachEvent("onBeforeLightbox", function (event_id) {
            var index = self.dataset.get_id_index(event_id);
            if (index !== null) {
                self.open_event(self.dataset.ids[index]);
            } else {
                self.slow_create(event_id, scheduler.getEvent(event_id));
            }
           return false;
        });

        this.refresh_scheduler();

        // Remove hard coded style attributes from dhtmlx scheduler
        this.$el.find(".dhx_cal_navline").removeAttr('style');
        instance.web.bus.on('resize',this,function(){
            self.$el.find(".dhx_cal_navline").removeAttr('style');
        });
    },
    view_changed: function(mode, date) {
        this.$el.find('.oe_calendar').removeClass('oe_cal_day oe_cal_week oe_cal_month').addClass('oe_cal_' + mode);
        if (!date.between(this.range_start, this.range_stop)) {
            this.update_range_dates(date);
            this.ranged_search();
        }
        this.ready.resolve();
    },
    update_range_dates: function(date) {
        this.range_start = date.clone().moveToFirstDayOfMonth();
        this.range_stop = this.range_start.clone().addMonths(1).addSeconds(-1);
    },
    refresh_scheduler: function() {
        scheduler.setCurrentView(scheduler._date);
    },
    reload_event: function(id) {
        this.dataset.read_ids([id], _.keys(this.fields)).done(this.proxy('events_loaded'));
    },
    get_color: function(key) {
        if (this.color_map[key]) {
            return this.color_map[key];
        }
        var index = _.keys(this.color_map).length % this.COLOR_PALETTE.length;
        var color = this.COLOR_PALETTE[index];
        this.color_map[key] = color;
        return color;
    },
    events_loaded: function(events, fn_filter, no_filter_reload) {
        var self = this;

        //To parse Events we have to convert date Format
        var res_events = [],
            sidebar_items = {};
        var selection_label;
        if(this.fields[this.color_field].selection) {
            selection_label = {};
            _(this.fields[this.color_field].selection).each(function(value){
                selection_label[value[0]] = value[1];
            });
        }
        for (var e = 0; e < events.length; e++) {
            var evt = events[e];
            if (!evt[this.date_start]) {
                break;
            }

            if (this.color_field) {
                var filter = evt[this.color_field];
                if (filter) {
                    if (typeof filter !== 'object') {
                        filter = [filter, filter];
                    }
                    if (selection_label) {
                        filter[1] = selection_label[filter[0]];
                    }
                    var filter_value = filter[0];
                    if (typeof(fn_filter) === 'function' && !fn_filter(filter_value)) {
                        continue;
                    }
                    var filter_item = {
                        value: filter_value,
                        label: filter[1],
                        color: this.get_color(filter_value)
                    };
                    if (!sidebar_items[filter_value]) {
                        sidebar_items[filter_value] = filter_item;
                    }
                    evt.color = filter_item.color;
                    evt.textColor = '#ffffff';
                } else {
                    evt.textColor = '#000000';
                }
            }

            if (this.fields[this.date_start]['type'] == 'date') {
                evt[this.date_start] = instance.web.auto_str_to_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss');
            }
            if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') {
                evt[this.date_stop] = instance.web.auto_str_to_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss');
            }
            res_events.push(this.convert_event(evt));
        }
        scheduler.parse(res_events, 'json');
        this.refresh_scheduler();
        if (!no_filter_reload && this.sidebar) {
            this.sidebar.filter.events_loaded(sidebar_items);
        }
    },
    convert_event: function(evt) {
        var date_start = instance.web.str_to_datetime(evt[this.date_start]),
            date_stop = this.date_stop ? instance.web.str_to_datetime(evt[this.date_stop]) : null,
            date_delay = evt[this.date_delay] || 1.0,
            res_text = '';

        if (this.info_fields) {
            res_text = _.map(this.info_fields, function(fld) {
                if(evt[fld] instanceof Array)
                    return evt[fld][1];
                return evt[fld];
            });
            res_text = _.filter(res_text, function(e){return !_.isEmpty(e);});
        }
        if (!date_stop && date_delay) {
            date_stop = date_start.clone().addHours(date_delay);
        }
        var r = {
            'start_date': date_start.toString('yyyy-MM-dd HH:mm:ss'),
            'end_date': date_stop.toString('yyyy-MM-dd HH:mm:ss'),
            'text': res_text.join(', '),
            'id': evt.id
        };
        if (evt.color) {
            r.color = evt.color;
        }
        if (evt.textColor) {
            r.textColor = evt.textColor;
        }
        return r;
    },
    get_event_data: function(event_obj) {
        var data = {
            name: event_obj.text || scheduler.locale.labels.new_event
        };
        if (this.fields[this.date_start].type == 'date') {
            data[this.date_start] = instance.web.date_to_str(event_obj.start_date)
        }else {
            data[this.date_start] = instance.web.datetime_to_str(event_obj.start_date)
        }
        if (this.date_stop) {
            data[this.date_stop] = instance.web.datetime_to_str(event_obj.end_date);
        }
        if (this.date_delay) {
            var diff_seconds = Math.round((event_obj.end_date.getTime() - event_obj.start_date.getTime()) / 1000);
            data[this.date_delay] = diff_seconds / 3600;
        }
        return data;
    },
    do_search: function(domain, context, group_by) {
        this.last_search = arguments;
        this.ranged_search();
    },
    ranged_search: function() {
        var self = this;
        scheduler.clearAll();
        $.when(this.has_been_loaded, this.ready).done(function() {
            self.dataset.read_slice(_.keys(self.fields), {
                offset: 0,
                domain: self.get_range_domain(),
                context: self.last_search[1]
            }).done(function(events) {
                self.dataset_events = events;
                self.events_loaded(events);
                if (self.dataset.index === null) {
                    if (events.length) {
                        self.dataset.index = 0;
                    }
                } else if (self.dataset.index >= events.length) {
                    self.dataset.index = events.length ? 0 : null;
                }
            });
        });
    },
    get_range_domain: function() {
        var format = instance.web.date_to_str;
        var A = format(this.range_start.clone().addDays(-6));
        var B = format(this.range_stop.clone().addDays(6));
        var domain = [
            '&', [this.date_start, '>=', A], [this.date_start, '<=', B]
        ];
        if (this.date_stop) {
            domain.push(
            '&', [this.date_stop, '>=', A], [this.date_stop, '<=', B],
            '&', [this.date_start, '<', A], [this.date_stop, '>', B]);
            domain.unshift("|", "|");
        }
        domain.concat(this.last_search[0].slice(0))
        return domain;
    },
    do_show: function () {
        var self = this;
        $.when(this.has_been_loaded).done(function() {
            self.$el.show();
            self.do_push_state({});
        });
    },
    get_selected_ids: function() {
        // no way to select a record anyway
        return [];
    },
    current_mode: function() {
        return scheduler.getState().mode;
    },

    quick_save: function(event_id, event_obj) {
        var self = this;
        var data = this.get_event_data(event_obj);
        delete(data.name);
        var index = this.dataset.get_id_index(event_id);
        if (index !== null) {
            event_id = this.dataset.ids[index];
            this.dataset.write(event_id, data, {});
        }
    },
    quick_create: function(event_id, event_obj) {
        var self = this;
        var data = this.get_event_data(event_obj);
        this.dataset.create(data).done(function(r) {
            var id = r;
            self.dataset.ids.push(id);
            scheduler.changeEventId(event_id, id);
            self.reload_event(id);
        }).fail(function(r, event) {
            event.preventDefault();
            self.slow_create(event_id, event_obj);
        });
    },
    get_form_popup_infos: function() {
        var parent = this.getParent();
        var infos = {
            view_id: false,
            title: this.name,
        };
        if (parent instanceof instance.web.ViewManager) {
            infos.view_id = parent.get_view_id('form');
            if (parent instanceof instance.web.ViewManagerAction && parent.action && parent.action.name) {
                infos.title = parent.action.name;
            }
        }
        return infos;
    },
    slow_create: function(event_id, event_obj) {
        var self = this;
        // Workaround, some browsers trigger onEmptyClick as well as onBeforeLightbox
        // during drag&drop which calls two slow_create calls, kills the second one
        if (this.is_slow_open) {
            scheduler.deleteEvent(event_id);
            return;
        }
        this.is_slow_open = true;
        if (this.current_mode() === 'month') {
            event_obj['start_date'].addHours(8);
            var timediff = Math.abs(event_obj['end_date'] - event_obj['start_date']);
            if (timediff <= 24*60*60*1000) {  // If it spans one day or less
                event_obj['end_date'] = new Date(event_obj['start_date']);
                event_obj['end_date'].addHours(1);
            } else {
                event_obj['end_date'].addHours(-4);
            }
        }
        var defaults = {};
        _.each(this.get_event_data(event_obj), function(val, field_name) {
            defaults['default_' + field_name] = val;
        });
        var something_saved = false;
        var pop = new instance.web.form.FormOpenPopup(this);
        var pop_infos = this.get_form_popup_infos();
        pop.show_element(this.dataset.model, null, this.dataset.get_context(defaults), {
            title: _.str.sprintf(_t("Create: %s"), pop_infos.title),
            disable_multiple_selection: true,
            view_id: pop_infos.view_id,
        });
        pop.on('closed', self, function() {
            this.is_slow_open = false;
            if (!something_saved) {
                scheduler.deleteEvent(event_id);
            }
        });
        pop.on('create_completed', self, function(id) {
            something_saved = true;
            self.dataset.ids.push(id);
            scheduler.changeEventId(event_id, id);
            self.reload_event(id);
        });
    },
    open_event: function(event_id) {
        var self = this;
        var index = this.dataset.get_id_index(event_id);
        if (index === null) {
            // Some weird behaviour in dhtmlx scheduler could lead to this case
            // eg: making multiple days event in week view, dhtmlx doesn't trigger eventAdded !!??
            // so the user clicks back on the orphan event and we land here. We have to duplicate
            // the dhtmlx internal event because it will delete it on next sheduler refresh.
            var event_obj = scheduler.getEvent(event_id);
            scheduler.deleteEvent(event_id);
            scheduler.addEvent({
                start_date: event_obj.start_date,
                end_date: event_obj.end_date,
                text: event_obj.text,
                _force_slow_create: true,
            });
        } else {
            var pop = new instance.web.form.FormOpenPopup(this);
            var pop_infos = this.get_form_popup_infos();
            var id_from_dataset = this.dataset.ids[index]; // dhtmlx scheduler loses id's type
            pop.show_element(this.dataset.model, id_from_dataset, this.dataset.get_context(), {
                title: _.str.sprintf(_t("Edit: %s"), pop_infos.title),
                view_id: pop_infos.view_id,
            });
            pop.on('write_completed', self, function(){
                self.reload_event(id_from_dataset);
            });
        }
    },
    delete_event: function(event_id, event_obj) {
        // dhtmlx sends this event even when it does not exist in openerp.
        // Eg: use cancel in dhtmlx new event dialog
        var self = this;
        var index = this.dataset.get_id_index(event_id);
        if (index !== null) {
            this.dataset.unlink(this.dataset.ids[index]);
        }
    },
});

instance.web_calendar.Sidebar = instance.web.Widget.extend({
    template: 'CalendarView.sidebar',
    start: function() {
        this._super();
        this.mini_calendar = scheduler.renderCalendar({
            container: this.$el.find('.oe_calendar_mini')[0],
            navigation: true,
            date: scheduler._date,
            handler: function(date, calendar) {
                scheduler.setCurrentView(date, 'day');
            }
        });
        scheduler.linkCalendar(this.mini_calendar);
        this.filter = new instance.web_calendar.SidebarFilter(this, this.getParent());
        this.filter.appendTo(this.$el.find('.oe_calendar_filter'));
    }
});
instance.web_calendar.SidebarFilter = instance.web.Widget.extend({
    events: {
        'change input:checkbox': 'filter_click'
    },
    init: function(parent, view) {
        this._super(parent);
        this.view = view;
    },
    events_loaded: function(filters) {
        var selected_filters = this.view.selected_filters.slice(0);
        this.$el.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
        this.$('div.oe_calendar_responsible input').each(function() {
            if (_.indexOf(selected_filters, $(this).val()) > -1) {
                $(this).click();
            }
        });
    },
    filter_click: function(e) {
        var self = this,
            responsibles = [],
            $e = $(e.target);
        this.view.selected_filters = [];
        this.$('div.oe_calendar_responsible input:checked').each(function() {
            responsibles.push($(this).val());
            self.view.selected_filters.push($(this).val());
        });
        scheduler.clearAll();
        if (responsibles.length) {
            this.view.events_loaded(this.view.dataset_events, function(filter_value) {
                return _.indexOf(responsibles, filter_value.toString()) > -1;
            }, true);
        } else {
            this.view.events_loaded(this.view.dataset_events, false, true);
        }
    }
});

};

// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
