import { MarkupEntity, MarkupEntityConstructorArgs } from './MarkupEntity';
import { MarkupSheet2D } from './MarkupSheet2D';
import { Vertex3 } from '../../math';
import { Color3, Color4 } from '../../loader/babylonjs-import';
import { MarkupProperty, MarkupStyle } from '../MarkupEntityTypes';
import { equalsWithEpsilonVertex3 } from '../../math/index';
import { MergableObjectEventArgs, MergableObjectEventSource } from '../../MergableSet';

/**
 * Style for the markup line.
 */
export interface MarkupLineStyle {
    /** Background color for the markup line */
    backgroundColor: Color4;
}

/**
 * Class representing a markup line in the markup API.
 */
export class MarkupLine extends MarkupEntity implements MarkupStyle<MarkupLineStyle> {
    linePoints: Vertex3[];
    style = { backgroundColor: new Color4(...Color3.Teal().asArray(), 1.0) };
    thickness: number;

    /**
     * Do not use. Use {@link MarkupSheet2D.add} instead.
     * @hidden
     */
    public constructor({
        id,
        sheet,
        properties,
        linePoints,
        style,
        thickness
    }: MarkupEntityConstructorArgs & { linePoints: Vertex3[]; style: MarkupLineStyle; thickness: number }) {
        super(id, sheet, properties);
        this.style = style;
        this.thickness = thickness;
        this.linePoints = linePoints;
    }

    /**
     * Copy the line to another markup sheet.
     * @param sheet Markup sheet to copy the line to.
     * @returns The new markup line entity.
     */
    copyTo(sheet: MarkupSheet2D): MarkupLine {
        const deepCopyPoints: Vertex3[] = [];

        const deepCopyLinePoints: Vertex3[] = [];
        for (let i = 0; i < this.linePoints.length; i++) {
            const originalLinePoint = this.linePoints[i];

            deepCopyLinePoints.push({ x: originalLinePoint.x, y: originalLinePoint.y, z: originalLinePoint.z });
        }
        return sheet.add(
            MarkupLine,
            {
                linePoints: deepCopyLinePoints,
                points: deepCopyPoints,
                style: { backgroundColor: this.style.backgroundColor.clone() },
                thickness: this.thickness
            },
            new Map<string, MarkupProperty>(this.properties)
        );
    }

    onUpdate(o: MergableObjectEventArgs<MarkupLine>): void {
        if (o.eventSource === MergableObjectEventSource.Remote && !o.conflictReason && o.remote) {
            this.copyFrom(o.remote);
        }

        super.onUpdate(o);
    }

    copyFrom(line: MarkupLine): void {
        this.linePoints = line.linePoints.map((p) => {
            return { ...p };
        });

        this.properties.clear();
        for (const [key, value] of line.properties) {
            this.properties.set(key, value);
        }
    }

    isEqual(o: MarkupLine): boolean {
        return (
            o.id === this.id &&
            o.sheet.isEqual(this.sheet) &&
            o.properties.size === this.properties.size &&
            [...this.properties].every(([key, value]) => value === o.properties.get(key)) &&
            this.linePoints.length === o.linePoints.length &&
            [...this.linePoints].every((point, idx) => equalsWithEpsilonVertex3(point, o.linePoints[idx]))
        );
    }

    clone(): MarkupLine {
        const deepCopyLinePoints: Vertex3[] = [];
        for (let i = 0; i < this.linePoints.length; i++) {
            const originalLinePoint = this.linePoints[i];

            deepCopyLinePoints.push({ x: originalLinePoint.x, y: originalLinePoint.y, z: originalLinePoint.z });
        }

        const line = new MarkupLine({
            id: this.id,
            sheet: this.sheet,
            properties: new Map<string, MarkupProperty>(this.properties),
            style: { backgroundColor: this.style.backgroundColor.clone() },
            thickness: this.thickness,
            linePoints: deepCopyLinePoints
        });
        return line;
    }
}
