define([
    'app',
    'js/templates-loader',
    'moment',
    'js/d3v4/d3.v4',
    'components/ag-grid/ag-grid-module'
],
    function (app, T, moment, d3, AgGridModule) {
        var Screen = {
            Models: {},
            Views: {},
            Collections: {},
        };

        Screen.Models.Main = Backbone.Epoxy.Model.extend({
            defaults: {
                productionDate: new moment().subtract(1, 'days').format('MM/DD/YYYY'),
                shiftId: 'D',
                crewId: 'C',
                shifts: [],
                hasData: true,
                isLoading: false,
                isPageLoading: true,
                renderCompleted: false,
                tphValues: null,

                showCaster1Data: true,
                showCaster2Data: true,
                showCaster1UncutTails: false,
                showDelaysCaster1Data: true,
                showDelaysCaster2Data: true,
            },
            initialize: function () {
                this.itemsWarehouseCosts = new Screen.Collections.ItemsCollection();
                this.itemsProductsCast = new Screen.Collections.ItemsCollection();
                this.itemsShiftResume = new Screen.Collections.ItemsCollection();
                this.itemsLogBook = new Screen.Collections.ItemsCollection();
                this.itemsShiftResumeCaster1 = new Screen.Collections.ItemsCollection();
                this.itemsMeltOrdersCaster1 = new Screen.Collections.ItemsCollection();
                this.itemsShiftResumeCaster2 = new Screen.Collections.ItemsCollection();
                this.itemsMeltOrdersCaster2 = new Screen.Collections.ItemsCollection();
                this.itemsUncutTails = new Screen.Collections.ItemsCollection();

                this.itemsAllHeats = new Screen.Collections.ItemsAllHeats();
                this.itemsAllDelays = new Screen.Collections.ItemsAllDelays();
                this.srdCategories = new Screen.Collections.SRDCategories();
                this.topDelaysCaster1 = new Screen.Collections.ItemsDelays();
                this.topDelaysCaster2 = new Screen.Collections.ItemsDelays();
            },

            computeds: {
                currentShift: {
                    deps: ['shiftId', 'productionDate', 'crewId'],
                    get: function (shiftId, productionDate, crewId) {
                        var shift = shiftId === 'D' ? 'Day Shift' : 'Night Shift';
                        var crew = crewId + ' Crew';
                        return productionDate + ' | ' + shift + ' | ' + crew;
                    },
                },
            },

            fetchData: function (params) {
                var that = this,
                    qp = new Core.Database.QueryParameters();

                qp.Add('@ProductionDate', 'DATE', params.productionDate);
                qp.Add('@ShiftId', 'INT', params.shiftId == '1' ? 1 : (params.shiftId == '2' ? 2 : null));
                qp.Add('@timeZoneCode', 'CHAR(10)', app.models.user.get('timezoneCode'));

                Core.Json.CallProcedure(
                    app.DatabaseNames.MES + '.CAS.GetShiftReport',
                    qp,
                    {
                        onSuccess: function (resp) {
                            try {
                                if (resp) {
                                    if (resp.Table15 && resp.Table15[0]) {
                                        that.set({
                                            showCaster1Data: resp.Table15[0].hasCaster1Data === 1 ? true : false,
                                            showCaster2Data: resp.Table15[0].hasCaster2Data === 1 ? true : false,
                                        });
                                    }

                                    if (resp.Table2) that.itemsWarehouseCosts.setDataColl(resp.Table2);
                                    if (resp.Table8) that.itemsProductsCast.setDataColl(resp.Table8);
                                    if (resp.Table9) that.itemsShiftResume.setDataColl(resp.Table9);
                                    if (resp.Table10) that.itemsLogBook.setDataColl(resp.Table10);
                                    if (resp.Table11) that.itemsShiftResumeCaster1.setDataColl(resp.Table11);
                                    if (resp.Table12) that.itemsMeltOrdersCaster1.setDataColl(resp.Table12);
                                    if (resp.Table13) that.itemsShiftResumeCaster2.setDataColl(resp.Table13);
                                    if (resp.Table14) that.itemsMeltOrdersCaster2.setDataColl(resp.Table14);
                                    if (resp.Table17) that.itemsAllDelays.setDataColl(resp.Table17, params.shiftId, params.productionDate);
                                    if (resp.Table18) that.srdCategories.setDataColl(resp.Table18);
                                    if (resp.Table19) {
                                        that.set({
                                            showDelaysCaster1Data: resp.Table19.some(function (x) { return x.CasterCode == 1; }),
                                            showDelaysCaster2Data: resp.Table19.some(function (x) { return x.CasterCode == 2; }),
                                        });
                                        that.topDelaysCaster1.setDataColl(resp.Table19.filter(function (x) { return x.CasterCode == 1; }));
                                        that.topDelaysCaster2.setDataColl(resp.Table19.filter(function (x) { return x.CasterCode == 2; }));
                                    }

                                    if (resp.Table16) {
                                        that.set({
                                            showCaster1UncutTails: resp.Table16.length > 0 ? true : false,
                                        });
                                        that.itemsUncutTails.setDataColl(resp.Table16);
                                    }

                                    var strands = null;
                                    var strandsCaster2 = null;

                                    that.set({
                                        tphValues: resp.Table4 && resp.Table4[0] ? resp.Table4[0] : {},
                                        crewId: resp.Table && resp.Table.CrewId ? resp.Table.CrewId : '-',
                                    });

                                    if (resp.Table6) strands = resp.Table6;
                                    if (resp.Table7) strandsCaster2 = resp.Table7;

                                    if (resp.Table) that.itemsAllHeats.setDataColl(resp.Table, params.shiftId, params.productionDate, strands, strandsCaster2);
                                    if (resp.Table17) that.itemsAllDelays.setDataColl(resp.Table17, params.shiftId, params.productionDate);

                                    that.trigger('fetch', resp);
                                }
                                else {
                                    if ((resp) && (resp.Message))
                                        console.error(new Error(resp.Message).stack);
                                    else
                                        console.error(new Error('SERVER_RESPONSE_NOT_VALID').stack);

                                    that.set({
                                        isLoading: false,
                                        hasData: false
                                    });
                                }
                            }
                            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;
            },

            fetchPickers: function (params, options) {
                var that = this,
                    qp = new Core.Database.QueryParameters(),
                    opt = _.extend({ async: true, }, options);

                Core.Json.CallProcedure(
                    app.DatabaseNames.MES + '.EAF.GetShiftReportPickers',
                    qp,
                    {
                        onSuccess: function (resp) {
                            try {
                                if ((resp) && (resp.Table)) {
                                    _.each(resp.Table, function (obj) {
                                        var tableKey = 'Table' + obj.Index;
                                        switch (obj.Name) {
                                            case 'SHIFTS':
                                                that.set('shifts', _.map(resp[tableKey], function (obj) {
                                                    return {
                                                        value: obj.Shift,
                                                        label: obj.Shift,
                                                    };
                                                }));
                                                break;
                                        }
                                    });
                                }
                                else {
                                    if ((resp) && (resp.Message))
                                        console.error(new Error(resp.Message).stack);
                                    else
                                        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: opt.async,
                    },
                    app.ConnectionStrings.app
                );

                return this;
            },
        });

        Screen.Collections.ItemsCollection = Backbone.Collection.extend({
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = data;
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.generateID = function (viewParams) {
            try {
                return 'casters-shift-report';
            } catch (Error) { }
        };

        Screen.Views.Main = Backbone.Epoxy.View.extend({
            template: 'casters-shift-report'
            , id: 'casters-shift-report'
            , title: 'Casters Shift Report'
            , isCacheable: false
            , events: function () {
                return {
                    'click #refreshBtn': this.refreshBtn_click,
                    'click #forwardNavBtn': this.forwardNavBtn_click,
                    'click #backNavBtn': this.backNavBtn_click,
                    'click #downloadPdfBtn': this.downloadPdfBtn_click,
                };
            }
            , bindings: 'data-bind'
            , viewParams: null
            , initialize: function () {
                this.options.state = app.view_states.loading;
                this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };
                this.model = new Screen.Models.Main();
                this.bindEvents();
            }

            , forwardNavBtn_click: function () { this.UpdateNav(true); }
            , backNavBtn_click: function () { this.UpdateNav(false); }

            , refreshBtn_click: function (model) {
                this.UpdateURL();
                this.refresh();
            }

            , downloadPdfBtn_click: function () {
                try {
                    var params = this.model.toJSON();
                    var shift = '1';
                    if (params.shiftId == 'D') shift = '1';
                    else if (params.shiftId == 'N') shift = '2';

                    var btn = this.$('#downloadPdfBtn');
                    var btnIcon = btn.find('svg');
                    var btnText = btn.find('span');
                    var originalText = btnText.text();

                    // Show loading state
                    btn.prop('disabled', true);
                    btnIcon.hide();
                    btnText.html('<svg class="sr-spin" style="display:inline-block;vertical-align:middle;margin-right:4px;" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle style="opacity:0.25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"/><path style="opacity:0.75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/></svg> Generating...');

                    var serviceUrl = (app.config && app.config.shiftReportPdfServiceUrl)
                        ? app.config.shiftReportPdfServiceUrl
                        : 'http://localhost:5050';

                    fetch(serviceUrl + '/api/shift-report/generate', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                            productionDate: new moment(params.productionDate, 'MM/DD/YYYY').format('YYYY-MM-DD'),
                            shiftId: parseInt(shift),
                            reportType: 'CAS'
                        })
                    })
                    .then(function (res) {
                        if (!res.ok) throw new Error('PDF generation failed: ' + res.statusText);
                        return res.blob();
                    })
                    .then(function (blob) {
                        var url = window.URL.createObjectURL(blob);
                        window.open(url, '_blank');
                        setTimeout(function () {
                            window.URL.revokeObjectURL(url);
                        }, 60000);
                    })
                    .catch(function (err) {
                        console.error('PDF download error:', err);
                        alert('Failed to download PDF. Please ensure the PDF service is running.');
                    })
                    .finally(function () {
                        // Restore button
                        btn.prop('disabled', false);
                        btnIcon.show();
                        btnText.text(originalText);
                    });
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }

            , bindEvents: function () {
                this.listenTo(this.model, 'fetch', this.fetched);
                this.listenTo(this.model, 'caster2_READY', this.caster1_chart);
                this.listenTo(this.model.itemsAllHeats, 'fetch', this.itemsAllHeats_ready);
            }

            , itemsAllHeats_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({
                            hasData_allHeats: (that.model.itemsAllHeats.length > 0),
                            isLoading_allHeats: false,
                        });
                        that.caster2_chart();
                        that.referencesChart();
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }

            , caster1_chart: function () {
                var that = this;
                setTimeout(function () {
                    that.getChart(true, 1400);
                    that.model.set({ isLoading_caster1: false });
                }, 100);
            }
            , caster2_chart: function () {
                var that = this;
                setTimeout(function () {
                    that.getChart(false, 1400);
                    that.model.trigger('caster2_READY', that);
                }, 100);
            }

            // GANTT chart — modern HTML/CSS timeline (same pattern as EAF)
            , getChart: function (caster1, width) {
                var heats = [], delays = [];
                var chart_class;
                if (caster1) {
                    heats = this.model.itemsAllHeats.getHeats_caster1();
                    delays = this.model.itemsAllDelays.getDelay_caster1();
                    chart_class = ".caster1-gantt-screen";
                } else {
                    heats = this.model.itemsAllHeats.getHeats_caster2();
                    delays = this.model.itemsAllDelays.getDelay_caster2();
                    chart_class = ".caster2-gantt-screen";
                }

                var $container = this.$el.find(chart_class);
                $container.empty();

                if (heats.length === 0 && delays.length === 0) return;

                var params = this.model.toJSON();
                var shiftId = params.shiftId;
                var divisions = 12;

                // Build time labels
                var timeLabels = [];
                if (shiftId === 'D') {
                    timeLabels = ['9AM','10AM','11AM','12PM','1PM','2PM','3PM','4PM','5PM','6PM','7PM','8PM','9PM'];
                } else {
                    timeLabels = ['9PM','10PM','11PM','12AM','1AM','2AM','3AM','4AM','5AM','6AM','7AM','8AM','9AM'];
                }

                // Helper: calculate left % and width % for a data item
                function calcPosition(d) {
                    var start = parseFloat(d.startHour);
                    var offset;
                    if (d.shift === 'D') { offset = start - 9; }
                    else {
                        offset = (d.startHour >= 21) ? start - 21 : start + 3;
                    }
                    var leftPct = (offset / divisions) * 100;
                    var widthPct = (d.diffHours > 0) ? (d.diffHours / divisions) * 100 : 0;
                    return { left: leftPct, width: widthPct };
                }

                // Build tooltip text
                function tooltipText(d) {
                    var startT = moment(d.start).format('HH:mm');
                    var endT = moment(d.end).format('HH:mm');
                    if (d.heatName) {
                        return 'Heat: ' + d.heatName + '\nStart: ' + startT + ' | End: ' + endT + '\nDuration: ' + d.duration;
                    }
                    return (d.causeName || d.name || 'Delay') + '\nStart: ' + startT + ' | End: ' + endT + '\nDuration: ' + d.duration;
                }

                // Time axis
                var axisHtml = '<div class="sr-gantt-axis">';
                for (var i = 0; i < timeLabels.length; i++) {
                    var pos = (i / divisions) * 100;
                    axisHtml += '<span class="sr-gantt-tick" style="left:' + pos + '%">' + timeLabels[i] + '</span>';
                }
                axisHtml += '</div>';

                // Grid lines
                var gridHtml = '';
                for (var i = 0; i <= divisions; i++) {
                    var pos = (i / divisions) * 100;
                    gridHtml += '<div class="sr-gantt-gridline" style="left:' + pos + '%"></div>';
                }

                // Heats lane
                var heatsHtml = '<div class="sr-gantt-lane"><span class="sr-gantt-lane-label">Heats</span><div class="sr-gantt-lane-track">' + gridHtml;
                _.each(heats, function (d) {
                    var pos = calcPosition(d);
                    var tip = tooltipText(d);
                    heatsHtml += '<div class="sr-gantt-bar sr-gantt-bar--heat" style="left:' + pos.left + '%;width:' + pos.width + '%;background-color:' + d.color + ';" title="' + tip.replace(/"/g, '&quot;') + '">';
                    heatsHtml += '<span class="sr-gantt-bar-label">' + (d.name || '') + '</span>';
                    heatsHtml += '</div>';
                });
                heatsHtml += '</div></div>';

                // Delays lane
                var delaysHtml = '<div class="sr-gantt-lane"><span class="sr-gantt-lane-label">Delays</span><div class="sr-gantt-lane-track">' + gridHtml;
                _.each(delays, function (d) {
                    var pos = calcPosition(d);
                    var tip = tooltipText(d);
                    delaysHtml += '<div class="sr-gantt-bar sr-gantt-bar--delay" style="left:' + pos.left + '%;width:' + pos.width + '%;background-color:' + d.color + ';" title="' + tip.replace(/"/g, '&quot;') + '">';
                    delaysHtml += '<span class="sr-gantt-bar-label">' + (d.name || '') + '</span>';
                    delaysHtml += '</div>';
                });
                delaysHtml += '</div></div>';

                $container.html(
                    '<div class="sr-gantt-timeline">' +
                        axisHtml +
                        heatsHtml +
                        delaysHtml +
                    '</div>'
                );
            }

            // GANTT legend — HTML badges (same pattern as EAF)
            , referencesChart: function () {
                try {
                    // Caster 1 legend
                    var c1Categories = this.model.srdCategories.filter(function (x) { return x.get('casterCode') == 1; });
                    var uniqueC1 = [];
                    var seenC1 = {};
                    c1Categories.forEach(function (cat) {
                        var desc = cat.get('description');
                        if (!seenC1[desc]) {
                            seenC1[desc] = true;
                            uniqueC1.push(cat);
                        }
                    });
                    var $legendC1 = this.$el.find('#gantt-legend-categories-c1');
                    $legendC1.find('.dynamic-badge').remove();
                    uniqueC1.forEach(function (cat) {
                        $legendC1.append('<div class="flex items-center gap-1 dynamic-badge"><span class="inline-block w-5 h-5 rounded" style="background:' + cat.get('color') + '; border:1px solid #505050;"></span><span class="text-sm text-gray-600">' + cat.get('description') + '</span></div>');
                    });

                    // Caster 2 legend
                    var c2Categories = this.model.srdCategories.filter(function (x) { return x.get('casterCode') == 2; });
                    var uniqueC2 = [];
                    var seenC2 = {};
                    c2Categories.forEach(function (cat) {
                        var desc = cat.get('description');
                        if (!seenC2[desc]) {
                            seenC2[desc] = true;
                            uniqueC2.push(cat);
                        }
                    });
                    var $legendC2 = this.$el.find('#gantt-legend-categories-c2');
                    $legendC2.find('.dynamic-badge').remove();
                    uniqueC2.forEach(function (cat) {
                        $legendC2.append('<div class="flex items-center gap-1 dynamic-badge"><span class="inline-block w-5 h-5 rounded" style="background:' + cat.get('color') + '; border:1px solid #505050;"></span><span class="text-sm text-gray-600">' + cat.get('description') + '</span></div>');
                    });
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }

            , fetched: function (resp) {
                var model = this.model.toJSON();
                this.model.set({
                    isLoading: false,
                    renderCompleted: true,
                    hasData:
                        (resp && resp.Table && resp.Table.length > 0) ||
                        (resp && resp.Table9 && resp.Table9.length > 0) ||
                        (resp && resp.Table2 && resp.Table2.length > 0) ? true : false,
                });
            }

            // --- Collection ready handlers ---
            , itemsShiftResume_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.shiftResumeGrid) that.shiftResumeGrid.setRowDataAsCollection(that.model.itemsShiftResume);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsWarehouseCosts_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.warehouseCostsGrid) that.warehouseCostsGrid.setRowDataAsCollection(that.model.itemsWarehouseCosts);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsProductsCast_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.productsCastGrid) that.productsCastGrid.setRowDataAsCollection(that.model.itemsProductsCast);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsShiftResumeCaster1_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.caster1ResumeGrid) that.caster1ResumeGrid.setRowDataAsCollection(that.model.itemsShiftResumeCaster1);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsShiftResumeCaster2_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.caster2ResumeGrid) that.caster2ResumeGrid.setRowDataAsCollection(that.model.itemsShiftResumeCaster2);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsDelaysCaster1_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.delaysCaster1Grid) that.delaysCaster1Grid.setRowDataAsCollection(that.model.topDelaysCaster1);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsDelaysCaster2_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.delaysCaster2Grid) that.delaysCaster2Grid.setRowDataAsCollection(that.model.topDelaysCaster2);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsMeltOrdersCaster1_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.meltOrdersCaster1Grid) that.meltOrdersCaster1Grid.setRowDataAsCollection(that.model.itemsMeltOrdersCaster1);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsMeltOrdersCaster2_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.meltOrdersCaster2Grid) that.meltOrdersCaster2Grid.setRowDataAsCollection(that.model.itemsMeltOrdersCaster2);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsUncutTails_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.uncutTailsCaster1Grid) that.uncutTailsCaster1Grid.setRowDataAsCollection(that.model.itemsUncutTails);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }
            , itemsLogBook_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.logBookGrid) that.logBookGrid.setRowDataAsCollection(that.model.itemsLogBook);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }

            , render: function (container, viewParams) {
                var that = this;
                var thatContainer = (this.options.container) ? this.options.container : container;
                this.options.container = thatContainer;
                var customPath = '/app/pages/caster/' + 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.append(thatContainer, that.$el);

                                var defaultGridOptions = {
                                    defaultColDef: { sortable: false, filter: false, resizable: true, suppressMenu: true },
                                    domLayout: 'autoHeight',
                                };

                                // Shift Resume Grid
                                that.shiftResumeGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'TotalTons', headerName: 'Total Tons', flex: 1 },
                                        { field: 'TotalHeats', headerName: 'Total Heats', flex: 1 },
                                        { field: 'DelaysCaster1', headerName: 'Delays C1', flex: 1 },
                                        { field: 'RobotCaster1', headerName: 'Robot C1', flex: 1 },
                                        { field: 'DelaysCaster2', headerName: 'Delays C2', flex: 1 },
                                        { field: 'RobotCaster2', headerName: 'Robot C2', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.shiftResumeGrid.render();
                                that.$el.find('.shift-resume-grid-container').append(that.shiftResumeGrid.$el);

                                // Warehouse Costs Grid
                                that.warehouseCostsGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'Department', headerName: 'Department', flex: 1 },
                                        { field: 'MonthIssues', headerName: 'Issues', flex: 1 },
                                        { field: 'MonthReturns', headerName: 'Returns', flex: 1 },
                                        { field: 'Total', headerName: 'Total', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.warehouseCostsGrid.render();
                                that.$el.find('.warehouse-costs-grid-container').append(that.warehouseCostsGrid.$el);

                                // Products Cast Grid
                                that.productsCastGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'Caster1', headerName: 'Caster 1', flex: 1 },
                                        { field: 'Caster2', headerName: 'Caster 2', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.productsCastGrid.render();
                                that.$el.find('.products-cast-grid-container').append(that.productsCastGrid.$el);

                                // Caster 1 Resume Grid
                                that.caster1ResumeGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'PrimeTons', headerName: 'Prime Tons', flex: 1 },
                                        { field: 'ScrapTons', headerName: 'Scrap Tons', flex: 1 },
                                        { field: 'DelayTotal', headerName: 'Delays Total', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.caster1ResumeGrid.render();
                                that.$el.find('.caster1-resume-grid-container').append(that.caster1ResumeGrid.$el);

                                // Delays Caster 1 Grid
                                that.delaysCaster1Grid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'CasterName', headerName: 'Caster', flex: 1 },
                                        { field: 'CauseName', headerName: 'Cause', flex: 2 },
                                        { field: 'AreaName', headerName: 'Area', flex: 1 },
                                        { field: 'Duration', headerName: 'Min', fieldType: 'Integer', flex: 1 },
                                        { field: 'Start', headerName: 'Start', fieldType: 'Datetime', format: 'HH:mm', flex: 1 },
                                        { field: 'End', headerName: 'End', fieldType: 'Datetime', format: 'HH:mm', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.delaysCaster1Grid.render();
                                that.$el.find('.delays-caster1-grid-container').append(that.delaysCaster1Grid.$el);

                                // Melt Orders Caster 1 Grid
                                that.meltOrdersCaster1Grid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'MeltOrder', headerName: 'MO', flex: 1 },
                                        { field: 'CasterSequence', headerName: 'Caster Seq', flex: 1 },
                                        { field: 'HeatName', headerName: 'Heat', flex: 1 },
                                        { field: 'Pieces', headerName: 'Pieces', flex: 1 },
                                        { field: 'OpenTime', headerName: 'Open', flex: 1 },
                                        { field: 'CloseTime', headerName: 'Close', flex: 1 },
                                        { field: 'CastMin', headerName: 'Cast Min', flex: 1 },
                                        { field: 'HeatInSequence', headerName: 'Heat in Seq', flex: 1 },
                                        { field: 'Product', headerName: 'Prod', flex: 1 },
                                        { field: 'FreeOpen', headerName: 'Free Open', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.meltOrdersCaster1Grid.render();
                                that.$el.find('.melt-orders-caster1-grid-container').append(that.meltOrdersCaster1Grid.$el);

                                // Uncut Tails Caster 1 Grid
                                that.uncutTailsCaster1Grid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'ProductNumber', headerName: 'Product Number', flex: 1 },
                                        { field: 'CutComplete', headerName: 'Cut Time', fieldType: 'Datetime', format: 'HH:mm', flex: 1 },
                                        { field: 'Weight', headerName: 'Weight', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'Length', headerName: 'Length', fieldType: 'Float', decimals: 1, flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.uncutTailsCaster1Grid.render();
                                that.$el.find('.uncut-tails-caster1-grid-container').append(that.uncutTailsCaster1Grid.$el);

                                // Caster 2 Resume Grid
                                that.caster2ResumeGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'PrimeTons', headerName: 'Prime Tons', flex: 1 },
                                        { field: 'ScrapTons', headerName: 'Scrap Tons', flex: 1 },
                                        { field: 'DelayTotal', headerName: 'Delays Total', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.caster2ResumeGrid.render();
                                that.$el.find('.caster2-resume-grid-container').append(that.caster2ResumeGrid.$el);

                                // Delays Caster 2 Grid
                                that.delaysCaster2Grid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'CasterName', headerName: 'Caster', flex: 1 },
                                        { field: 'CauseName', headerName: 'Cause', flex: 2 },
                                        { field: 'AreaName', headerName: 'Area', flex: 1 },
                                        { field: 'Duration', headerName: 'Min', fieldType: 'Integer', flex: 1 },
                                        { field: 'Start', headerName: 'Start', fieldType: 'Datetime', format: 'HH:mm', flex: 1 },
                                        { field: 'End', headerName: 'End', fieldType: 'Datetime', format: 'HH:mm', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.delaysCaster2Grid.render();
                                that.$el.find('.delays-caster2-grid-container').append(that.delaysCaster2Grid.$el);

                                // Melt Orders Caster 2 Grid
                                that.meltOrdersCaster2Grid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'MeltOrder', headerName: 'MO', flex: 1 },
                                        { field: 'CasterSequence', headerName: 'Caster Seq', flex: 1 },
                                        { field: 'HeatName', headerName: 'Heat', flex: 1 },
                                        { field: 'Pieces', headerName: 'Pieces', flex: 1 },
                                        { field: 'OpenTime', headerName: 'Open', flex: 1 },
                                        { field: 'CloseTime', headerName: 'Close', flex: 1 },
                                        { field: 'CastMin', headerName: 'Cast Min', flex: 1 },
                                        { field: 'HeatInSequence', headerName: 'Heat in Seq', flex: 1 },
                                        { field: 'Product', headerName: 'Prod', flex: 1 },
                                        { field: 'FreeOpen', headerName: 'Free Open', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.meltOrdersCaster2Grid.render();
                                that.$el.find('.melt-orders-caster2-grid-container').append(that.meltOrdersCaster2Grid.$el);

                                // Log Book Grid
                                that.logBookGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'Timestamp', headerName: 'Timestamp', fieldType: 'Datetime', format: 'MM/DD/YY HH:mm', flex: 1 },
                                        { field: 'Type', headerName: 'Type', flex: 1 },
                                        { field: 'Equipment', headerName: 'Equipment', flex: 1 },
                                        { field: 'Comments', headerName: 'Comments', flex: 3 },
                                    ],
                                    pagination: false,
                                    gridOptions: defaultGridOptions,
                                });
                                that.logBookGrid.render();
                                that.$el.find('.logbook-grid-container').append(that.logBookGrid.$el);

                                // Bind collection events to grid ready handlers
                                that.listenTo(that.model.itemsShiftResume, 'fetch', that.itemsShiftResume_ready);
                                that.listenTo(that.model.itemsWarehouseCosts, 'fetch', that.itemsWarehouseCosts_ready);
                                that.listenTo(that.model.itemsProductsCast, 'fetch', that.itemsProductsCast_ready);
                                that.listenTo(that.model.itemsShiftResumeCaster1, 'fetch', that.itemsShiftResumeCaster1_ready);
                                that.listenTo(that.model.itemsShiftResumeCaster2, 'fetch', that.itemsShiftResumeCaster2_ready);
                                that.listenTo(that.model.topDelaysCaster1, 'fetch', that.itemsDelaysCaster1_ready);
                                that.listenTo(that.model.topDelaysCaster2, 'fetch', that.itemsDelaysCaster2_ready);
                                that.listenTo(that.model.itemsMeltOrdersCaster1, 'fetch', that.itemsMeltOrdersCaster1_ready);
                                that.listenTo(that.model.itemsMeltOrdersCaster2, 'fetch', that.itemsMeltOrdersCaster2_ready);
                                that.listenTo(that.model.itemsUncutTails, 'fetch', that.itemsUncutTails_ready);
                                that.listenTo(that.model.itemsLogBook, 'fetch', that.itemsLogBook_ready);

                                that.applyBindings();

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

                                // Append view to main container
                                that.append(thatContainer, that.$el);

                                // Load pickers synchronously
                                that.model.fetchPickers({ async: false });

                                var fixedParams = _.extend({}, viewParams);
                                if (fixedParams.productionDate)
                                    fixedParams.productionDate = new moment(fixedParams.productionDate, 'YYYYMMDD').format('MM/DD/YYYY');
                                if (fixedParams.shiftId)
                                    fixedParams.shiftId = fixedParams.shiftId;
                                that.model.set(fixedParams);

                                // Set loading state to false when everything is ready
                                that.model.set({ isPageLoading: false });

                                that.refresh();
                                that.UpdateURL();
                            },
                            true,
                            customPath
                        );
                    },
                    customPath
                );
            }

            , refresh: function () {
                try {
                    // Clear chart containers
                    this.$el.find('.caster1-gantt-screen').html('');
                    this.$el.find('.caster2-gantt-screen').html('');

                    var params = this.model.toJSON();
                    var shift = '1';
                    if (params.shiftId == 'D') shift = '1';
                    else if (params.shiftId == 'N') shift = '2';

                    this.model.set({
                        isLoading: true,
                        hasData: false,
                    });
                    this.model.fetchData({
                        productionDate: params.productionDate,
                        shiftId: shift,
                    });
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }

            , UpdateNav: function (nav) {
                var params = this.model.toJSON();
                if (nav && this.model.get('shiftId') == 'N')
                    this.model.set('productionDate', new moment(params.productionDate, 'MM/DD/YYYY').add(1, 'day').format('MM/DD/YYYY'));
                if (!nav && this.model.get('shiftId') == 'D')
                    this.model.set('productionDate', new moment(params.productionDate, 'MM/DD/YYYY').subtract(1, 'day').format('MM/DD/YYYY'));
                if (this.model.get('shiftId') == 'N') this.model.set('shiftId', 'D');
                else if (this.model.get('shiftId') == 'D') this.model.set('shiftId', 'N');
                this.UpdateURL();
                this.refresh();
            }

            , UpdateURL: function () {
                try {
                    var params = this.model.toJSON();
                    app.router.navigate(
                        app.router.resolveURL(
                            app.router.currentModule,
                            _.extend({}, params, {
                                productionDate: new moment(params.productionDate, "MM/DD/YYYY").format("YYYYMMDD"),
                            }),
                            false
                        ),
                        { trigger: false }
                    );
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }

            // 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; }
            }
            , bindViewScopedEvents: function () { }
            , unbindViewScopedEvents: function () { }
            , close: function () {
                this.options.state = app.view_states.closed;
                this.remove();
                this.unbindViewScopedEvents();
                this.unbind();
            }
            , show: function () {
                this.options.state = app.view_states.shown;
                this.bindEvents();
                this.$el.show();
            }
            , hide: function () {
                this.options.state = app.view_states.hidden;
                this.$el.hide();
                this.unbind();
                this.stopListening();
            }
            , preRender: function () {
                app.models.subnavbar.set('subnavbar', false);
            }
            , reRender: function (viewParams) {
                try { this.refresh(); } catch (Error) { }
            }
        });

        // ============================================
        // Collections for Gantt chart data
        // ============================================

        Screen.Models.ItemAllHeat = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    CasterNumber: obj.CasterNumber,
                    HeatName: obj.HeatName,
                    Start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"),
                    End: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"),
                    PowerOn: obj.PowerOn,
                };
                return result;
            },
        });

        Screen.Collections.ItemsAllHeats = Backbone.Collection.extend({
            model: Screen.Models.ItemAllHeat,
            caster1_heats: [],
            caster2_heats: [],
            strandSpeeds: [],
            strandSpeedsCaster2: [],
            setDataColl: function (data, shiftId, productionDate, strands, strandsCaster2) {
                var newColl, that = this;
                that.setDataChart(data, shiftId, productionDate, strands, strandsCaster2);
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
            setDataChart: function (heats, shiftId, prodDate, strands, strandsCaster2) {
                var caster1_heats = [], caster2_heats = [];
                var aux_start = null, aux_end = null, shift = 'D';
                var START_D = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_D = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    START_N = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_N = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').add(1, 'd').format('YYYY-MM-DD HH:mm:ss');

                if (shiftId == '1') shift = 'D';
                else if (shiftId == '2') shift = 'N';

                _.each(heats, function (obj) {
                    var start = new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss");
                    var end = new moment(obj.End).format("YYYY-MM-DD HH:mm:ss");
                    var push = false;
                    if (shift == 'D') {
                        if (moment(START_D).isBefore(end) && moment(start).isBefore(END_D)) {
                            push = true;
                            aux_start = moment(start).isBefore(START_D) ? START_D : start;
                            aux_end = moment(END_D).isBefore(end) ? END_D : end;
                        }
                    } else if (shift == 'N') {
                        if (moment(START_N).isBefore(end) && moment(start).isBefore(END_N)) {
                            push = true;
                            aux_start = moment(start).isBefore(START_N) ? START_N : start;
                            aux_end = moment(END_N).isBefore(end) ? END_N : end;
                        }
                    }

                    if (push) {
                        var aux_start_diff = moment(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var aux_end_diff = moment(new moment(aux_end, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var diffHours = aux_end_diff.diff(aux_start_diff, 'hours', true).toFixed(2);
                        var m = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('mm')) / 60;
                        var s = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('ss')) / 3600;
                        var aux = parseFloat(parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('HH')) + m + s).toFixed(2);
                        var aux_color = '#3ebf3c';
                        if (parseFloat(obj.Duration) > 40 && parseFloat(obj.Duration) <= 50) aux_color = '#f3f317';
                        else if (parseFloat(obj.Duration) > 50) aux_color = '#ff5900';

                        var heatData = {
                            type: 'heat',
                            name: obj.HeatName.substr(obj.HeatName.length - 3),
                            diffHours: diffHours,
                            duration: obj.Duration + ' Min',
                            color: aux_color,
                            start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"),
                            startHour: aux,
                            end: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"),
                            shift: shift,
                            heatName: obj.HeatName,
                        };

                        if (obj.CasterNumber == 'Caster 1') {
                            caster1_heats.push(heatData);
                        } else if (obj.CasterNumber == 'Caster 2') {
                            caster2_heats.push(heatData);
                        }
                    }
                });
                this.caster1_heats = caster1_heats;
                this.caster2_heats = caster2_heats;
                this.strandSpeeds = strands;
                this.strandSpeedsCaster2 = strandsCaster2;
            },
            getHeats_caster1: function () { return this.caster1_heats; },
            getHeats_caster2: function () { return this.caster2_heats; },
            getStrandSpeeds: function () { return this.strandSpeeds; },
            getCaster2StrandSpeeds: function () { return this.strandSpeedsCaster2; },
        });

        Screen.Models.ItemAllDelay = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    CasterName: obj.CasterName,
                    Code: obj.Code,
                    Start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"),
                    End: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"),
                    Duration: obj.Duration,
                    Color: obj.Color,
                };
                return result;
            },
        });

        Screen.Collections.ItemsAllDelays = Backbone.Collection.extend({
            model: Screen.Models.ItemAllDelay,
            caster1_delays: [],
            caster2_delays: [],
            setDataColl: function (data, shiftId, productionDate) {
                var newColl, that = this;
                that.setDataChart(data, shiftId, productionDate);
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
            setDataChart: function (delays, shiftId, prodDate) {
                var caster1_delays = [], caster2_delays = [];
                var aux_start = null, aux_end = null, shift = 'D';
                var START_D = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_D = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    START_N = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_N = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').add(1, 'd').format('YYYY-MM-DD HH:mm:ss');

                if (shiftId == '1') shift = 'D';
                else if (shiftId == '2') shift = 'N';

                _.each(delays, function (obj) {
                    var start = new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss");
                    var end = new moment(obj.End).format("YYYY-MM-DD HH:mm:ss");
                    var push = false;
                    if (shift == 'D') {
                        if (moment(START_D).isBefore(end) && moment(start).isBefore(END_D)) {
                            push = true;
                            aux_start = moment(start).isBefore(START_D) ? START_D : start;
                            aux_end = moment(END_D).isBefore(end) ? END_D : end;
                        }
                    } else if (shift == 'N') {
                        if (moment(START_N).isBefore(end) && moment(start).isBefore(END_N)) {
                            push = true;
                            aux_start = moment(start).isBefore(START_N) ? START_N : start;
                            aux_end = moment(END_N).isBefore(end) ? END_N : end;
                        }
                    }

                    if (push) {
                        var aux_start_diff = moment(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var aux_end_diff = moment(new moment(aux_end, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var diffHours = aux_end_diff.diff(aux_start_diff, 'hours', true).toFixed(2);
                        var m = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('mm')) / 60;
                        var s = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('ss')) / 3600;
                        var aux = parseFloat(parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('HH')) + m + s).toFixed(2);
                        var delayData = {
                            type: 'delay',
                            name: obj.Duration + ' Min',
                            diffHours: diffHours,
                            duration: obj.Duration + ' Min',
                            color: obj.Color,
                            start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"),
                            startHour: aux,
                            end: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"),
                            shift: shift,
                            causeName: obj.CauseName,
                            delayId: obj.Id,
                        };
                        if (obj.CasterName == 'Caster 1') {
                            caster1_delays.push(delayData);
                        } else if (obj.CasterName == 'Caster 2') {
                            caster2_delays.push(delayData);
                        }
                    }
                });
                this.caster1_delays = caster1_delays;
                this.caster2_delays = caster2_delays;
            },
            getDelay_caster1: function () { return this.caster1_delays; },
            getDelay_caster2: function () { return this.caster2_delays; },
        });

        Screen.Models.SRDCategory = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    id: obj.DelayCategoryId,
                    description: obj.DelayCategoryName,
                    color: obj.DelayCategoryColor,
                    casterCode: obj.DelayAreaAssetId == 1000 ? 1 : 2,
                };
                return result;
            },
        });

        Screen.Collections.SRDCategories = Backbone.Collection.extend({
            model: Screen.Models.SRDCategory,
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Models.ItemDelay = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    RN: obj.rn,
                    CasterName: obj.CasterName,
                    AreaName: obj.AreaName,
                    Start: new moment(obj.Start).format("MM/DD/YYYY HH:mm:ss"),
                    End: new moment(obj.End).format("MM/DD/YYYY HH:mm:ss"),
                    Duration: obj.Duration,
                    CauseName: obj.CauseName,
                    CasterCode: obj.CasterCode,
                };
                return result;
            },
        });

        Screen.Collections.ItemsDelays = Backbone.Collection.extend({
            model: Screen.Models.ItemDelay,
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        // Required, return the module for AMD compliance.
        return Screen;

    });
