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

/**
 * Style for the markup arr.
 */
export interface MarkupCircleStyle {
    /** Background color for the markup circle */
    backgroundColor: Color4;
}

/**
 * Class representing a markup circle in the markup API.
 */
export class MarkupCircle extends MarkupEntity implements MarkupStyle<MarkupCircleStyle> {
    circlePoint: Vertex3;
    radiusPoint: Vertex3;
    lineThickness: number;

    style = { backgroundColor: new Color4(...Color3.Teal().asArray(), 1.0) };

    /**
     * Do not use. Use {@link MarkupSheet2D.add} instead.
     * @hidden
     */
    public constructor({
        id,
        sheet,
        properties,
        circlePoint,
        radiusPoint,
        lineThickness,
        style
    }: MarkupEntityConstructorArgs & {
        circlePoint: Vertex3;
        radiusPoint: Vertex3;
        lineThickness: number;
        style: MarkupCircleStyle;
    }) {
        super(id, sheet, properties);
        this.style = style;
        this.circlePoint = circlePoint;
        this.radiusPoint = radiusPoint;
        this.lineThickness = lineThickness;
    }

    /**
     * Copy the circle to another markup sheet.
     * @param sheet Markup sheet to copy the circle to.
     * @returns The new markup circle entity.
     */
    copyTo(sheet: MarkupSheet2D): MarkupCircle {
        const deepCopyCirclePoint: Vertex3 = { x: this.circlePoint.x, y: this.circlePoint.y, z: this.circlePoint.z };
        const deepCopyRadiusPoint: Vertex3 = { x: this.radiusPoint.x, y: this.radiusPoint.y, z: this.radiusPoint.z };

        return sheet.add(
            MarkupCircle,
            {
                circlePoint: deepCopyCirclePoint,
                radiusPoint: deepCopyRadiusPoint,
                lineThickness: this.lineThickness,
                style: { backgroundColor: this.style.backgroundColor.clone() }
            },
            new Map<string, MarkupProperty>(this.properties)
        );
    }

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

        super.onUpdate(o);
    }

    copyFrom(circle: MarkupCircle): void {
        this.circlePoint = { x: circle.circlePoint.x, y: circle.circlePoint.y, z: circle.circlePoint.z };
        this.radiusPoint = { x: circle.radiusPoint.x, y: circle.radiusPoint.y, z: circle.radiusPoint.z };
        this.lineThickness = circle.lineThickness;

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

    isEqual(o: MarkupCircle): 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)) &&
            equalsWithEpsilonVertex3(this.circlePoint, o.circlePoint) &&
            equalsWithEpsilonVertex3(this.radiusPoint, o.radiusPoint) &&
            this.lineThickness === o.lineThickness
        );
    }

    clone(): MarkupCircle {
        const circle = new MarkupCircle({
            id: this.id,
            sheet: this.sheet,
            properties: new Map<string, MarkupProperty>(this.properties),
            style: { backgroundColor: this.style.backgroundColor.clone() },
            circlePoint: { x: this.circlePoint.x, y: this.circlePoint.y, z: this.circlePoint.z },
            radiusPoint: { x: this.radiusPoint.x, y: this.radiusPoint.y, z: this.radiusPoint.z },
            lineThickness: this.lineThickness
        });
        return circle;
    }
}
