// TODO: add undo for layer drag n drop

/** Handler class for a single canvas (a single layer)
 *
 * @param width Canvas width
 * @param height Canvas height
 * @param canvas HTML canvas element or the ID of the canvas related to the layer
 */
class Layer {
    static layerCount = 1;
    static maxZIndex = 3;
    
    static unusedIDs = [];
    static currentID = 1;

    static layerOptions = document.getElementById("layer-properties-menu");

    // TODO: this is simply terrible. menuEntry can either be an HTML element, a string or undefined.
    // If it's an HTML element it is added, if it's a string nothing happens, if it's undefined the 
    // first menuEntry is used (the one that appears on load)
    constructor(width, height, canvas, menuEntry) {
        // REFACTOR: the canvas should actually be a Canvas instance
        this.canvas = Util.getElement(canvas);
        this.canvas.width = width;
        this.canvas.height = height;
        // REFACTOR: the context could be an attribute of the canvas class, but it's a bit easier
        // to just type layer.context, we should discuss this
        this.context = this.canvas.getContext('2d');
        this.isSelected = false;
        this.isVisible = true;
        this.isLocked = false;
        
        this.oldLayerName = null;

        if (typeof menuEntry == "string")
            this.menuEntry = document.getElementById("layers-menu").firstElementChild;
        else if (menuEntry !== undefined)
            this.menuEntry = menuEntry;

        let id = Layer.unusedIDs.pop();

        if (id == null) {
            id = Layer.currentID;
            Layer.currentID++;
        }

        this.id = "layer" + id;

        // Binding the events
        if (this.menuEntry !== undefined) {
            this.name = this.menuEntry.getElementsByTagName("p")[0].innerHTML;
            this.menuEntry.id = "layer" + id;

            this.menuEntry.onmouseover = () => this.hover();
            this.menuEntry.onmouseout = () => this.unhover();
            this.menuEntry.onclick = () => this.selectLayer();
            this.menuEntry.getElementsByTagName("button")[0].onclick = () => this.toggleLock();
            this.menuEntry.getElementsByTagName("button")[1].onclick = () => this.toggleVisibility();

            this.menuEntry.addEventListener("mouseup", this.openOptionsMenu, false);
            this.menuEntry.addEventListener("dragstart", this.layerDragStart, false);
            this.menuEntry.addEventListener("drop", this.layerDragDrop, false);
            this.menuEntry.addEventListener("dragover", this.layerDragOver, false);
            this.menuEntry.addEventListener("dragleave", this.layerDragLeave, false);
            this.menuEntry.addEventListener("dragend", this.layerDragEnd, false);

            Events.onCustom("del", this.tryDelete.bind(this));

            this.menuEntry.getElementsByTagName("canvas")[0].getContext('2d').imageSmoothingEnabled = false;
        }

        this.initialize();
    }

    hasCanvas() {
        return this.menuEntry != null;
    }

    tryDelete() {
        if (Input.getLastTarget() != this.menuEntry && Input.getLastTarget().parentElement != this.menuEntry)
            return;
        LayerList.deleteLayer();
    }

    // Initializes the canvas
    initialize() {
        //resize canvas
        this.canvas.style.width = (this.canvas.width*currFile.zoom)+'px';
        this.canvas.style.height = (this.canvas.height*currFile.zoom)+'px';

        //show canvas
        this.canvas.style.display = 'block';

        //center canvas in window
        this.canvas.style.left = 64+currFile.canvasView.clientWidth/2-(currFile.canvasSize[0]*currFile.zoom/2)+'px';
        this.canvas.style.top = 48+currFile.canvasView.clientHeight/2-(currFile.canvasSize[1]*currFile.zoom/2)+'px';

        this.context.imageSmoothingEnabled = false;
        this.context.mozImageSmoothingEnabled = false;
    }

    rename() {
        this.menuEntry.getElementsByTagName("p")[0].setAttribute("contenteditable", false);

        if (this.oldLayerName != null) {
            let name = this.menuEntry.getElementsByTagName("p")[0].innerHTML;
            
            for (let i=0; i<currFile.layers.length; i++) {
                if (name === currFile.layers[i].name) {
                    name += ' (1)';
                    i = 0;
                }
            }
            this.name = name;
            this.menuEntry.getElementsByTagName("p")[0].innerHTML = name;

            new HistoryState().RenameLayer(this.oldLayerName, name, currFile.currentLayer);
            this.oldLayerName = null;
        }
    }

    hover() {
        // Hides all the layers but the current one
        for (let i=1; i<currFile.layers.length - nAppLayers; i++) {
            if (currFile.layers[i] !== this) {
                currFile.layers[i].canvas.style.opacity = 0.3;
            }
        }
    }

    unhover() {
        // Shows all the layers again
        for (let i=1; i<currFile.layers.length - nAppLayers; i++) {
            if (currFile.layers[i] !== this) {
                currFile.layers[i].canvas.style.opacity = 1;
            }
        }
    }

    setID(id) {
        this.id = id;
        if (this.menuEntry != null) {
            this.menuEntry.id = id;
        }
    }

    // Resizes canvas
    resize() {
        let newWidth = (this.canvas.width * currFile.zoom) + 'px';
        let newHeight = (this.canvas.height *currFile.zoom)+ 'px';

        this.canvas.style.width = newWidth;
        this.canvas.style.height = newHeight;
    }

    setCanvasOffset (offsetLeft, offsetTop) {
        //horizontal offset
        var minXOffset = -currFile.canvasSize[0] * currFile.zoom;
        var maxXOffset = window.innerWidth - 300;
    
        if 	(offsetLeft < minXOffset)
            this.canvas.style.left = minXOffset +'px';
        else if (offsetLeft > maxXOffset)
            this.canvas.style.left = maxXOffset +'px';
        else
            this.canvas.style.left = offsetLeft +'px';
    
        //vertical offset
        var minYOffset = -currFile.canvasSize[1] * currFile.zoom + 164;
        var maxYOffset = window.innerHeight - 100;
    
        if 	(offsetTop < minYOffset)
            this.canvas.style.top = minYOffset +'px';
        else if (offsetTop > maxYOffset)
            this.canvas.style.top = maxYOffset +'px';
        else
            this.canvas.style.top = offsetTop +'px';
    }

    // Copies the otherLayer's position and size
    copyData(otherLayer) {
        this.canvas.style.width = otherLayer.canvas.style.width;
        this.canvas.style.height = otherLayer.canvas.style.height;
        
        this.canvas.style.left = otherLayer.canvas.style.left;
        this.canvas.style.top = otherLayer.canvas.style.top;
    }

    selectLayer(hideOptions = true) {
        if (hideOptions)
            LayerList.closeOptionsMenu();
        // Deselecting the old layer
        currFile.currentLayer.deselectLayer();

        // Selecting the current layer
        this.isSelected = true;
        this.menuEntry.classList.add("selected-layer");
        currFile.currentLayer = this;
    }

    toggleLock() {
        if (this.isLocked) {
            this.unlock();
        }
        else {
            this.lock();
        }
    }

    toggleVisibility() {
        if (this.isVisible) {
            this.hide();
        }
        else {
            this.show();
        }
    }

    deselectLayer() {
        this.isSelected = false;
        this.menuEntry.classList.remove("selected-layer");
    }

    lock() {
        this.isLocked = true;
        this.menuEntry.getElementsByClassName("layer-button")[0].style.visibility = "visible";

        this.menuEntry.getElementsByClassName("default-icon")[0].style.display = "none";
        this.menuEntry.getElementsByClassName("edited-icon")[0].style.display = "inline-block";
    }

    unlock() {
        this.isLocked = false;
        this.menuEntry.getElementsByClassName("layer-button")[0].style.visibility = "hidden";

        this.menuEntry.getElementsByClassName("default-icon")[0].style.display = "inline-block";
        this.menuEntry.getElementsByClassName("edited-icon")[0].style.display = "none";
    }

    show() {
        this.isVisible = true;
        this.canvas.style.visibility = "visible";
        this.menuEntry.getElementsByClassName("layer-button")[1].style.visibility = "hidden";

        // Changing icon
        this.menuEntry.getElementsByClassName("default-icon")[1].style.display = "inline-block";
        this.menuEntry.getElementsByClassName("edited-icon")[1].style.display = "none";
    }

    hide() {
        this.isVisible = false;
        this.canvas.style.visibility = "hidden";
        this.menuEntry.getElementsByClassName("layer-button")[1].style.visibility = "visible";

        // Changing icon
        this.menuEntry.getElementsByClassName("default-icon")[1].style.display = "none";
        this.menuEntry.getElementsByClassName("edited-icon")[1].style.display = "inline-block";
    }

    updateLayerPreview() {
        // Getting the canvas
        let destination = this.menuEntry.getElementsByTagName("canvas")[0];
        let widthRatio = currFile.canvasSize[0] / currFile.canvasSize[1];
        let heightRatio = currFile.canvasSize[1] / currFile.canvasSize[0];

        // Computing width and height for the preview image
        let previewWidth = destination.width;
        let previewHeight = destination.height;

        // If the sprite is rectangular, I apply the ratio to the preview as well
        if (widthRatio < 1) {
            previewWidth = destination.width * widthRatio;
        }
        else if (widthRatio > 1) {
            previewHeight = destination.height * heightRatio;
        }

        // La appiccico sulla preview
        destination.getContext('2d').clearRect(0, 0, destination.width, destination.height);
        destination.getContext('2d').drawImage(this.canvas,
            // This is necessary to center the preview in the canvas
            (destination.width - previewWidth) / 2, (destination.height - previewHeight) / 2,
            previewWidth, previewHeight);
    }

    drawLine(x0,y0,x1,y1, brushSize) {
        var dx = Math.abs(x1-x0);
        var dy = Math.abs(y1-y0);
        var sx = (x0 < x1 ? 1 : -1);
        var sy = (y0 < y1 ? 1 : -1);
        var err = dx-dy;
    
        while (true) {
            //set pixel
            // If the current tool is the brush
            if (ToolManager.currentTool().name == 'brush' || ToolManager.currentTool().name == 'rectangle' || ToolManager.currentTool().name == 'ellipse') {
                // I fill the rect
                currFile.currentLayer.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize);
            } else if (ToolManager.currentTool().name == 'eraser') {
                // In case I'm using the eraser I must clear the rect
                currFile.currentLayer.context.clearRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize);
            }
    
            //if we've reached the end goal, exit the loop
            if ((x0==x1) && (y0==y1)) break;
            var e2 = 2*err;
    
            if (e2 >-dy) {
                err -=dy; 
                x0+=sx;
            }
    
            if (e2 < dx) {
                err +=dx; 
                y0+=sy;
            }
        }
    }
}