/*
 * 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, plusplus: true, devel: true, nomen: true, indent: 4, bitwise: true, node: true */
/*global define, csInterface, require, module */

"use strict";

module.exports = {
    /**
     * @param layers {Array} An array of duck-typed layer objects. Expected properties defined below.
     * @param layers[i].id {string} The layer id.
     * @param layers[i].name {string} The original layer name.
     * @param layers[i].extension {string}* optional parameter for a file extension without the "." (so "png")
     * @return {Object} A hash map of layer ids to sanitized, unique basenames.
     */
    createSanitizedUniqueLayerBasenamesMap: function (layers) {
        var resultLayers = this._cloneLayers(layers);
        this._sanitizeLayerNames(resultLayers);
        this._sortLayersForConflictResolution(resultLayers);
        this._resolveLayerNameConflicts(resultLayers);
        return this._createLayerNameMap(resultLayers);
    },

    _cloneLayers: function (layers) {
        // Clone only exactly we need from each layer.
        return layers.map(function (layer) {
            return {
                id: layer.id,
                name: layer.name,
                extension: layer.extension
            };
        });
    },

    _sanitizeLayerNames: function (layers) {
        layers.forEach(function (layer) {
            layer.name = this.sanitize(layer.name);
        }, this);
    },

    /**
     * Sort the layer ids by case-insensitive name, descending, then id, ascending.
     *
     * Suppose we're exporting layers with names "layer-1", "layer", and "layer". Sorting by name descending ensures
     * that "layer-1" comes before both "layer"s. When we're reserving unique names for each layer, this ensures
     * the following results:
     *
     * (1) "layer-1": "layer-1.png" // Filename matches layer name.
     * (2) "layer": "layer.png"     // Filename matches layer name.
     * (3) "layer": "layer-2.png"
     *
     * If they were sorted the other way, for example, the following would occur:
     *
     * (1) "layer": "layer.png"     // Filename matches layer name.
     * (2) "layer": "layer-1.png"
     * (3) "layer-1": "layer-2.png"
     *
     * The latter sort creates fewer matches between filename and layer name, so it is less desirable.
     *
     * We also sort by id in order to create a stable sort. This ensures when both "layer" and "layer" are selected, the
     * same one will receive "layer.png" every time. Although not entirely necessary, we sort the name
     * case-insensitively so that id will dictate the suffix assignment order when layers have the same name except for
     * case. For our purposes, conflicts are case-insensitive, so the resulting assignment order is more expected and
     * easier to test.
     */
    _sortLayersForConflictResolution: function (layers) {
        layers.sort(function (layer1, layer2) {
            var name1 = layer1.name.toLowerCase(),
                name2 = layer2.name.toLowerCase();
            
            if (layer1.extension) {
                name1 += "." + layer1.extension.toLowerCase();
            }
            if (layer2.extension) {
                name2 += "." + layer2.extension.toLowerCase();
            }

            if (name1 < name2)
                return 1;
            else if (name1 > name2)
                return -1;
            else
                return layer1.id - layer2.id;
        });
    },

    _resolveLayerNameConflicts: function (layers) {
        var reservedNames = {};
        layers.forEach(function (layer) {
            var extension = layer.extension ? "." + layer.extension.toLowerCase() : "";
            layer.name = this._suffixNameNumericallyIfNeeded(layer.name, function (attemptedName) {
                // If the attempted name has been reserved by another layer already, we have a conflict.
                var lowercaseAttemptedName = attemptedName.toLowerCase() + extension;
                return reservedNames[lowercaseAttemptedName];
            });

            // Reserve the name for this layer, in a case-insensitive way.
            var lowercaseName = layer.name.toLowerCase() + extension;
            reservedNames[lowercaseName] = true;
        }, this);
    },

    _createLayerNameMap: function (layers) {
        var layerNameMap = {};
        layers.forEach(function (layer) {
            layerNameMap[layer.id] = layer.name;
        });
        return layerNameMap;
    },

    _suffixNameNumericallyIfNeeded: function(name, predicate) {
        var resultName = name;
        for (var i = 1; predicate(resultName); i++) {
            resultName = name + "-" + i;
        }
        return resultName;
    },

    _badChars: /[\\\/":*?<>!|]/g,

    sanitize: function(path, replacement) {
        replacement = replacement || "_";
        return path.replace(this._badChars, replacement);
    }
};