import { ToolboxDraggingObject, IShapeToolboxOptions, Toolbox } from "./Toolbox";
import { IShapeDescriptionManager } from "../../Model/Shapes/Descriptions/ShapeDescriptionManager";
import { svgNS } from "../RenderHelper";
import { Shape } from "../../Model/Shapes/Shape";
import { ShapeDescription } from "../../Model/Shapes/Descriptions/ShapeDescription";
import { UnitConverter } from "@devexpress/utils/lib/class/unit-converter";
import { Point } from "@devexpress/utils/lib/geometry/point";
import { TextShapeDescription } from "../../Model/Shapes/Descriptions/General/TextShapeDescription";
import { ITextMeasurer } from "../Measurer/ITextMeasurer";
import { DEFAULT_STROKE_WIDTH } from "../../Model/Style";
import { CustomShapeDescription } from "../../Model/Shapes/Descriptions/CustomShapeDescription";
import { RectanglePrimitive } from "../Primitives/RectaglePrimitive";

const DEFAULT_SHAPE_ICON_SIZE = 32;
const SHRINK_TEXT_SHAPE_ICON_SIZE = 26;
const SHRINKED_TEXT = "T";

export class IconToolbox extends Toolbox {
    options: IShapeIconToolboxOptions;
    instanceId: string;

    constructor(parent: HTMLElement, readonly: boolean, allowDragging: boolean,
        shapeDescriptionManager: IShapeDescriptionManager,
        shapeTypes: string[], getAllowedShapeTypes: (shapes: string[]) => string[],
        options: IShapeIconToolboxOptions, measurer: ITextMeasurer, instanceId: string) {
        super(parent, readonly, allowDragging, shapeDescriptionManager, shapeTypes, getAllowedShapeTypes);
        this.options = options;
        this.measurer = measurer;
        this.instanceId = instanceId;
    }
    createElements(element: HTMLElement, shapeTypes: string[]) {
        const svgElement = document.createElementNS(svgNS, "svg");
        svgElement.className.baseVal = "dxdi-canvas";
        element.appendChild(svgElement);
        this.drawShapeIcons(svgElement, shapeTypes, this.options.toolboxWidth || svgElement.getBoundingClientRect().width);
    }
    drawShapeIcons(parent: SVGElement, shapeTypes: string[], svgWidth: number) {
        const lineWidth = DEFAULT_STROKE_WIDTH;
        const targetWidth = svgWidth - 2 * lineWidth;
        let shapeIconSize = this.options.shapeIconSize;
        if(!shapeIconSize && this.options.shapeIconSpacing && this.options.shapeIconCountInRow)
            shapeIconSize = Math.floor((targetWidth - (this.options.shapeIconCountInRow - 1) * this.options.shapeIconSpacing) / this.options.shapeIconCountInRow);
        if(!shapeIconSize)
            shapeIconSize = DEFAULT_SHAPE_ICON_SIZE;
        shapeIconSize = Math.max(shapeIconSize, this.options.shapeIconSpacing / 2);
        let width = shapeIconSize;
        let iconCountInRow = this.options.shapeIconCountInRow;
        if(!iconCountInRow) {
            iconCountInRow = 1;
            while(width < targetWidth) {
                width += this.options.shapeIconSpacing + shapeIconSize;
                if(width < targetWidth)
                    iconCountInRow++;
            }
        }
        const shapeIconSpacing = (iconCountInRow > 1) ? (targetWidth - shapeIconSize * iconCountInRow) / (iconCountInRow - 1) : 0;
        let xPos = lineWidth; let yPos = lineWidth;
        const size = UnitConverter.pixelsToTwips(shapeIconSize);
        shapeTypes.forEach((shapeType, index) => {
            if(index > 0 && index % iconCountInRow === 0) {
                xPos = lineWidth;
                yPos += shapeIconSize + shapeIconSpacing;
            }
            const shapeDescription = this.shapeDescriptionManager.get(shapeType);
            const shape = this.createShape(shapeDescription, xPos, yPos, shapeIconSize < SHRINK_TEXT_SHAPE_ICON_SIZE);
            this.updateShapeIconBounds(shape, shapeIconSize);
            const shapeEl = this.drawShape(parent, shape);
            this.drawSelector(shapeEl, UnitConverter.pixelsToTwips(xPos), UnitConverter.pixelsToTwips(yPos), size);
            xPos += shapeIconSize + shapeIconSpacing;
        });
        parent.style.height = yPos + shapeIconSize + lineWidth + "px";
        parent.style.width = svgWidth + "px";
    }
    drawShape(parent: SVGElement, shape: Shape): SVGGElement {
        const primitives = shape.description.createPrimitives(shape, this.instanceId, true);
        const gEl: SVGGElement = document.createElementNS(svgNS, "g");
        gEl.setAttribute("data-tb-type", shape.description.key.toString());
        gEl.setAttribute("class", "toolbox-item");
        gEl.setAttribute("title", shape.description.getTitle());
        if(this.options.shapeIconAttributes)
            for(const key in this.options.shapeIconAttributes)
                if(Object.prototype.hasOwnProperty.call(this.options.shapeIconAttributes, key))
                    gEl.setAttribute(key, this.options.shapeIconAttributes[key]);


        parent.appendChild(gEl);
        primitives.forEach(pr => {
            const el = pr.createElement(e => gEl.appendChild(e));
            pr.applyElementProperties(el, this.measurer);
        });
        return gEl;
    }
    drawSelector(parent: SVGGElement, x: number, y: number, size: number) {
        const selectorRect = new RectanglePrimitive(x, y, size, size, undefined, "selector");
        selectorRect.createElement(el => {
            selectorRect.applyElementProperties(el, this.measurer);
            parent.appendChild(el);
        });
    }
    createShape(shapeDescription: ShapeDescription, xPos: number, yPos: number, shrinkText?: boolean) {
        const xPosT = UnitConverter.pixelsToTwips(xPos);
        const yPosT = UnitConverter.pixelsToTwips(yPos);
        const shape = new Shape(shapeDescription, new Point(xPosT, yPosT), true);
        if(this.needResetShapeText(shapeDescription))
            shape.text = "";
        else if(shrinkText)
            shape.text = SHRINKED_TEXT;
        return shape;
    }
    needResetShapeText(shapeDescription: ShapeDescription) {
        if(shapeDescription instanceof TextShapeDescription)
            return false;
        if(shapeDescription instanceof CustomShapeDescription && shapeDescription.baseDescription instanceof TextShapeDescription)
            return false;
        return true;
    }
    updateShapeIconBounds(shape: Shape, shapeIconSize: number) {
        const shapeSizeT = UnitConverter.pixelsToTwips(shapeIconSize);
        shape.size.height = shape.size.width * shape.getToolboxHeightToWidthRatio();
        if(shape.size.width > shape.size.height) {
            const ratio = shape.size.height / shape.size.width;
            shape.size.width = shapeSizeT;
            shape.size.height = shapeSizeT * ratio;
            shape.position.y = shape.position.y + (shapeSizeT - shape.size.height) / 2;
            shape.parameters.forEach((p) => { p.value = p.value * shapeSizeT / shape.description.defaultSize.width; });
        }
        else if(shape.size.width < shape.size.height) {
            const ratio = shape.size.width / shape.size.height;
            shape.size.height = shapeSizeT;
            shape.size.width = shapeSizeT * ratio;
            shape.position.x = shape.position.x + (shapeSizeT - shape.size.width) / 2;
            shape.parameters.forEach((p) => { p.value = p.value * shapeSizeT / shape.description.defaultSize.height; });
        }
        else {
            shape.size.width = shapeSizeT;
            shape.size.height = shapeSizeT;
            shape.parameters.forEach((p) => { p.value = p.value * shapeSizeT / shape.description.defaultSize.width; });
        }
    }
    protected createDraggingElement(draggingObject: ToolboxDraggingObject): HTMLElement {
        const element = document.createElement("DIV");
        element.setAttribute("class", "dxdi-toolbox-drag-item");
        document.body.appendChild(element);

        const svgElement = document.createElementNS(svgNS, "svg");
        svgElement.className.baseVal = "dxdi-canvas";
        element.appendChild(svgElement);

        const shapeDescription = this.shapeDescriptionManager.get(draggingObject.evt.data);
        const shape = this.createShape(shapeDescription, DEFAULT_STROKE_WIDTH, DEFAULT_STROKE_WIDTH);
        this.drawShape(svgElement, shape);

        element.style.width = UnitConverter.twipsToPixels(shape.size.width) + 2 * DEFAULT_STROKE_WIDTH + "px";
        element.style.height = UnitConverter.twipsToPixels(shape.size.height) + 2 * DEFAULT_STROKE_WIDTH + "px";
        return element;
    }
}

export interface IShapeIconToolboxOptions extends IShapeToolboxOptions {
    shapeIconSpacing: number;
    shapeIconSize?: number;
    shapeIconCountInRow?: number;
    shapeIconAttributes?: {[key: string]: any};
    toolboxWidth?: number;
}
