/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *  
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *  
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 * 
 */

/*jslint vars: true, node: true, plusplus: true, devel: true, nomen: true, indent: 4*/

(function () {
    "use strict";
    
    var AssetExporter   = require("./asset-exporter"),
        DocumentManager = require("generator-assets/lib/documentmanager");

    // TODO: This class doesn't represent any abstraction layer. It's vestigal and should be rethought. Perhaps some or
    // all of it belongs in AssetExporter.
    function AssetExtractor() {
        this._generator = null;
        this._config = null;
        this._logger = null;
        this._documentManager = null;
    }

    AssetExtractor.prototype.init = function (generator, config, logger) {
        this._generator = generator;
        this._config = config;
        this._logger = logger;
        var options = { getDocumentInfoFlags: { getTextStyles: false,
                                                compInfo: false,
                                                getCompLayerSettings: false }};
        this._documentManager = new DocumentManager(generator, config, logger, options);
    };
    
    /**
     * Renders a component object, representing an asset, to a temporary location
     *
     * @param {!Component} component
     * @param {number} component.documentId Document to export, or if layerId is defined, the document that the layerId
     *      belongs to.
     * @param {number=} component.layerId Layer to export.
     * @param {!string} component.extension The type of asset to export (e.g. "jpg").
     * @param {number=} component.quality Quality settings for the exported asset.
     *      For extension "png", set quality to 8 to produce a PNG-8 image.
     *      For extension "jpg", set quality from 0-100.
     * @param {number=} component.scale The scale of the exported asset.
     * @param {number=} component.width The width of the exported asset.
     * @param {number=} component.height The height of the exported asset.
     * return {Promise} This promise is resolved when the layer is finished rendering with the temp file location or buffer
     */
    AssetExtractor.prototype.generateComponent = function (component) {
        // Resolve documentId and layerId to DOM objects.
        return this
            .getDocument(component.documentId)
            .then(function (document) {
                component.document = document;

                if (component.layerId) {
                    var result = document.layers.findLayer(component.layerId);
                    if (!result) {
                        throw new Error("Layer with id %d not found.", component.layerId);
                    }

                    component.layer = result.layer;
                }

                return AssetExporter
                    .exportAsset({
                        component: component,
                        generator: this._generator,
                        logger: this._logger,
                        config: this._config
                    });
            }.bind(this));
    };

    AssetExtractor.prototype.generatePreview = function (component) {
        return this
            .generateComponent(component)
            .then(function (result) {
                result = result || {};
                // note: we might have result.errors, if need that.
                return { size: 0 };
            })
            .then(function (fileStats) {
                component.fileSize = fileStats.size;
            })
            .catch(function (e) {
                e = e || {};
                this._logger.error("Error generating preview:", this._getComponentNameForLogging(component), e);
                return component;
            }.bind(this));
    };


    AssetExtractor.prototype._getComponentNameForLogging = function (component) {
        return (component.layer && component.layer.name) ||
            (component.document && component.document.name);
    };

    /**
     * Recursively determine PS visibility of layer. If visible property is undefined or null,
     * then interpret it as visible:true
     * @return {boolean} True if layer is visible in PS
     */
    AssetExtractor.prototype._isVisible = function (layer) {
        var layerVisible = (layer.visible !== false);
        if (!layerVisible || !layer.layers || !layer.layers.length) {
            return layerVisible;
        } else {
            // Only need 1 sublayer to be visible
            return layer.layers.some(this._isVisible.bind(this));
        }
    };

    /**
     * Component layer is always visible for extracting, so ignore visible flag.
     * Group is visible for extracting if it has at least one visible child.
     * @return {boolean} True if layer is visible for extracting
     */
    AssetExtractor.prototype._isComponentVisible = function (layer) {
        return !layer || !layer.layers || layer.layers.some(this._isVisible.bind(this));
    };

    /**
     * Gets a document by id.
     *
     * return {Promise} Resolved with the specified document or rejected if it is not available.
     */
    AssetExtractor.prototype.getDocument = function (id) {
        return this._documentManager.getDocument(id);
    };

    /**
     * Gets the currently open document in Photoshop.
     *
     * return {Promise} Resolved with the active document or rejected if none is open.
     */
    AssetExtractor.prototype.getActiveDocument = function () {
        return this._documentManager.getActiveDocument();
    };

    module.exports = new AssetExtractor();
}());
