import { Rectangle } from "@devexpress/utils/lib/geometry/rectangle";
import { Size } from "@devexpress/utils/lib/geometry/size";
import { Point } from "@devexpress/utils/lib/geometry/point";
import { SvgPrimitive } from "../../Render/Primitives/Primitive";
import { ShapeDescription, ShapeMinDimension } from "./Descriptions/ShapeDescription";
import { ShapeParameters } from "./ShapeParameters";
import { DiagramItem, ConnectionPointSide } from "../DiagramItem";
import { ConnectionPoint } from "../ConnectionPoint";
import { ImageInfo } from "../../Images/ImageInfo";
import { INativeShape } from "../../Api/INativeItem";
import { NativeShape } from "../../Api/NativeItem";
import { TextAngle } from "../../Render/Primitives/TextPrimitive";
import { DiagramUnit } from "../..";

export class Shape extends DiagramItem {
    position: Point;
    size: Size;
    text: string;
    image: ImageInfo;
    parameters: ShapeParameters;
    children: DiagramItem[] = [];
    expanded: boolean = true;
    expandedSize: Size = undefined;

    constructor(public description: ShapeDescription, position: Point, forToolbox?: boolean) {
        super();
        if(!description)
            throw Error("Shape type is incorrect");

        this.position = position.clone();
        const defaultSize = forToolbox ? description.toolboxSize : description.defaultSize;
        this.size = defaultSize.clone();
        this.text = description.getDefaultText();
        this.image = new ImageInfo(description.getDefaultImageUrl());

        this.parameters = new ShapeParameters();
        description.createParameters(this.parameters);
    }

    assign(item: Shape) {
        super.assign(item);

        item.size = this.size.clone();
        item.text = this.text;
        item.image = this.image.clone();
        item.parameters = this.parameters.clone();

        item.children = this.children.slice();
        item.expanded = this.expanded;
        if(this.expandedSize)
            item.expandedSize = this.expandedSize.clone();
    }
    clone() {
        const clone = new Shape(this.description, this.position.clone());
        this.assign(clone);
        return clone;
    }

    get enableText() { return this.description.enableText; }
    get allowEditText() { return this.description.allowEditText; }

    get hasTemplate() { return this.description.hasTemplate; }

    get enableChildren() { return this.description.enableChildren; }

    get enableImage() { return this.description.enableImage; }
    get allowEditImage() { return this.description.allowEditImage; }

    getMinWidth(settingsMinWidth: number | undefined) {
        return typeof (this.description.minWidth) === "number" ? this.description.minWidth :
            typeof (settingsMinWidth) === "number" ? settingsMinWidth : ShapeMinDimension;
    }
    getMinHeight(settingsMinHeight: number | undefined) {
        return typeof (this.description.minHeight) === "number" ? this.description.minHeight :
            typeof (settingsMinHeight) === "number" ? settingsMinHeight : ShapeMinDimension;
    }
    getMaxWidth(settingsMaxWidth: number | undefined) {
        return typeof (this.description.maxWidth) === "number" ? this.description.maxWidth :
            typeof (settingsMaxWidth) === "number" ? settingsMaxWidth : undefined;
    }
    getMaxHeight(settingsMaxHeight: number | undefined) {
        return typeof (this.description.maxHeight) === "number" ? this.description.maxHeight :
            typeof (settingsMaxHeight) === "number" ? settingsMaxHeight : undefined;
    }

    createPrimitives(instanceId: string): SvgPrimitive<SVGGraphicsElement>[] {
        return this.description.createPrimitives(this, instanceId);
    }
    createSelectorPrimitives(): SvgPrimitive<SVGGraphicsElement>[] {
        return this.description.createSelectorPrimitives(this);
    }
    normalizeX(x: number) {
        return Math.max(this.position.x, Math.min(x, this.position.x + this.size.width));
    }
    normalizeY(y: number) {
        return Math.max(this.position.y, Math.min(y, this.position.y + this.size.height));
    }
    getConnectionPoints(): ConnectionPoint[] {
        const result = this.description.getConnectionPoints().map(pt => {
            const point = new ConnectionPoint(
                this.position.x + pt.x * this.size.width,
                this.position.y + pt.y * this.size.height,
                pt.side
            );
            this.description.processConnectionPoint(this, point);
            return point;
        });
        return result;
    }
    getConnectionPointSide(point: ConnectionPoint, targetPoint?: Point): ConnectionPointSide {
        if(point.side !== ConnectionPointSide.Undefined)
            return point.side;
        return this.getConnectionPointSideByGeometry(point);
    }
    getConnectionPointSideByGeometry(point: Point): ConnectionPointSide {
        const pt = point.clone().offset(-this.position.x, -this.position.y).multiply(1 / this.size.width, 1 / this.size.height);
        return ShapeDescription.getConnectionPointSideByGeometry(pt);
    }
    getConnectionPointIndexForItem(item: DiagramItem, connectionPointIndex: number): number {
        return this.description.getConnectionPointIndexForItem(item, connectionPointIndex);
    }
    getConnectionPointIndexForSide(side: ConnectionPointSide): number {
        return this.description.getConnectionPointIndexForSide(side);
    }
    toggleExpandedSize(): void {
        if(!this.expanded) {
            this.expandedSize = this.size.clone();
            this.size = this.getCollapsedSize();
        }
        else {
            this.size = this.getExpandedSize();
            this.expandedSize = undefined;
        }
    }
    getExpandedSize(): Size {
        return this.description.getExpandedSize(this);
    }
    getCollapsedSize(): Size {
        return this.description.getCollapsedSize(this);
    }
    getToolboxHeightToWidthRatio(): number {
        return this.description.getToolboxHeightToWidthRatio(this.size.width, this.size.height);
    }
    get allowResizeHorizontally() {
        return this.description.allowResizeHorizontally(this);
    }
    get allowResizeVertically() {
        return this.description.allowResizeVertically(this);
    }

    get rectangle(): Rectangle {
        return Rectangle.fromGeometry(this.position, this.size);
    }
    get clientRectangle(): Rectangle {
        return this.description.getClientRectangle(this);
    }
    get textRectangle(): Rectangle {
        return this.description.getTextRectangle(this);
    }
    get textEditRectangle(): Rectangle {
        return this.description.getTextEditRectangle(this);
    }
    get textAngle(): TextAngle {
        return this.description.getTextAngle();
    }

    toNative(units?: DiagramUnit): INativeShape {
        const item = new NativeShape(this.key, this.dataKey);
        item.type = this.description.key;
        item.text = this.text;
        item.position = this.position.clone();
        item.size = this.size.clone();
        item.attachedConnectorIds = this.attachedConnectors.map(c => c.key);
        item.applyUnits(units);

        item.containerId = this.container ? this.container.key : null;
        item.containerChildItemIds = this.children.map(item => item.key);
        item.containerExpanded = this.expanded;

        return item;
    }
}
