import { UnitConverter } from "@devexpress/utils/lib/class/unit-converter";
import { Size } from "@devexpress/utils/lib/geometry/size";

import { DiagramLocalizationService } from "../../../LocalizationService";
import { TextOwner } from "../../../Render/Measurer/ITextMeasurer";
import { SvgPrimitive } from "../../../Render/Primitives/Primitive";
import { RectanglePrimitive } from "../../../Render/Primitives/RectaglePrimitive";
import { TextAngle, TextPrimitive } from "../../../Render/Primitives/TextPrimitive";
import { EventDispatcher } from "../../../Utils";
import { Rectangle } from "@devexpress/utils/lib/geometry/rectangle";
import { ConnectionPoint } from "../../ConnectionPoint";
import { ConnectionPointSide, DiagramItem } from "../../DiagramItem";
import { Shape } from "../Shape";
import { ShapeParameterPoint } from "../ShapeParameterPoint";
import { ShapeParameters } from "../ShapeParameters";
import { Point } from "@devexpress/utils/lib/geometry/point";

export const ShapeDefaultDimension = 1440;
export const ShapeMinDimension = 360;
export const ShapeDefaultSize = new Size(ShapeDefaultDimension, ShapeDefaultDimension);
export const ShapeTextPadding = UnitConverter.pixelsToTwips(10);

export abstract class ShapeDescription {
    protected connectionPoints: ConnectionPoint[];
    protected connectionPointsWhileSelected: ConnectionPoint[];

    static readonly textSpacing = UnitConverter.pixelsToTwips(2);

    onChanged: EventDispatcher<IShapeDescriptionChangesListener> = new EventDispatcher();

    constructor(public defaultSize: Size = ShapeDefaultSize.clone(), protected hasDefaultText?: boolean) {
        this.connectionPoints = this.createConnectionPoints();
    }

    abstract get key(): string;

    get enableText() { return true; }
    get allowEditText() { return true; }

    get enableImage() { return false; }
    get allowEditImage() { return true; }

    get hasTemplate() { return false; }

    get enableChildren() { return false; }

    get minWidth() { return undefined; }
    get minHeight() { return undefined; }
    get maxWidth() { return undefined; }
    get maxHeight() { return undefined; }

    get toolboxSize() { return this.defaultSize; }

    abstract get keepRatioOnAutoSize(): boolean;

    getTitle() {
        return DiagramLocalizationService.shapeTexts[this.key];
    }
    getDefaultText() {
        return this.hasDefaultText ? DiagramLocalizationService.shapeTexts[this.key] : "";
    }
    getDefaultImageUrl() {
        return "";
    }
    getConnectionPoints(): ConnectionPoint[] {
        return this.connectionPoints;
    }
    protected createConnectionPoints(): ConnectionPoint[] {
        return [
            new ConnectionPoint(0.5, 0, ConnectionPointSide.North),
            new ConnectionPoint(1, 0.5, ConnectionPointSide.East),
            new ConnectionPoint(0.5, 1, ConnectionPointSide.South),
            new ConnectionPoint(0, 0.5, ConnectionPointSide.West)
        ];
    }
    processConnectionPoint(shape: Shape, point: ConnectionPoint) {
    }
    getConnectionPointIndexForItem(item: DiagramItem, connectionPointIndex: number): number {
        return connectionPointIndex;
    }
    getConnectionPointIndexForSide(side: ConnectionPointSide): number {
        return side;
    }
    static getConnectionPointSideByGeometry(point: Point): ConnectionPointSide {
        if(point.x >= point.y && (point.x > 0 || point.y > 0)) {
            if(point.x < 0.5 || (1 - point.x) >= point.y)
                return ConnectionPointSide.North;
            return ConnectionPointSide.East;
        }
        else {
            if(point.x > 0.5 || (1 - point.x) <= point.y)
                return ConnectionPointSide.South;
            return ConnectionPointSide.West;
        }
    }
    createParameters(parameters: ShapeParameters) {
    }
    normalizeParameters(shape: Shape, parameters: ShapeParameters) {
    }
    modifyParameters(shape: Shape, parameters: ShapeParameters, deltaX: number, deltaY: number) {
        throw Error("Not implemented");
    }
    changeParameterValue(parameters: ShapeParameters, key: string, change: (p) => number) {
        const p = parameters.get(key);
        p.value = change(p);
    }
    getParameterPoints(shape: Shape): ShapeParameterPoint[] {
        return [];
    }
    getExpandedSize(shape: Shape): Size {
        return shape.size;
    }
    getCollapsedSize(shape: Shape): Size {
        return shape.size;
    }
    getToolboxHeightToWidthRatio(width: number, height: number): number {
        return height / width;
    }
    allowResizeHorizontally(_shape: Shape): boolean {
        return true;
    }
    allowResizeVertically(_shape: Shape): boolean {
        return true;
    }

    createPrimitives(shape: Shape, instanceId: string, forToolbox?: boolean): SvgPrimitive<SVGGraphicsElement>[] {
        let primitives: SvgPrimitive<SVGGraphicsElement>[] = [];
        primitives = primitives.concat(this.createShapePrimitives(shape, forToolbox));
        if(this.enableImage)
            primitives = primitives.concat(this.createImagePrimitives(shape, forToolbox));
        if(this.enableText)
            primitives = primitives.concat(this.createTextPrimitives(shape, forToolbox));
        return primitives;
    }
    abstract createShapePrimitives(shape: Shape, forToolbox?: boolean): SvgPrimitive<SVGGraphicsElement>[];

    createImagePrimitives(_shape: Shape, _forToolbox: boolean): SvgPrimitive<SVGGraphicsElement>[] {
        return [];
    }
    createTextPrimitives(shape: Shape, forToolbox?: boolean): SvgPrimitive<SVGGraphicsElement>[] {
        if(shape.text === undefined || shape.text === "") return [];

        const rect = this.getTextRectangle(shape);
        return [
            new TextPrimitive(rect.x, rect.y, shape.text, TextOwner.Shape, rect.width, rect.height, ShapeDescription.textSpacing,
                shape.styleText, false, this.getTextClipPathId(forToolbox), undefined, this.getTextAngle()
            )
        ];
    }
    getTextClipPathId(_forToolbox?: boolean): string {
        return undefined;
    }
    getTextAngle(): TextAngle {
        return TextAngle.Angle0deg;
    }
    getClientRectangle(shape: Shape): Rectangle {
        return shape.rectangle;
    }
    getTextEditRectangle(shape: Shape): Rectangle {
        return this.getTextRectangle(shape);
    }
    createSelectorPrimitives(shape: Shape): SvgPrimitive<SVGGraphicsElement>[] {
        return [
            new RectanglePrimitive(shape.position.x, shape.position.y, shape.size.width, shape.size.height, null, "selector")
        ];
    }
    protected raiseShapeDescriptionChanged(description: ShapeDescription): void {
        this.onChanged.raise1(l => l.notifyShapeDescriptionChanged(description));
    }
    abstract getTextRectangle(shape: Shape): Rectangle;
    abstract getSizeByText(textSize: Size, shape: Shape): Size;
}

export interface IShapeDescriptionChangesListener {
    notifyShapeDescriptionChanged(description: ShapeDescription);
}
