//SCREEN-BOILERPLATE
//this boilerplate builds screens that are gonna be shown in the <div class='content'> tag of our main HTML.
//do not build app components with this boilerplate, only screens (reports)
//
// EAF Delays Manager — modern refactor of the legacy custom-screens/delays/delays-1 screen.
// Keeps the IHDelays2.WebApp backend and the EAF feature set (Declare / Split / Join) but renders
// it with AG Grid and the Caster Delays Manager visual style.

define([
    // Application variable, always include it to have access to app methods.
    'app',

    //templates-loader: this loads templates async.
    'js/templates-loader',
    'backgrid',
    'moment',

    'modules/modal2',

    'components/ag-grid/ag-grid-module',
    'components/modal-confirmation/modal-confirmation',
    'backgrid/infinator',
    'backgrid/moment-cell',
    'backgrid/grouped-columns',

    "js/jquery.floatThead/jquery.floatThead",
    'js/jquery.contextMenu/jquery.contextMenu',
    "js/bootstrap-slider/bootstrap-slider.min"
],
    function (app, T, Backgrid, moment, Modal, AgGridModule, ModalConfirmation) {

        var Screen = { Models: {}, Views: {}, Collections: {} };

        var CREWS = [
            { value: 1, label: 'A' },
            { value: 2, label: 'B' },
            { value: 3, label: 'C' },
            { value: 4, label: 'D' }
        ];

        Screen.generateID = function (viewParams) {
            try { return 'delays-manager'; } catch (Error) { }
        };

        Screen.Models.Main = Backbone.Epoxy.Model.extend({
            defaults: {
                start: new moment().subtract(1, 'days').format('YYYY-MM-DD'),
                end: new moment().format('YYYY-MM-DD'),

                machineId: -1,
                machines: [],

                shiftId: -1,
                shifts: [],

                crewId: -1,
                crews: CREWS,

                delayCategoryId: -1,
                delayCategories: [],

                hasData: false,
                isLoading: false,
                isPageLoading: false
            },
            computeds: {
                start$: {
                    deps: ['start'],
                    get: function (value) {
                        return value;
                    },
                    set: function (value) {
                        return { start: (value) ? new moment(value, app.translate(app, 'MM/DD/YYYY')).format('YYYY-MM-DD') : value };
                    }
                },
                end$: {
                    deps: ['end'],
                    get: function (value) {
                        return value;
                    },
                    set: function (value) {
                        return { end: (value) ? new moment(value, app.translate(app, 'MM/DD/YYYY')).format('YYYY-MM-DD') : value };
                    }
                }
            },
            initialize: function () {
                this.fetchProductionItems();
                this.fetchShifts();
                this.fetchDelayCategories();
            },
            fetchProductionItems: function () {
                var that = this;
                Core.Json.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.GetProductionItems', null, {
                    onSuccess: function (data) {
                        if (data && data.Table) {
                            that.set({
                                machines: _.map(_.filter(data.Table, function (m) {
                                    return m.ProductionItemType && m.ProductionItemType.toUpperCase() === 'MACHINE';
                                }), function (m) {
                                    return { value: m.Id, label: m.Name };
                                })
                            });
                        }
                    },
                    Async: true,
                    Secured: true
                }, app.ConnectionStrings.app);
            },
            fetchShifts: function () {
                var that = this;
                Core.Json.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.GetShifts', null, {
                    onSuccess: function (data) {
                        if (data && data.Table) {
                            that.set({
                                shifts: _.map(data.Table, function (m) {
                                    return { value: m.Id, label: m.Name };
                                })
                            });
                        }
                    },
                    Async: true,
                    Secured: true
                }, app.ConnectionStrings.app);
            },
            fetchDelayCategories: function () {
                var that = this;
                Core.Json.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.GetDelayCategories', null, {
                    onSuccess: function (data) {
                        if (data && data.Table) {
                            that.set({
                                delayCategories: _.map(data.Table, function (m) {
                                    return { value: m.Id, label: m.Name };
                                })
                            });
                        }
                    },
                    Async: true,
                    Secured: true
                }, app.ConnectionStrings.app);
            }
        });

        var CustomAgGridModule = AgGridModule.extend({
            initialize: function (options) {
                this.bus = options.bus;
                this.itemsColl = options.delaysColl;
                this.columnDefs = [
                    { field: 'Start', headerName: 'Start', flex: 1, minWidth: 120, fieldType: 'Datetime', format: 'YYYY/MM/DD HH:mm', cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'End', headerName: 'End', flex: 1, minWidth: 120, fieldType: 'Datetime', format: 'YYYY/MM/DD HH:mm', cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'Duration', headerName: 'Duration', flex: 1, minWidth: 90, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'ProductionItem', headerName: 'Furnace', flex: 1, minWidth: 80, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'Shift', headerName: 'Shift', flex: 1, minWidth: 70, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'Crew', headerName: 'Crew', flex: 1, minWidth: 70, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'CodeGroup', headerName: 'Code Group', flex: 1, minWidth: 90, cellClass: 'text-center', headerClass: 'ag-header-cell-center', cellRenderer: this.translateCodeRenderer.bind(this) },
                    { field: 'Code', headerName: 'Code', flex: 1, minWidth: 90, cellClass: 'text-center', headerClass: 'ag-header-cell-center', cellRenderer: this.translateCodeRenderer.bind(this) },
                    { field: 'Comments', headerName: 'Comments', flex: 2, minWidth: 120, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    {
                        headerName: 'Actions',
                        field: null,
                        cellRenderer: this.actionsCellRenderer.bind(this),
                        minWidth: 110,
                        sortable: false,
                        cellClass: 'text-center',
                        headerClass: 'ag-header-cell-center'
                    }
                ];

                options.columnDefs = this.columnDefs;

                if (!options.gridOptions) {
                    options.gridOptions = {};
                }
                options.gridOptions.getRowClass = this.getRowClass.bind(this);
                options.gridOptions.getRowStyle = this.getRowStyle.bind(this);
                // Start in loading state so the first paint shows "Loading..." instead of
                // "No Rows To Show" (gridApi isn't ready yet for the first showLoadingOverlay call).
                options.gridOptions.loading = true;

                AgGridModule.prototype.initialize.call(this, options);
            },

            isUndeclared: function (data) {
                if (!data) return false;
                return (data.DelayCodeId === null || data.DelayCodeId === undefined || data.DelayCodeId === '' ||
                    !data.Code || data.Code === 'NOTDECLARED');
            },
            getRowClass: function (params) {
                if (params.data && this.isUndeclared(params.data)) {
                    return 'undeclared-delay-row';
                }
                return '';
            },
            getRowStyle: function (params) {
                if (params && params.data && this.isUndeclared(params.data)) {
                    return { backgroundColor: '#D0A4A4' };
                }
                return null;
            },
            translateCodeRenderer: function (params) {
                var value = params.value;
                if (value === null || value === undefined || value === '') return '';
                // Codes/groups are stored as human-readable names; only the sentinel needs mapping.
                var labels = { 'NOTDECLARED': 'Not Declared', 'MAINTENANCE': 'Maintenance', 'MICRODELAY': 'Micro Delay' };
                return labels[value] || value;
            },

            actionsCellRenderer: function (params) {
                var rowData = params.data;
                var that = this;

                // Declare / Edit button (always available)
                var declareBtn = document.createElement('button');
                declareBtn.className = 'MuiButtonBase-root MuiIconButton-root';
                declareBtn.setAttribute('title', 'Declare');
                declareBtn.style.display = 'inline-block';
                declareBtn.style.minWidth = '30px';
                declareBtn.style.minHeight = '24px';
                declareBtn.style.marginRight = '8px';
                declareBtn.style.zIndex = '1000';
                declareBtn.innerHTML = `
                    <span class="MuiIconButton-label">
                        <svg class="MuiSvgIcon-root text-blue-500 MuiSvgIcon-fontSizeInherit" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="cursor: pointer;">
                            <path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18zM18 14H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"></path>
                        </svg>
                    </span>`;

                // Split button (only when the delay has an end and a positive duration)
                var splitBtn = document.createElement('button');
                splitBtn.className = 'MuiButtonBase-root MuiIconButton-root';
                splitBtn.setAttribute('title', 'Split');
                splitBtn.style.display = 'inline-block';
                splitBtn.style.minWidth = '30px';
                splitBtn.style.minHeight = '24px';
                splitBtn.style.marginRight = '8px';
                splitBtn.style.zIndex = '1000';
                splitBtn.innerHTML = `
                    <span class="MuiIconButton-label">
                        <svg class="MuiSvgIcon-root text-blue-500 MuiSvgIcon-fontSizeInherit" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="cursor: pointer;">
                            <path d="M14 4l2.29 2.29-2.88 2.88 1.42 1.42 2.88-2.88L20 10V4zm-4 0H4v6l2.29-2.29 4.71 4.7V20h2v-8.41l-5.29-5.3z"></path>
                        </svg>
                    </span>`;
                var canSplit = !!rowData.End && Number(rowData.Seconds) > 0;
                if (!canSplit) {
                    splitBtn.disabled = true;
                    splitBtn.style.opacity = '0.4';
                    splitBtn.style.cursor = 'default';
                }

                // Join button (only when the delay is a split child — has a ParentId)
                var joinBtn = document.createElement('button');
                joinBtn.className = 'MuiButtonBase-root MuiIconButton-root';
                joinBtn.setAttribute('title', 'Join');
                joinBtn.style.display = 'inline-block';
                joinBtn.style.minWidth = '30px';
                joinBtn.style.minHeight = '24px';
                joinBtn.style.zIndex = '1000';
                joinBtn.innerHTML = `
                    <span class="MuiIconButton-label">
                        <svg class="MuiSvgIcon-root text-blue-500 MuiSvgIcon-fontSizeInherit" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="cursor: pointer;">
                            <path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"></path>
                        </svg>
                    </span>`;
                var canJoin = !!rowData.ParentId;
                if (!canJoin) {
                    joinBtn.disabled = true;
                    joinBtn.style.opacity = '0.4';
                    joinBtn.style.cursor = 'default';
                }

                var container = document.createElement('div');
                container.className = 'flex justify-center';
                container.style.display = 'inline-block';
                container.style.minWidth = '100px';
                container.style.minHeight = '24px';
                container.appendChild(declareBtn);
                container.appendChild(splitBtn);
                container.appendChild(joinBtn);

                declareBtn.addEventListener('click', function () {
                    var declareModal = new Screen.Views.DeclareDelay({
                        viewParams: { rowData: { id: rowData.Id, model: rowData } },
                        // Seed the modal from the grid row so Start/End/Duration show immediately;
                        // GetDelayData is then used only to load the hierarchical code list.
                        model: new Screen.Models.DeclareDelay({
                            id: rowData.Id,
                            delayId: rowData.Id,
                            parentId: rowData.ParentId,
                            productionItem: rowData.ProductionItem,
                            start: rowData.Start,
                            end: rowData.End,
                            durationinMin: rowData.Duration,
                            commentsDelay: rowData.Comments || '',
                            delayCodeId: (rowData.DelayCodeId != null) ? rowData.DelayCodeId : -1
                        }),
                        bus: that.bus
                    });
                    declareModal.show();
                });

                splitBtn.addEventListener('click', function () {
                    if (!canSplit) return;
                    var splitModal = new Screen.Views.SplitDelay({
                        viewParams: { rowData: { id: rowData.Id, model: rowData } },
                        model: new Screen.Models.SplitDelay({
                            id: rowData.Id,
                            start: new moment(rowData.Start),
                            end: new moment(rowData.End),
                            duration: Number(rowData.Seconds)
                        }),
                        bus: that.bus
                    });
                    splitModal.show();
                });

                joinBtn.addEventListener('click', function () {
                    if (!canJoin) return;
                    var modalConfirmation = new ModalConfirmation.Views.ModalConfirmation({
                        model: new ModalConfirmation.Models.ModalConfirmation({
                            message: 'Are you sure you want to join this delay back with its parent?',
                            title: 'Join Delay Confirmation',
                            acceptBtnLabel: 'JOIN',
                            cancelBtnLabel: 'CANCEL',
                            action: 'CONFIRM'
                        }),
                        bus: that.bus
                    });
                    modalConfirmation.listenToOnce(modalConfirmation, 'acceptBtn', function () {
                        try {
                            that.joinDelay(rowData);
                        } catch (error) {
                            console.error((error.stack) ? error.stack : new Error(error).stack);
                        }
                    });
                    modalConfirmation.show();
                });

                return container;
            },
            joinDelay: function (rowData) {
                var that = this;
                var QP = new Core.Database.QueryParameters();
                QP.Add('Id', 'INT', rowData.Id);
                app.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.JoinDelay', QP, {
                    onSuccess: function () {
                        that.bus.trigger('refreshGrid');
                        app.views.topMessages.showMessage('Delay Joined', { stay: 5000 });
                    },
                    onFailure: function (resp) {
                        console.error(resp);
                    },
                    Secured: true
                }, app.ConnectionStrings.app);
            }
        });

        Screen.Views.Main = Backbone.Epoxy.View.extend({
            template: 'delays-manager',
            id: 'delays-manager',
            title: 'EAF Delays Manager',
            events: function () {
                return {
                    'click #refreshBtn': this.refreshBtn_click,
                    'click #exportExcelBtn': this.exportToExcel,
                    'click #calendarStartBtn': this.calendarStartBtn_click,
                    'click #calendarEndBtn': this.calendarEndBtn_click
                };
            },
            bindings: 'data-bind',
            bindingSources: null,
            grids: null,
            itemsColl: null,
            subviews: null,
            viewParams: null,
            initialize: function () {
                this.options.state = app.view_states.loading;
                this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };
                this.bus = _.extend({}, Backbone.Events);

                this.itemsColl = new Screen.Collections.Delays();

                this.agGridModule = new CustomAgGridModule({
                    delaysColl: this.itemsColl,
                    bus: this.bus,
                    rowData: [],
                    pagination: true,
                    onChange: this.handleGridChange.bind(this),
                    onRemove: this.handleGridRemove.bind(this),
                    fileName: 'eaf-delays-manager'
                });

                if (!this.model)
                    this.model = new Screen.Models.Main();

                this.grids = {};
                this.bindingSources = {};
                this.subviews = {};
                this.autoRefresh = {
                    enabled: null,
                    toid: null,
                    every: 5 * 1000
                };

                this.bindEvents();

                this.bus.on('refreshGrid', this._refresh, this);
                this.bus.on('modalAcceptBtn', this.onModalAcceptBtn, this);
            },

            handleGridChange: function () { },
            handleGridRemove: function () { },

            calendarStartBtn_click: function () {
                this.$el.find('#dateStartTbx').datepicker('show');
            },
            calendarEndBtn_click: function () {
                this.$el.find('#dateEndTbx').datepicker('show');
            },

            onModalAcceptBtn: function () {
                this.refreshBtn_click();
            },
            bindEvents: function () {
                this.listenTo(this.itemsColl, 'fetch', this.processes_ready)
                    .listenTo(this.model, 'triggerFetch', this.refreshBtn_click);
            },
            _refresh: function (opt) {
                this.itemsColl.reset();
                this.model.set({ hasData: false });

                if (this.autoRefresh.toid != null) {
                    clearTimeout(this.autoRefresh.toid);
                    this.autoRefresh.toid = null;
                }

                this.refreshProcesses(opt);

                if (this.autoRefresh.enabled == true) {
                    var that = this;
                    this.autoRefresh.toid = setTimeout(
                        function () { that._refresh(); },
                        this.autoRefresh.every
                    );
                }
            },
            render: function (container, viewParams) {
                var that = this;

                this.viewParams = viewParams || {};

                that.model.set({ isPageLoading: true });

                var thatContainer = (this.options.container) ? this.options.container : container;
                this.options.container = thatContainer;

                var customPath = '/app/pages/eaf/' + this.template + '/';

                T.render.call(
                    this,
                    this.template,
                    function (tmp) {
                        if (!that.options.i18n) that.options.i18n = {};

                        app.getI18NJed(
                            that,
                            that.template,
                            function (i18nJED) {
                                that.options.i18n[that.template] = i18nJED;

                                that.$el.html(tmp());
                                that.applyBindings();

                                that.$el.find('.input-date').datepicker();

                                that.agGridModule.render();
                                that.$el.find('.processes-grid-container').append(that.agGridModule.$el);

                                that.append(thatContainer, that.$el);

                                // Apply URL params over the model before first fetch.
                                var fixedParams = _.extend({}, viewParams);
                                if (fixedParams.start)
                                    fixedParams.start = new moment(fixedParams.start, 'YYYYMMDD').format('YYYY-MM-DD');
                                if (fixedParams.end)
                                    fixedParams.end = new moment(fixedParams.end, 'YYYYMMDD').format('YYYY-MM-DD');
                                if (viewParams.machineId)
                                    fixedParams.machineId = (viewParams.machineId != '-' && viewParams.machineId != null) ? parseInt(viewParams.machineId) : -1;
                                if (viewParams.shiftId)
                                    fixedParams.shiftId = (viewParams.shiftId != '-' && viewParams.shiftId != null) ? parseInt(viewParams.shiftId) : -1;
                                if (viewParams.crewId)
                                    fixedParams.crewId = (viewParams.crewId != '-' && viewParams.crewId != null) ? parseInt(viewParams.crewId) : -1;
                                if (viewParams.delayCategoryId)
                                    fixedParams.delayCategoryId = (viewParams.delayCategoryId != '-' && viewParams.delayCategoryId != null) ? parseInt(viewParams.delayCategoryId) : -1;
                                that.model.set(fixedParams);

                                var params = that.model.toJSON();

                                that._refresh({
                                    reset: true,
                                    params: _.extend({}, params, {})
                                });

                                that.model.set({ isPageLoading: false });
                            },
                            true,
                            customPath
                        );
                    },
                    customPath, "delays_report"
                );
            },
            processes_ready: function () {
                try {
                    var that = this;
                    var collectionData = that.itemsColl;
                    setTimeout(
                        function () {
                            that.agGridModule.setRowDataAsCollection(collectionData);
                            that.agGridModule.hideOverlay();
                            if (that.agGridModule.getGridApi()) {
                                that.agGridModule.getGridApi().refreshCells({ force: true });
                            }
                            that.model.set({
                                hasData: (that.itemsColl.length > 0),
                                isLoading: false
                            });
                        },
                        100
                    );
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            refreshProcesses: function (options) {
                var attrs = this.model.toJSON(),
                    opt = _.extend({}, options, { params: {} });

                if ((opt.refresh == true) || (opt.reset == true)) {
                    this.model.set('isLoading', true);
                    this.agGridModule.showLoadingOverlay();
                }

                this.itemsColl.fetch(_.extend({}, attrs, { refresh: true }));
            },
            refreshBtn_click: function () {
                try {
                    var params = this.model.toJSON();
                    app.router.navigate(
                        app.router.resolveURL(
                            app.router.currentModule,
                            _.extend({}, params, {
                                start: new moment(params.start, 'YYYY-MM-DD').format('YYYYMMDD'),
                                end: new moment(params.end, 'YYYY-MM-DD').format('YYYYMMDD'),
                                machineId: (params.machineId == null || params.machineId == '') ? '-' : params.machineId,
                                shiftId: (params.shiftId == null || params.shiftId == '') ? '-' : params.shiftId,
                                crewId: (params.crewId == null || params.crewId == '') ? '-' : params.crewId,
                                delayCategoryId: (params.delayCategoryId == null || params.delayCategoryId == '') ? '-' : params.delayCategoryId
                            }),
                            false
                        ),
                        { trigger: false }
                    );

                    this._refresh({
                        reset: true,
                        params: _.extend({}, params, {})
                    });

                    window.scrollTo(0, 0);
                } catch (Error) { console.error(Error.stack); }
            },
            exportToExcel: function () {
                this.agGridModule.exportToExcel(false);
            },

            //Common functions
            append: function (container, el) {
                el = (el != null && el != undefined) ? el : this.$el;

                if (this.options.state == app.view_states.loading
                    || this.options.state == app.view_states.shown) {
                    this.options.state = app.view_states.shown;
                    container.append(el);
                    this.options.onappend(this);
                }

                if (this.options.state == app.view_states.hidden) {
                    container.append(el);
                }

                if (this.options.state == app.view_states.closed) {
                    return;
                }
            },
            close: function () {
                this.options.state = app.view_states.closed;

                if (this.autoRefresh.toid != null) {
                    clearTimeout(this.autoRefresh.toid);
                    this.autoRefresh.toid = null;
                }

                this.closeSubviews();
                this.remove();
                this.unbind();
            },
            closeSubviews: function () {
                _.each(this.subviews, function (sview) {
                    sview.close();
                });
            },
            show: function () {
                this.options.state = app.view_states.shown;
                this.showSubviews();
                this.bindEvents();
                this.$el.show();
            },
            showSubviews: function () {
                _.each(this.subviews, function (sview) {
                    sview.show();
                });
            },
            hide: function () {
                this.options.state = app.view_states.hidden;
                this.hideSubviews();

                if (this.autoRefresh.toid != null) {
                    clearTimeout(this.autoRefresh.toid);
                    this.autoRefresh.toid = null;
                }

                this.$el.hide();
                this.unbind();
                this.stopListening();
            },
            hideSubviews: function () {
                _.each(this.subviews, function (sview) {
                    sview.hide();
                });
            },
            preRender: function () {
                app.models.subnavbar.set("subnavbar", false);
            },
            reRender: function (viewParams) {
                try {
                    this.refresh(viewParams);
                } catch (Error) { }
            }
        });

        Screen.Collections.Delays = Backbone.Collection.extend({
            isFetching: false,

            fixedParameters: [],

            fetch: function (params) {
                var that = this;
                var qp = new Core.Database.QueryParameters();
                var options = params ? _.clone(params) : {};

                if (options.refresh) {
                    var crew = null;
                    if (options.crewId && options.crewId != -1) {
                        var crewItem = _.findWhere(CREWS, { value: parseInt(options.crewId) });
                        crew = crewItem ? crewItem.label : null;
                    }

                    this.fixedParameters = [
                        { Name: '@From', Type: 'DATETIME', Value: options.start },
                        { Name: '@To', Type: 'DATETIME', Value: options.end },
                        { Name: '@ProductionItemId', Type: 'INT', Value: (options.machineId && options.machineId != -1) ? options.machineId : null },
                        { Name: '@Timezone', Type: 'VARCHAR', Value: app.models.user.get('timezoneCode') },
                        { Name: '@ShiftId', Type: 'INT', Value: (options.shiftId && options.shiftId != -1) ? options.shiftId : null },
                        { Name: '@CrewId', Type: 'VARCHAR', Value: crew },
                        { Name: '@delayCategoryId', Type: 'INT', Value: (options.delayCategoryId && options.delayCategoryId != -1) ? options.delayCategoryId : null },
                        // Pass -1/-1 so the stored procedure returns the whole range (client-side AG Grid).
                        { Name: '@fromRow', Type: 'INT', Value: -1 },
                        { Name: '@toRow', Type: 'INT', Value: -1 }
                    ];
                }

                _.each(this.fixedParameters, function (qpParams) {
                    qp.Add(qpParams.Name, qpParams.Type, qpParams.Value);
                });

                this.isFetching = true;

                Core.Json.CallProcedure(
                    app.DatabaseNames.IHDelays + '.WebApp.GetDelaysCodeGroup',
                    qp,
                    {
                        onSuccess: function (resp) {
                            try {
                                if (resp && resp.Table) {
                                    that.set(resp.Table);
                                    that.trigger('fetch', that, resp.Table);
                                    that.isFetching = false;
                                } else {
                                    if (resp && resp.Message) {
                                        console.error(new Error(resp.Message).stack);
                                        app.views.topMessages.showMessage('CALL TO DATABASE FAILED', { stay: 5000 });
                                    } else {
                                        app.views.topMessages.showMessage('SERVER RESPONSE NOT VALID', { stay: 5000 });
                                        console.error(new Error('SERVER_RESPONSE_NOT_VALID').stack);
                                    }
                                }
                            } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        },
                        onFailure: function (resp) {
                            console.error(resp);
                        },
                        Secured: true,
                        Async: true
                    },
                    app.ConnectionStrings.app
                );
                return this;
            }
        });

        // ----------------------------------------------------------------------------------------
        // DECLARE / EDIT DELAY
        // ----------------------------------------------------------------------------------------

        Screen.Models.DeclareDelay = Backbone.Epoxy.Model.extend({
            defaults: {
                id: null,
                delayId: null,
                parentId: null,
                productionItem: null,
                start: null,
                end: null,
                duration: null,
                durationinMin: null,
                delayCodeId: -1,
                commentsDelay: '',
                batchId: null,
                delayCodes: [],
                fetched: false
            },
            initialize: function () {
                this.fetchDelayCodes();
            },
            // Start/End/Duration/Comments/DelayCode are seeded from the grid row on open.
            // GetDelayData is used here only to load the hierarchical delay code list (Table3).
            fetchDelayCodes: function () {
                var that = this;
                var QP = new Core.Database.QueryParameters();
                QP.Add('DelayID', 'INT', this.get('id'));
                QP.Add('Timezone', 'VARCHAR', app.models.user.get('timezoneCode'));

                Core.Json.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.GetDelayData', QP, {
                    onSuccess: function (data) {
                        if (data && data.Table3) {
                            that.set({
                                delayCodes: _.map(data.Table3, function (m) {
                                    return { value: m.Id, label: m.Hierarchy };
                                }),
                                fetched: true
                            });
                        }
                    },
                    Async: false,
                    Secured: true
                }, app.ConnectionStrings.app);
            }
        });

        Screen.Views.DeclareDelay = Backbone.Epoxy.View.extend({
            template: 'delays-manager',
            id: 'delays-manager-declare',
            title: '',
            className: 'modal hide fade modal-wider',
            bindings: 'data-bind',
            isCacheable: true,
            initialize: function () {
                _.bindAll(this);
                this.options.MYREFERENCES = { rowData: null };
                this.bus = this.options.bus;
                if (this.options.viewParams) {
                    this.options.MYREFERENCES.rowData = this.options.viewParams.rowData;
                }
                if (!this.model) {
                    this.model = new Screen.Models.DeclareDelay();
                }
                this.options._isRendered = false;
                this.bindEvents();
                this.delegateEvents();
            },
            saveDelay: function () {
                var that = this;
                var QP = new Core.Database.QueryParameters();
                var model = this.model;

                var delayCodeId = parseInt(model.get('delayCodeId'));
                delayCodeId = (delayCodeId != -1 && !isNaN(delayCodeId)) ? delayCodeId : null;

                QP.Add('DelayID', 'INT', model.get('delayId') || model.get('id'));
                QP.Add('BatchId', 'INT', model.get('batchId'));
                QP.Add('Comments', 'VARCHAR', model.get('commentsDelay'));
                QP.Add('DelayCodeID', 'INT', delayCodeId);

                app.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.UpdateDelay', QP, {
                    onSuccess: function () {
                        that.bus.trigger('refreshGrid');
                        that.hide();
                        app.views.topMessages.showMessage('Delay Declared', { stay: 5000 });
                    },
                    Secured: true
                }, app.ConnectionStrings.app);
            },
            events: {
                'click .close, .btn-close': 'handleClose',
                'click #delay_declare_btn': 'saveDelay'
            },
            handleClose: function (event) {
                event.preventDefault();
                this.trigger('cancel');
                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            render: function () {
                var that = this;
                var customPath = '/app/pages/eaf/delays-manager/';

                T.render.call(this, this.template, function (tmp) {
                    if (!that.options.i18n) that.options.i18n = {};
                    app.getI18NJed(that, that.template, function (i18nJED) {
                        that.options.i18n[that.template] = i18nJED;

                        that.$el.html(tmp({}));
                        that.applyBindings();
                        that.delegateEvents();

                        that.options._isRendered = true;
                    }, true, customPath);
                }, customPath, "declare_delay");
            },
            show: function () {
                if (!this.options._isRendered) {
                    this.render();
                    setTimeout(this.show, 100);
                    return;
                }

                var self = this, $el = this.$el;
                $el.modal({ keyboard: false, backdrop: 'static' });

                $('.modal-backdrop').one('click', function () {
                    if (self.options.content && self.options.content.trigger) {
                        self.options.content.trigger('cancel', self);
                    }
                    self.trigger('cancel');
                });

                $(document).one('keyup.dismiss.modal', function (e) {
                    e.which == 27 && self.trigger('cancel');
                });

                this.on('cancel', function () { self.hide(); });
                return this;
            },
            hide: function (data) {
                var self = this, $el = this.$el;
                $el.one('hidden', function onHidden(e) {
                    if (e.target !== e.currentTarget) {
                        return $el.one('hidden', onHidden);
                    }
                    self.newDelay = data;
                    if (self.options.content && self.options.content.trigger) {
                        self.options.content.trigger('hidden', self);
                    }
                    self.close();
                });
                $el.modal('hide');
            },
            bindEvents: function () { },
            close: function () {
                this.unbind();
            },
            preRender: function () { },
            reRender: function () { }
        });

        // ----------------------------------------------------------------------------------------
        // SPLIT DELAY
        // ----------------------------------------------------------------------------------------

        Screen.Models.SplitDelay = Backbone.Epoxy.Model.extend({
            defaults: {
                id: null,
                start: null,
                end: null,
                duration: null,
                split1_duration: null,
                split2_duration: null,
                delay1Start: null,
                delay1End: null,
                delay2Start: null,
                delay2End: null
            }
        });

        Screen.Views.SplitDelay = Backbone.Epoxy.View.extend({
            template: 'delays-manager',
            id: 'delays-manager-split',
            title: '',
            className: 'modal hide fade modal-wider',
            bindings: 'data-bind',
            isCacheable: true,
            initialize: function () {
                _.bindAll(this);
                this.options.MYREFERENCES = { rowData: null };
                this.bus = this.options.bus;
                if (this.options.viewParams) {
                    this.options.MYREFERENCES.rowData = this.options.viewParams.rowData;
                }
                if (!this.model) {
                    this.model = new Screen.Models.SplitDelay();
                }
                this.options._isRendered = false;
                this.options.sliders = {};
                this.bindEvents();
                this.delegateEvents();
            },
            splitDelay: function () {
                var that = this;
                var model = this.model;
                var s1duration = model.get('split1_duration');
                var s2duration = model.get('split2_duration');

                if (s1duration > 0 && s2duration > 0) {
                    var QP = new Core.Database.QueryParameters();
                    QP.Add('Id', 'INT', model.get('id'));
                    QP.Add('Seconds', 'INT', s1duration);
                    app.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.SplitDelayAt', QP, {
                        onSuccess: function () {
                            that.bus.trigger('refreshGrid');
                            that.hide();
                            app.views.topMessages.showMessage('Delay Splitted', { stay: 5000 });
                        },
                        Secured: true
                    }, app.ConnectionStrings.app);
                }
            },
            events: {
                'click .close, .btn-close': 'handleClose',
                'click .delay_split_modal_split_btn': 'splitDelay',
                'click .slider-arrow-right': 'slideRight',
                'click .slider-arrow-left': 'slideLeft',
                'change #delay1-end': 'delay1End_changed'
            },
            handleClose: function (event) {
                event.preventDefault();
                this.trigger('cancel');
                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            render: function () {
                var that = this;
                var customPath = '/app/pages/eaf/delays-manager/';

                T.render.call(this, this.template, function (tmp) {
                    if (!that.options.i18n) that.options.i18n = {};
                    app.getI18NJed(that, that.template, function (i18nJED) {
                        that.options.i18n[that.template] = i18nJED;

                        var startDate = new moment(that.model.get('start'));
                        var endDate = new moment(that.model.get('end'));

                        var ctx = {
                            start_date: startDate.format('YYYY-MM-DD'),
                            end_date: endDate.format('YYYY-MM-DD'),
                            start_time: startDate.format('HH:mm:ss'),
                            end_time: endDate.format('HH:mm:ss')
                        };

                        that.$el.html(tmp(ctx));
                        that.applyBindings();
                        that.delegateEvents();

                        setTimeout(function () {
                            that.options.sliders.split = that.$el.find('.slider').slider({
                                formatter: function (value) {
                                    var start = new Date(startDate.toDate().getTime() + (parseInt(value, 10) * 1000));
                                    return 'Split at: ' + new moment(start).format('YYYY-MM-DD HH:mm:ss');
                                },
                                min: 0,
                                max: that.model.get('duration'),
                                step: 1,
                                value: parseInt(that.model.get('duration')) / 2
                            }).data('slider');

                            that.$el.find('.slider').on('change', _.throttle(function () {
                                that.updateModelSplitDurations();
                            }, 100));

                            // NOTE: we intentionally do NOT set native min/max on #delay1-end.
                            // A time input handles below-min values differently from above-max
                            // (the browser can swallow the change event), which made the backward
                            // adjustment fail. The range is enforced entirely by the JS clamp in
                            // delay1End_changed, which snaps out-of-range input to the nearest
                            // valid bound consistently in both directions.

                            that.updateModelSplitDurations();
                        }, 500);

                        that.options._isRendered = true;
                    }, true, customPath);
                }, customPath, "split_delay");
            },
            updateModelSplitDurations: function () {
                var val = this.options.sliders.split.getValue();
                var duration = this.model.get('duration');

                var startDate = new moment(this.model.get('start'));
                var endDate = new moment(this.model.get('end'));
                var delay1End = startDate.clone().add(val, 'seconds');
                var delay2Start = delay1End.clone();

                this.model.set({
                    split1_duration: val,
                    split2_duration: duration - val,
                    delay1Start: startDate.format('HH:mm:ss'),
                    delay1End: delay1End.format('HH:mm:ss'),
                    delay2Start: delay2Start.format('HH:mm:ss'),
                    delay2End: endDate.format('HH:mm:ss')
                });
            },
            // The user can type/pick the exact End time of Delay 1 (HH:mm:ss); the slider and
            // every other value (durations, Delay 2 start) recalculate from it.
            delay1End_changed: function (e) {
                try {
                    var raw = $(e.target).val();
                    if (!raw) return;

                    var t = moment(raw, ['HH:mm:ss', 'HH:mm']);
                    if (!t.isValid()) return;

                    var start = new moment(this.model.get('start'));
                    var end = new moment(this.model.get('end'));

                    // Map the time-of-day onto the delay window (handle a span that crosses midnight).
                    // NOTE: moment 2.10.6 has no isSameOrBefore — use !isAfter instead.
                    var cand = start.clone().hour(t.hour()).minute(t.minute()).second(t.second()).millisecond(0);
                    if (cand.isBefore(start)) {
                        var candNext = cand.clone().add(1, 'day');
                        if (!candNext.isAfter(end)) cand = candNext;
                    }

                    var duration = this.model.get('duration');
                    var seconds = cand.diff(start, 'seconds');
                    // Keep both halves at least 1 second long (snaps out-of-range input to the boundary).
                    seconds = Math.max(1, Math.min(duration - 1, seconds));

                    if (this.options.sliders.split) {
                        // triggerChangeEvent = true -> fires the slider 'change' -> recompute everything.
                        this.options.sliders.split.setValue(seconds, true, true);
                    } else {
                        this.updateModelSplitDurations();
                    }
                } catch (Error) { console.error((Error.stack) ? Error.stack : new Error(Error).stack); }
            },
            slideRight: function () {
                var val = this.options.sliders.split.getValue();
                this.options.sliders.split.setValue(val + 1, true, true);
            },
            slideLeft: function () {
                var val = this.options.sliders.split.getValue();
                this.options.sliders.split.setValue(val - 1, true, true);
            },
            checkSplitsLength: function () {
                var s1duration = this.model.get('split1_duration');
                var s2duration = this.model.get('split2_duration');
                var splitBtn = this.$el.find('.delay_split_modal_split_btn');
                if (s1duration > 0 && s2duration > 0) splitBtn.prop('disabled', false);
                else splitBtn.prop('disabled', true);
            },
            show: function () {
                if (!this.options._isRendered) {
                    this.render();
                    setTimeout(this.show, 100);
                    return;
                }

                var self = this, $el = this.$el;
                $el.modal({ keyboard: false, backdrop: 'static' });

                $('.modal-backdrop').one('click', function () {
                    if (self.options.content && self.options.content.trigger) {
                        self.options.content.trigger('cancel', self);
                    }
                    self.trigger('cancel');
                });

                $(document).one('keyup.dismiss.modal', function (e) {
                    e.which == 27 && self.trigger('cancel');
                });

                this.on('cancel', function () { self.hide(); });
                return this;
            },
            hide: function (data) {
                var self = this, $el = this.$el;
                $el.one('hidden', function onHidden(e) {
                    if (e.target !== e.currentTarget) {
                        return $el.one('hidden', onHidden);
                    }
                    self.newDelay = data;
                    if (self.options.content && self.options.content.trigger) {
                        self.options.content.trigger('hidden', self);
                    }
                    self.close();
                });
                $el.modal('hide');
            },
            bindEvents: function () {
                this.listenTo(this.model, 'change:split1_duration', this.checkSplitsLength);
                this.listenTo(this.model, 'change:split2_duration', this.checkSplitsLength);
            },
            close: function () {
                this.unbind();
            },
            preRender: function () { },
            reRender: function () { }
        });

        return Screen;
    });
