jscape.domain = jscape.domain || {};

/** @class */
jscape.TimeAccess = Class.extend(
    /** @lends {jscape.TimeAccess.prototype} */
{
    /** @constructor */
    initialize: function(a, b) {
        this.enabled = a || false;
        this.rules = b || [];
    }
});
jscape.TimeAccess.fromJSON = function(data) {
    return new jscape.TimeAccess(data && data.enabled, data ? data.rules : []);
};

/** @class */
jscape.TimeAccessRule = Class.extend(
    /** @lends {jscape.TimeAccessRule.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c) {
        this.startTime = parseInt(a) || 0;
        this.endTime = parseInt(b) || 0;
        this.dayOfWeek = c || jscape.TimeAccessRule.DAY_OF_WEEK.monday;
    }
});
jscape.TimeAccessRule.DAY_OF_WEEK = {"monday": 2, "tuesday": 3, "wednesday": 4, "thursday": 5, "friday": 6, "saturday": 7, "sunday": 1};
jscape.TimeAccessRule.fromJSON = function(data) {
    return data ? new jscape.TimeAccessRule(data.startTime, data.endTime, data.dayOfWeek): null;
};

/** @class */
jscape.domain.TimeAccessModule = jscape.domain.DomainModule.extend(
    /** @lends {jscape.domain.TimeAccessModule.prototype} */
{
    onCreate: function () {
        this._super.apply(this, arguments);

        this.controller = new jscape.domain.TimeAccessController();
        this.controller.onCreate(this.managementService, this.domainName);
    },

    onStart: function () {
        this.controller.onStart();
    },

    onPause: function() {
        if (this.controller.hasChanged()) {
            this._confirmAndApply([this.controller]);
        }
    },

    onResume: function() {
        this.controller.onResume();
    }
});

/** @class */
jscape.domain.TimeAccessController = Class.extend(
    /** @lends {jscape.domain.TimeAccessController.prototype} */
{
    DEFAULT_DESCRIPTOR: new jscape.TimeAccess(false),

    view: null,
    descriptor: null,

    /** @constructor */
    initialize: function() {
        this._initValidators();
    },

    onCreate: function (managementService, domainName) {
        this.managementService = managementService;
        this.domainName = domainName;
    },

    onStart: function () {
        if (this.view == null) {
            this.view = new jscape.domain.TimeAccessView();
        }
        this.view.show(this);
    },

    hasChanged: function() {
        return this.descriptor && !Object._equals(this.descriptor, this._createDescriptor());
    },

    onResume: function () {
        this.managementService.getTimeAccess(this.domainName)
                .done($.proxy(function (descriptor) { this.descriptor = descriptor; }, this))
                .fail($.proxy(function () { this.descriptor = this._copyDescriptor(this.DEFAULT_DESCRIPTOR); }, this))
                .always($.proxy(function () { this._setupView(this.descriptor); }, this));
    },

    onApply: function () {
        var descriptor = this._createDescriptor();
        return this.managementService.setTimeAccess(this.domainName, descriptor)
                .done($.proxy(function (descriptor) {
                          this.descriptor = this._copyDescriptor(descriptor);
                          this._fireApplySuccess();
                      }, this));
    },

    onDiscard: function() {
        var descriptor = this.descriptor;
        this.descriptor = this._copyDescriptor(descriptor);
        this._setupView(this.descriptor);
    },

    _createDescriptor: function() {
        var descriptor = this._copyDescriptor(this.descriptor);
        descriptor.enabled = this.view.getEnabled();
        descriptor.rules = this.view.getRules();
        return descriptor;
    },

    _copyDescriptor: function(descriptor) {
        var rules = descriptor ? $.map(descriptor.rules, function(rule) { return $.extend(new jscape.TimeAccessRule(), rule); }) : [];
        return new jscape.TimeAccess(descriptor && descriptor.enabled, rules);
    },

    _setupView: function(descriptor) {
        if (descriptor && this.view) {
            this.view.setEnabled(descriptor.enabled);
            this.view.setRules(descriptor.rules);
        }
    },

    _fireApplySuccess: function () {
        $(document).triggerHandler("broadcast", [window.R.string.APPLICATION_SUCCESS_APPLY]);
    },

    _initValidators: function() {
        var v = new jscape.Validator();
        v.rule("time_span", $.proxy(this._validRange, this), window.R.string.TIME_ACCESS_BAD_RULE);
    },

    _validRange: function(value, params) {
        if (value && params && params.length > 1) {
            value = this._toMillis(value);
            var min = this._toMillis(params[0]);
            var max = this._toMillis(params[1]);
            return value >= min && value <= max;
        }
        return true;
    },

    _toMillis: function(value) {
        var values = value.split($.fn.timespinner.defaults.separator);
        var hours = parseInt(values[0], 10);
        var minutes = parseInt(values[1], 10);
        return hours * 60 * 60 * 1000 + minutes * 60 * 1000;
    }
});

/** @class */
jscape.domain.TimeAccessView = Class.extend(
    /** @lends {jscape.domain.TimeAccessView.prototype} */
{
    DEFAULT_START_TIME: 0,
    DEFAULT_END_TIME: 24 * 60 * 60 * 1000 - 1,

    /** @constructor */
    initialize: function() {
        this.fieldset = $("fieldset[name=timeaccessfields]");

        this.enabled = $("input[name=enable]:checkbox", this.fieldset);
        this.enabled.on("change", $.proxy(this._adjustFieldState, this));

        this.mondayView = new jscape.domain.DayOfWeekAccessView($("[name=monday]", this.fieldset),
                                                                $("[name=mondayfrom]", this.fieldset),
                                                                $("[name=mondayto]", this.fieldset));
        this.tuesdayView = new jscape.domain.DayOfWeekAccessView($("[name=tuesday]", this.fieldset),
                                                                 $("[name=tuesdayfrom]", this.fieldset),
                                                                 $("[name=tuesdayto]", this.fieldset));
        this.wednesdayView = new jscape.domain.DayOfWeekAccessView($("[name=wednesday]", this.fieldset),
                                                                   $("[name=wednesdayfrom]", this.fieldset),
                                                                   $("[name=wednesdayto]", this.fieldset));
        this.thursdayView = new jscape.domain.DayOfWeekAccessView($("[name=thursday]", this.fieldset),
                                                                  $("[name=thursdayfrom]", this.fieldset),
                                                                  $("[name=thursdayto]", this.fieldset));
        this.fridayView = new jscape.domain.DayOfWeekAccessView($("[name=friday]", this.fieldset),
                                                                $("[name=fridayfrom]", this.fieldset),
                                                                $("[name=fridayto]", this.fieldset));
        this.saturdayView = new jscape.domain.DayOfWeekAccessView($("[name=saturday]", this.fieldset),
                                                                  $("[name=saturdayfrom]", this.fieldset),
                                                                  $("[name=saturdayto]", this.fieldset));
        this.sundayView = new jscape.domain.DayOfWeekAccessView($("[name=sunday]", this.fieldset),
                                                                $("[name=sundayfrom]", this.fieldset),
                                                                $("[name=sundayto]", this.fieldset));

        this.applyButton = $("a[role=button][name=apply]", this.fieldset);
        this.applyButton.linkbutton({onClick: $.proxy(this._onClick$ApplyButton, this)});

        this.discardButton = $("a[role=button][name=discard]", this.fieldset);
        this.discardButton.linkbutton({onClick: $.proxy(this._onClick$DiscardButton, this)});
    },

    getEnabled: function() {
        return this.enabled.is(":checked");
    },

    setEnabled: function(value) {
        this.enabled.prop("checked", !!value);
        this._adjustFieldState();
    },

    getRules: function () {
        return $.grep([
                this._getAccess(this.mondayView, jscape.TimeAccessRule.DAY_OF_WEEK.monday),
                this._getAccess(this.tuesdayView, jscape.TimeAccessRule.DAY_OF_WEEK.tuesday),
                this._getAccess(this.wednesdayView, jscape.TimeAccessRule.DAY_OF_WEEK.wednesday),
                this._getAccess(this.thursdayView, jscape.TimeAccessRule.DAY_OF_WEEK.thursday),
                this._getAccess(this.fridayView, jscape.TimeAccessRule.DAY_OF_WEEK.friday),
                this._getAccess(this.saturdayView, jscape.TimeAccessRule.DAY_OF_WEEK.saturday),
                this._getAccess(this.sundayView, jscape.TimeAccessRule.DAY_OF_WEEK.sunday)
            ],
            function (rule) { return rule != null; }
        );
    },

    setRules: function(values) {
        this._setAccess(this.mondayView, this._ruleFor(values, jscape.TimeAccessRule.DAY_OF_WEEK.monday));
        this._setAccess(this.tuesdayView, this._ruleFor(values, jscape.TimeAccessRule.DAY_OF_WEEK.tuesday));
        this._setAccess(this.wednesdayView, this._ruleFor(values, jscape.TimeAccessRule.DAY_OF_WEEK.wednesday));
        this._setAccess(this.thursdayView, this._ruleFor(values, jscape.TimeAccessRule.DAY_OF_WEEK.thursday));
        this._setAccess(this.fridayView, this._ruleFor(values, jscape.TimeAccessRule.DAY_OF_WEEK.friday));
        this._setAccess(this.saturdayView, this._ruleFor(values, jscape.TimeAccessRule.DAY_OF_WEEK.saturday));
        this._setAccess(this.sundayView, this._ruleFor(values, jscape.TimeAccessRule.DAY_OF_WEEK.sunday));
    },

    show: function(listener) {
        this.listener = listener || jscape.domain.TimeAccessView.LISTENER;
        this._adjustFieldState();
    },

    _onClick$ApplyButton: function() {
        if (this.fieldset.form("validate")) {
            this.applyButton.linkbutton("disable");
            this.listener.onApply(this)
                    .always($.proxy(function() { this.applyButton.linkbutton("enable"); }, this));
        }
    },

    _onClick$DiscardButton: function() {
        this.listener.onDiscard();
    },

    _ruleFor: function(rules, dayOfWeek) {
        for (var i = 0; i < rules.length; i++) {
            var rule = rules[i];
            if (rule.dayOfWeek == dayOfWeek) {
                return rule;
            }
        }
        return null;
    },

    _getAccess: function(view, dayOfWeek) {
        return view.isAllowed() ? new jscape.TimeAccessRule(view.getStartTime(), view.getEndTime(), dayOfWeek): null;
    },

    _setAccess: function(view, value) {
        if (value != null) {
            view.setAllowed(true);
            view.setStartTime(value.startTime);
            view.setEndTime(value.endTime);
        } else {
            view.setAllowed(false);
            view.setStartTime(this.DEFAULT_START_TIME);
            view.setEndTime(this.DEFAULT_END_TIME);
        }
    },

    _adjustFieldState: function() {
        var enabled = this.getEnabled();
        this.mondayView.setEnabled(enabled);
        this.tuesdayView.setEnabled(enabled);
        this.wednesdayView.setEnabled(enabled);
        this.thursdayView.setEnabled(enabled);
        this.fridayView.setEnabled(enabled);
        this.saturdayView.setEnabled(enabled);
        this.sundayView.setEnabled(enabled);
    }
});
jscape.domain.TimeAccessView.LISTENER = {
    onApply: $.noop,
    onDiscard: $.noop
};

/** @class */
jscape.domain.DayOfWeekAccessView = Class.extend(
    /** @lends {jscape.domain.DayOfWeekAccessView.prototype} */
{
    MILLIS_IN_HOUR: 60 * 60 * 1000,
    MILLIS_IN_MINUTE: 60 * 1000,
    MIN_VALUE: "00:00",
    MAX_VALUE: "23:59",

    /** @constructor */
    initialize: function (allowedInput, startTimeInput, endTimeInput) {
        this.allowed = allowedInput;
        this.allowed.click($.proxy(this._adjustFieldState, this));

        this.startTime = startTimeInput;
        this.startTime.timespinner({
            required: true,
            disabled: true,
            min: this.MIN_VALUE,
            max: this.MAX_VALUE,
            value: this.MIN_VALUE,
            validType: "time_span",
            tipPosition: "left",
            onSpinUp: $.proxy(this._adjustTimeSpan, this),
            onSpinDown: $.proxy(this._adjustTimeSpan, this),
            width: 70
        });

        this.endTime = endTimeInput;
        this.endTime.timespinner({
            required: true,
            showSeconds: false,
            disabled: true,
            min: this.MIN_VALUE,
            max: this.MAX_VALUE,
            value: this.MAX_VALUE,
            validType: "time_span",
            tipPosition: "right",
            onSpinUp: $.proxy(this._adjustTimeSpan, this),
            onSpinDown: $.proxy(this._adjustTimeSpan, this),
            width: 70
        });
    },

    show: function(listener) {
        this.listener = listener || jscape.domain.DayOfWeekAccessView.LISTENER;
        this.startTime.timespinner("reset");
        this.endTime.timespinner("reset");
    },

    isEnabled: function() {
        return !this.allowed.is(":disabled");
    },

    setEnabled: function(value) {
        this.allowed.prop("disabled", !value);
        this._adjustFieldState();
    },

    isAllowed: function() {
        return this.allowed.is(":checked");
    },

    setAllowed: function(value) {
        this.allowed.prop("checked", !!value);
        this._adjustFieldState();
    },

    getStartTime: function() {
        return this.startTime.timespinner("getHours") * this.MILLIS_IN_HOUR
               + this.startTime.timespinner("getMinutes") * this.MILLIS_IN_MINUTE;
    },

    setStartTime: function(time) {
        var date = new Date(time);
        var value = [date.getUTCHours(), date.getUTCMinutes()].join(this.startTime.timespinner("options").separator);
        this.startTime.timespinner("setValue", value);
    },

    getEndTime: function() {
        return this.endTime.timespinner("getHours") * this.MILLIS_IN_HOUR
               + this.endTime.timespinner("getMinutes") * this.MILLIS_IN_MINUTE;
    },

    setEndTime: function(time) {
        var date = new Date(time);
        var value = [date.getUTCHours(), date.getUTCMinutes()].join(this.startTime.timespinner("options").separator);
        this.endTime.timespinner("setValue", value);
    },

    _adjustTimeSpan: function() {
        this.startTime.timespinner("textbox").validatebox({validType: "time_span['" + this.MIN_VALUE + "','" + this.endTime.timespinner("getValue") + "']"});
        this.endTime.timespinner("textbox").validatebox({validType: "time_span['" + this.startTime.timespinner("getValue") + "','" + this.MAX_VALUE + "']"});
    },

    _adjustFieldState: function() {
        var enabled = this.isAllowed() && this.isEnabled();
        this.startTime.timespinner(enabled ? "enable" : "disable").timespinner(enabled ? "enableValidation" : "disableValidation");
        this.endTime.timespinner(enabled ? "enable" : "disable").timespinner(enabled ? "enableValidation" : "disableValidation");
    }
});
jscape.domain.DayOfWeekAccessView.LISTENER = {};