/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright (c) 2015 Adobe Systems Incorporated. All rights reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 */

/*jslint vars: true, node: true, plusplus: true, devel: true, nomen: true, indent: 4, node: true */
/*global window: true, csInterface: true*/
"use strict";

var $               = require("jquery"),
    _               = require("underscore"),
    Q               = require("q"),
    Path            = require("path"),
    Backbone        = require("backbone"),
    Errors          = require("./errors.js"),
    PathUtils       = require("shared/PathUtils"),
    path            = require("path"),
    PreviewModel    = require("./previewModel.js"),
    PreviewView     = require("./previewView.js"),
    PSAlerts        = require("./psAlerts.js"),
    ServerInterface = require("./serverInterface.js"),
    Headlights      = require("./utils/Headlights"),
    FileDialog      = require("./fileDialog.js"),
    Template        = require("./TemplateLoader"),
    Strings         = require("./LocStrings"),
    ActionRollup    = require("./actionRollup.js"),
    JSXRunner       = require("./JSXRunner"),
    LocalPrefs      = require("./utils/localPrefs.js"),
    HelpTextView    = require("./HelpTextView.js"),
    DocinfoUtils    = require("shared/DocinfoUtils"),
    UserSettings    = require("shared/UserSettings"),
    GenableDialogView   = require("./genableDialogView.js"),
    LayerCollectionView = require("./layerCollectionView.js"),
    SemiModalDialogView = require("./semiModalDialogView.js");

var DialogView = Backbone.View.extend({

    $spinner: $(),
    $spinnerCursor: $(),
    $fileSize: $(),
    $gzipSize: $(),
    $exportBtn: $(),
    $addToExportQueueCheckbox: $(),
    $addToExportQueueContainer: $(),
    zoomOverlayView: null,

    events: {
        "click .cancel-button": "cancelDialog",
        "click .extract-button": "extractLayerAssets",
        "mousemove .spinner": "updateSpinnerCursor",
        "mouseleave .spinner": "hideCursorOnMouseLeave"
    },

    initialize: function (options) {

        var generatorModel = options.generatorModel || new Backbone.Model();
        generatorModel.defaultSettingsModel = generatorModel.defaultSettingsModel || new Backbone.Model();

        this.generatorModel = generatorModel;
        this.previewModel = new PreviewModel();
        this.settings = generatorModel.defaultSettingsModel;
        this._UserSettings = new UserSettings(require("shared/UserSettings/SettingsFileInterface.js"));
        this.listenTo(this.previewModel, "change:srcModel change:loading", this.renderSizes);
        this.listenTo(this.previewModel, "change:srcModel", this.listenForSizeChanges);
        this.listenForSizeChanges();
        this.listenTo(this.model, "change:exporting", this.renderSpinnerVisibility);
        this.listenTo(this.model, "change:exporting", this._updateExportingOnGeneratorModel);
        this.listenTo(this.generatorModel, "change:layerSettingsDisabled", this.renderExportButtonDisabledState);

        if (window.document) {
            $(window.document).keyup(this.handleDocKeyUp.bind(this));
        }

        this._footerTemplate = _.template(Template.loadTemplate("../templates/footerView.html"));
        this._spinnerTemplate = _.template(Template.loadTemplate("../images/Cur_Spinner_11_11.svg"));
    },

    renderExportButtonDisabledState: function () {
        this.$exportBtn.prop("disabled", this.generatorModel.isExportDisabled());
    },

    _updateExportingOnGeneratorModel: function () {
        this.generatorModel.set("exporting", this.model.get("exporting"));
    },
    
    renderLayerCollectionView: function () {
        if (!this.$assetsColumn) {
            this.$assetsColumn = this.$(".assets-column");
        }

        if (!this.layerCollectionView) {
            this.layerCollectionView = new LayerCollectionView({
                collection: this.generatorModel.layerCollection,
            });
        }
        
        $(".dialog-assets.lists").append(this.layerCollectionView.render().$el);
        this.$assetsColumn.removeClass("hide");

        this.renderExportButtonDisabledState();
    },

    renderPreviewView: function () {
        if (!this.previewView) {
            var $previewEl = $(".preview");
            this.previewView = new PreviewView({model: this.previewModel,
                collection: this.generatorModel.layerCollection,
                compCollection: this.generatorModel.compCollection,
                defaultSettings: this.generatorModel.defaultSettingsModel,
                el: $previewEl,
                generatorModel: this.generatorModel
                });
        }

        this.previewView.render();

        var widthClass = "canvas-multiple-layers",
            $frame = $(".canvas-frame"),
            $canvas = $(".canvas-centering-container");

        $frame.addClass(widthClass);
        $canvas.addClass(widthClass);
    },
    
    renderSettingsView: function () {
        if (!this.genableDialogView) {
            this.genableDialogView = new GenableDialogView({ el: $(".dialog-settings"), collection: this.generatorModel.layerCollection});
        }
        this.genableDialogView.render();
        this.renderHelpText();
    },

    renderAddToAssetQueueCheck: function () {
        var checked = LocalPrefs.getPref("addToExportQueueCheckbox") !== "false";
        this.$addToExportQueueCheckbox.prop("checked", checked);
    },

    handleDocKeyUp: function (e) {
        var $target = $(e.target);
        if ($target.is("select")) {
            return;
        }
        if (e.which === 13) { //enter
            if (!$target.is("input") && this.generatorModel.layerCollection.hasDisplayableItems()) {
                this.extractLayerAssets();
            }
        } else if (e.which === 27) {// esc
            this.cancelDialog();
        }
    },

    cancelDialog: function () {
        Headlights.logEvent(Headlights.CREMA_ACTION, Headlights.DLG_CLICK_CANCEL);
        if (this.previewView.$el.hasClass("loading")) {
            Headlights.logEvent(Headlights.CREMA_ACTION, Headlights.DLG_CANCEL_WHILE_LOADING);
        }
        var docId = this.generatorModel.get("docId");
        csInterface.closeExtension();
    },
    
    persistUsersActions: function () {
        return ActionRollup.apply();
    },

    saveDialogSettings: function (genableModel) {
        if (!genableModel) {
            return;
        }
        ActionRollup.serializeDocGeneratorSettings({ docSettings: {
            extension: genableModel.get("extension"),
            quality: genableModel.get("quality"),
            scale: genableModel.get("scale"),
            interpolationType: genableModel.get("interpolationType")
        }});
    },

    logExportEvent: function (headlightsEvent, numberExported, seconds) {
        var exportDoc = false,
            exportDocArtboards = false;
        
        if (this.generatorModel.get("isPerformingDocumentExport")) {
            exportDoc = true;
        } else if (this.generatorModel.get("isPerformingDocumentArtboardExport")) {
            exportDocArtboards = true;
        }
        
        if (this.generatorModel.layerCollection) {
            this.generatorModel.layerCollection.logExportedAssetData(exportDoc, exportDocArtboards, seconds);
        }
        Headlights.logEvent(Headlights.CREMA_ACTION, headlightsEvent);
        if (numberExported) {
            if (exportDoc) {
                Headlights.logEvent(Headlights.CREMA_FUNNEL, Headlights.DLG_EXPORT_FOR_DOCUMENT_DONE);
            } else if (exportDocArtboards) {
                Headlights.logEvent(Headlights.CREMA_FUNNEL, Headlights.DLG_EXPORT_FOR_DOCUMENT_ARTBOARDS_DONE + numberExported);
            } else {
                Headlights.logEvent(Headlights.CREMA_FUNNEL, Headlights.DLG_EXPORT_FOR_SELECTION_DONE + numberExported);
            }
        }
    },

    saveAndCloseDialog: function () {
        var docId = this.generatorModel.get("docId");
        Headlights.logEvent(Headlights.CREMA_ACTION, Headlights.CLICK_DONE);
        this.persistUsersActions().then(function () {
            this.logExportEvent(Headlights.FINISH_DONE);
            csInterface.closeExtension();
        }.bind(this));
    },

    render: function () {
        this.renderFooter();
        this.renderSubViews();
        this.cacheElements();
        this.renderSizes();
        this.renderAddToAssetQueueCheck();
        this.renderExportButtonDisabledState();
        this.renderSpinner();
        return this;
    },

    cacheElements: function () {
        this.$fileSize = this.$("#preview-file-size");
        this.$gzipSize = this.$("#preview-file-size-zipped");
        this.$fileInfoContainer = this.$(".file-size-overlay-container");
        this.$exportBtn = this.$(".extract-button");
        this.$addToExportQueueCheckbox = this.$("input[value='add-to-export-queue']");
        this.$addToExportQueueContainer = this.$("label.add-to-export-queue");
    },
    renderFooter: function () {
        var context;

        if (!this.$elFooter) {
            this.$elFooter = this.$("footer");
        }
        context = Template.createTemplateContext(Strings, {});
        this.$elFooter.html(this._footerTemplate(context));
    },

    renderSpinner: function () {
        var context = Template.createTemplateContext(Strings, {});
        this.$spinner = $("<div></div>").addClass("spinner spinner-cursor-area");
        this.$spinnerCursor = $("<div></div>").addClass("spinner-cursor").appendTo(this.$spinner);
        this.$spinnerCursor.html(this._spinnerTemplate(context));
        this.renderSpinnerVisibility();
        this.$el.append(this.$spinner);
    },

    renderSpinnerVisibility: function () {
        var show = this.model.get("exporting") || false;
        this.$spinner.toggleClass("hide", !show);
    },

    updateSpinnerCursor: function (e) {
        this.$spinnerCursor.show().offset({ top: e.clientY, left: e.clientX });
    },

    hideCursorOnMouseLeave: function () {
        this.$spinnerCursor.hide();
    },

    renderSubViews: function () {
        this.renderPreviewView();
        this.generatorModel.once("docinfo-loaded", function () {
            this.renderLayerCollectionView();
            this.renderSettingsView();
        }.bind(this));
    },

    renderHelpText: function() {
        if(!this.$help) {
            this.$help = this.$(".help");
            this.helpText = new HelpTextView({});
        }
        this.$help.append(this.helpText.render().$el);
    },


    renderSizes: function () {
        var srcModel = this.previewModel.get("srcModel"),
            bytes = (srcModel && !srcModel.get("loading") && srcModel.get("fileSize")) || 0,
            gZipBytes = 0,
            sizeText = this.formatBytesToLabel(bytes); //we don't guess this currently
        if (sizeText) {
            this.$fileInfoContainer.addClass("active");
        } else {
            this.$fileInfoContainer.removeClass("active");
        }
        this.$fileSize.text(sizeText);
        this.$gzipSize.text(this.formatBytesToLabel(gZipBytes));
    },

    listenForSizeChanges: function () {
        var cur = this.previewModel.get("srcModel"),
            prev = this.previewModel.previous("srcModel");

        if (prev) {
            this.stopListening(prev, "change:fileSize change:loading", this.renderSizes);
        }
        if (cur) {
            this.listenTo(cur, "change:fileSize change:loading", this.renderSizes);
        }
    },

    formatBytesToLabel: function (bytes) {
        if (!bytes) {
            return "";
        }
        if (bytes < 1e3) {
            return Strings.formatStr(Strings.SIZE_BYTES, bytes);
        }
        if (bytes < 1e6) {
            return Strings.formatStr(Strings.SIZE_KILOBYTES, Math.round(bytes / 1e2) / 10);
        }
        return Strings.formatStr(Strings.SIZE_MEGABYTES, Math.round(bytes / 1e5) / 10);
    },
    
    createExportComponent: function (genableModel, documentId, layerId, filePath) {
        return genableModel.createExportComponent(documentId, layerId, filePath);
    },
    
    createExportComponentsFromLayer: function(documentId, layerModel) {
        return layerModel.layerSettingsCollection.map(_.partial(this.createExportComponent, _, documentId, layerModel.get("layerId")));
    },
    
    getExportableLayers: function (allLayers, logHeadlights) {
        return allLayers.filter(function (layer) {
            var retVal = true,
                headlightsEvent;
            
            if (layer.get("artboardEmpty")) {
                headlightsEvent = Headlights.ARTBOARD_EMPTY_DLG_EXP;
                retVal = false;
            } else if (layer.get("groupEmpty")) {
                headlightsEvent = Headlights.GROUP_EMPTY_DLG_EXP;
                retVal = false;
            } else if (layer.get("layerEmpty")) {
                headlightsEvent = Headlights.LAYER_EMPTY_DLG_EXP;
                retVal = false;
            } else if (layer.get("outsideDocumentBounds")) {
                headlightsEvent = Headlights.LAYER_OUTSIDE_DOC_DLG_EXP;
                retVal = false;
            } else if (layer.get("clippedByDocumentBounds")) {
                headlightsEvent = Headlights.LAYER_CLIPPEDBY_DOC_DLG_EXP;
            } else if (layer.get("clippedByArtboardBounds")) {
                headlightsEvent = Headlights.LAYER_CLIPPEDBY_ARTBOARD_DLG_EXP;
            }

            if (logHeadlights && headlightsEvent) {
                Headlights.logEvent(Headlights.CREMA_ACTION, headlightsEvent);
            }
            return retVal;
        });
    },
    
    createQuickExportComponents: function () {
        var docId = this.generatorModel.get("docId"),
            exportableItems = this.getExportableLayers(this.generatorModel.layerCollection, true),
            createComponentsForDoc = _.partial(this.createExportComponentsFromLayer, docId),
            quickExportComponents = _.flatten(exportableItems.map(createComponentsForDoc, this));
        
        return quickExportComponents;
    },
    
    updateQuickExportComponentPaths: function(quickExportComponents) {
        var docFileBaseName = this.generatorModel.get("docFileBaseName"),
            docFileDirectory = this.generatorModel.get("docFileDirectory");

        docFileDirectory = this._UserSettings.get(this.generatorModel.get("docFilepath")) || docFileDirectory;
        
        if (quickExportComponents.length === 1) {
            var component = _.first(quickExportComponents);
            return FileDialog.promptForExportFile(docFileBaseName, component.basename, component.extension, docFileDirectory).then(function(filepath) {
                component.path = filepath;
                this._stashDirSetting(path.dirname(filepath));
                return quickExportComponents;
            }.bind(this), function (err) {
                return Q.reject(err);
            });
        }
        
        return FileDialog.promptForExportDirectory(docFileBaseName, docFileDirectory).then(function(destFolder) {
            _.each(quickExportComponents, function(component) {
                component.path = PathUtils.buildPath(destFolder, component.basename, component.extension);
            }, this);
            this._stashDirSetting(destFolder);
            return quickExportComponents;
        }.bind(this), function (err) {
            return Q.reject(err);
        });
    },
    
    getCurrentPreviewSettingsModel: function () {
        return this.previewModel.get("layerModel").getActivePreview();
    },
    _stashDirSetting: function(destFolder) {
        this._UserSettings.setCachedValue(this.generatorModel.get("docFilepath"), destFolder);
    },
    extractLayerAssets: function () {
        Headlights.logEvent(Headlights.CREMA_ACTION, Headlights.DLG_CLICK_EXPORT);
        var saveToExportQueue = true, // this.$addToExportQueueCheckbox.prop("checked"),
            docId = this.generatorModel.get("docId"),
            quickExportComponents = this.createQuickExportComponents(),
            currentPreviewGenableModel = this.getCurrentPreviewSettingsModel(),
            shouldClose = true,
            startTime,
            onError = function (e) {
                if (e && e.message === "cancel") {
                    // User canceled the export. Keep the dialog open.
                    shouldClose = false;
                } else if (e instanceof Errors.UserFacingError) {
                    // There was an expected error we need to show the user. Keep the dialog open so they can fix it.
                    shouldClose = false;
                    return PSAlerts.alert(e.message);
                } else {
                    // Uh oh, we didn't expect this error.
                    console.warn("Error in extractLayerAssets: " + e && e.message);
                }
            }.bind(this),
            exportFinished = function (result) {
                var stopTime = new Date().getTime(),
                    secondsToTenths = Math.round((stopTime - startTime) / 100) / 10;

                if (result && result.length) {
                    this.logExportEvent(Headlights.DLG_FINISH_EXPORT, result.length, secondsToTenths);
                }
            }.bind(this),
        exportComponents = function (quickExportComponents) {
                this.model.set("exporting", true);
                startTime = new Date().getTime();
                return ServerInterface.sendCommand("exportComponents", {components: quickExportComponents})
                    .catch(function (e) {
                        if (/WriteError/.test(e)) {
                            Headlights.logEvent(Headlights.CREMA_ACTION, Headlights.WRITEERROR_ON_DLG_EXPORT);                       
                            throw new Errors.FSWriteError(Path.dirname(_.first(quickExportComponents).path));
                        }

                        // Don't stop the export workflow if there was a silent error during asset generation (e.g. user
                        // tried to export an empty layer).
                        console.warn("Error in exportComponents: " + e);
                    }.bind(this));
            }.bind(this),
            commitSave = function () {
                this.saveDialogSettings(currentPreviewGenableModel);
                LocalPrefs.setPref("addToExportQueueCheckbox", saveToExportQueue ? "true" : "false");
                if (saveToExportQueue) {
                    return this.persistUsersActions();
                }
                return Q.resolve();
            }.bind(this),
            closeIfNeeded = function () {
                this.model.set("exporting", false);

                if (shouldClose) {
                    global.csInterface.closeExtension();
                } else {
                    JSXRunner.runRawJSX("app.bringToFront();");
                }
            }.bind(this);
        
        if (!quickExportComponents || quickExportComponents.length === 0) {
            return closeIfNeeded();
        }
        
        return this.updateQuickExportComponentPaths(quickExportComponents)
            .then(exportComponents)
            .then(exportFinished)
            .then(commitSave)
            .catch(onError)
            .finally(closeIfNeeded);
    }
});

module.exports = DialogView;
