///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
// All rights reserved.
//
// This software and its documentation and related materials are owned by
// the Alliance. The software may only be incorporated into application
// programs owned by members of the Alliance, subject to a signed
// Membership Agreement and Supplemental Software License Agreement with the
// Alliance. The structure and organization of this software are the valuable
// trade secrets of the Alliance and its suppliers. The software is also
// protected by copyright law and international treaty provisions. Application
// programs incorporating this software must include the following statement
// with their copyright notices:
//
//   This application incorporates Open Design Alliance software pursuant to a
//   license agreement with Open Design Alliance.
//   Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
//   All rights reserved.
//
// By use of this software, its documentation or related materials, you
// acknowledge and accept the above terms.
///////////////////////////////////////////////////////////////////////////////
import * as utils from "./MeasureUtils";

export class MeasureLineItem {
  protected htmlElemStartPoint: HTMLElement;
  protected htmlElemEndPoint: HTMLElement;
  protected htmlElemLine: HTMLElement;
  protected htmlElemTitle: HTMLElement;
  protected startPoint: number[];
  protected endPoint: number[];
  protected unit: string;
  protected scale: number;
  protected size: number;
  protected style: CSSStyleDeclaration;
  protected viewer: any;
  protected moduleInstance: any;
  protected targetElement: HTMLElement;
  protected isFinishDraw: boolean;

  public lineThickness: number;

  constructor(targetElement: HTMLElement, viewer: any, moduleInstance: any) {
    this.htmlElemStartPoint = null;
    this.htmlElemEndPoint = null;
    this.htmlElemLine = null;
    this.htmlElemTitle = null;

    this.startPoint = null;
    this.endPoint = null;

    this.unit = "";
    this.scale = 1.0;
    this.size = 10.0;
    this.lineThickness = 2;

    this.style = {
      border: "2px solid #FFFFFF",
      background: "#009bff",
      color: "white",
      boxShadow: "0 0 10px rgba(0,0,0,0.5)",
    } as CSSStyleDeclaration;

    this.htmlElemStartPoint = utils.createHtmlElementIfNeed(this.htmlElemStartPoint, targetElement, "ruler-start");
    this.htmlElemEndPoint = utils.createHtmlElementIfNeed(this.htmlElemEndPoint, targetElement, "ruler-end");
    this.htmlElemLine = utils.createHtmlElementIfNeed(this.htmlElemLine, targetElement, "ruler-line");
    this.htmlElemTitle = utils.createHtmlElementIfNeed(this.htmlElemTitle, targetElement, "ruler-value");

    this.viewer = viewer;
    this.moduleInstance = moduleInstance;
    this.targetElement = targetElement;
    this.isFinishDraw = false;
  }

  drawMeasureLine(): void {
    const pointSize = this.size;
    const rect = this.moduleInstance.canvas.getBoundingClientRect();

    // draw start point
    if (this.startPoint) {
      this.htmlElemStartPoint = utils.createHtmlElementIfNeed(
        this.htmlElemStartPoint,
        this.targetElement,
        "ruler-start"
      );

      const pScreenStart = utils.worldToScreen(this.startPoint, this.moduleInstance, this.viewer);
      if (utils.isInsideRect(pScreenStart, rect.width, rect.height)) {
        this.htmlElemStartPoint.style.display = "block";
        this.htmlElemStartPoint.style.cursor = "pointer";
        this.htmlElemStartPoint.style.position = "absolute";
        this.htmlElemStartPoint.style.top = `${pScreenStart.y - pointSize / 2}px`;
        this.htmlElemStartPoint.style.left = `${pScreenStart.x - pointSize / 2}px`;
        this.htmlElemStartPoint.style.borderRadius = `${pointSize}px`;
        this.htmlElemStartPoint.style.border = this.style.border;
        this.htmlElemStartPoint.style.background = this.style.background;
        this.htmlElemStartPoint.style.zIndex = "2";
        this.htmlElemStartPoint.style.width = `${pointSize}px`;
        this.htmlElemStartPoint.style.height = `${pointSize}px`;
        this.htmlElemStartPoint.style.boxShadow = this.style.boxShadow;
      } else {
        this.htmlElemStartPoint.style.display = "none";
      }
    }

    // draw end point
    if (this.endPoint && this.isFinishDraw) {
      this.htmlElemEndPoint = utils.createHtmlElementIfNeed(this.htmlElemEndPoint, this.targetElement, "ruler-end");

      const pScreenEnd = utils.worldToScreen(this.endPoint, this.moduleInstance, this.viewer);

      if (utils.isInsideRect(pScreenEnd, rect.width, rect.height)) {
        this.htmlElemEndPoint.style.display = "block";
        this.htmlElemEndPoint.style.cursor = "pointer";
        this.htmlElemEndPoint.style.position = "absolute";
        this.htmlElemEndPoint.style.top = `${pScreenEnd.y - pointSize / 2}px`;
        this.htmlElemEndPoint.style.left = `${pScreenEnd.x - pointSize / 2}px`;
        this.htmlElemEndPoint.style.borderRadius = `${pointSize}px`;
        this.htmlElemEndPoint.style.border = this.style.border;
        this.htmlElemEndPoint.style.background = this.style.background;
        this.htmlElemEndPoint.style.zIndex = "2";
        this.htmlElemEndPoint.style.width = `${pointSize}px`;
        this.htmlElemEndPoint.style.height = `${pointSize}px`;
        this.htmlElemEndPoint.style.boxShadow = this.style.boxShadow;
      } else {
        this.htmlElemEndPoint.style.display = "none";
      }
    }

    if (this.endPoint && this.startPoint) {
      const point1 = utils.worldToScreen(this.startPoint, this.moduleInstance, this.viewer);
      const point2 = utils.worldToScreen(this.endPoint, this.moduleInstance, this.viewer);

      const { p1, p2, angle, width } = utils.getDataForDrawLineWithFixed(point1, point2, rect.width, rect.height);

      const dx = p2.x - p1.x;
      const dy = p2.y - p1.y;

      const height = this.lineThickness;
      if (utils.isInsideRect(p1, rect.width, rect.height) && utils.isInsideRect(p2, rect.width, rect.height)) {
        this.htmlElemLine = utils.createHtmlElementIfNeed(this.htmlElemLine, this.targetElement, "ruler-line");
        this.htmlElemLine.style.display = "block";
        this.htmlElemLine.style.cursor = "pointer";
        this.htmlElemLine.style.position = "absolute";
        this.htmlElemLine.style.top = `${p1.y}px`;
        this.htmlElemLine.style.left = `${p1.x}px`;
        this.htmlElemLine.style.width = `${width}px`;
        this.htmlElemLine.style.transform = `rotate(${angle}deg)`;
        this.htmlElemLine.style.transformOrigin = `0px ${height / 2}px`;
        this.htmlElemLine.style.boxShadow = this.style.boxShadow;
        this.htmlElemLine.style.border = "none";
        this.htmlElemLine.style.background = this.style.background;
        this.htmlElemLine.style.zIndex = "1";
        this.htmlElemLine.style.height = `${height}px`;

        const distance = `${this.getDistance()} ${this.unit}`;

        const pX = p1.x + dx / 2;
        const pY = p1.y + dy / 2;

        const widthTitle = distance.length * 10;

        this.htmlElemTitle = utils.createHtmlElementIfNeed(this.htmlElemTitle, this.targetElement, "ruler-value");
        this.htmlElemTitle.style.display = "block";
        this.htmlElemTitle.style.cursor = "pointer";
        this.htmlElemTitle.style.font = "10px";
        this.htmlElemTitle.style.color = "white";
        this.htmlElemTitle.style.position = "Absolute";
        this.htmlElemTitle.style.top = `${pY}px`;
        this.htmlElemTitle.style.left = `${pX - widthTitle / 2}px`;
        this.htmlElemTitle.style.width = `${widthTitle}px`;
        this.htmlElemTitle.style.transformOrigin = "0px 0px";
        this.htmlElemTitle.style.borderRadius = "5px";
        this.htmlElemTitle.style.boxShadow = this.style.boxShadow;
        this.htmlElemTitle.style.border = "none";
        this.htmlElemTitle.style.background = this.style.background;
        this.htmlElemTitle.style.zIndex = "3";
        this.htmlElemTitle.style.padding = "2px";
        this.htmlElemTitle.style.textAlign = "center";
        this.htmlElemTitle.innerHTML = `${distance}`;
      } else {
        this.htmlElemLine.style.display = "none";
        this.htmlElemTitle.style.display = "none";
      }
    }
  }

  getDistance(): number {
    let distance = utils.getDistance(this.startPoint, this.endPoint, this.moduleInstance);
    if (Math.abs(this.scale - 1.0) > 10e-5) {
      distance = (distance / this.scale).toFixed(2);
    }

    return distance;
  }

  setStartPoint(gePoint: number[]): void {
    this.startPoint = gePoint;
    this.drawMeasureLine();
  }

  setEndPoint(gePoint: number[], isFinish: boolean): void {
    this.isFinishDraw = isFinish === undefined ? true : isFinish;
    this.endPoint = gePoint;
    this.drawMeasureLine();
  }

  update(): void {
    this.drawMeasureLine();
  }

  setSize(size: number): void {
    this.size = size;
    this.drawMeasureLine();
  }

  clear(): void {
    this.endPoint = null;
    this.startPoint = null;
    this.htmlElemStartPoint = utils.destroyHtmlElement(this.htmlElemStartPoint, this.targetElement);
    this.htmlElemEndPoint = utils.destroyHtmlElement(this.htmlElemEndPoint, this.targetElement);
    this.htmlElemLine = utils.destroyHtmlElement(this.htmlElemLine, this.targetElement);
    this.htmlElemTitle = utils.destroyHtmlElement(this.htmlElemTitle, this.targetElement);
  }

  setUnit(unit: string): void {
    this.unit = unit;
    this.drawMeasureLine();
  }

  setConversionFactor(scale: number): void {
    this.scale = scale;
    this.drawMeasureLine();
  }

  setStyle(style: CSSStyleDeclaration): void {
    this.style = style;
    this.drawMeasureLine();
  }

  setSelectionReactor(reactor: any): void {
    utils.onSetCallback(this.htmlElemStartPoint, reactor ? reactor.onStartPoint : null);
    utils.onSetCallback(this.htmlElemEndPoint, reactor ? reactor.onEndPoint : null);
    utils.onSetCallback(this.htmlElemTitle, reactor ? reactor.onTitle : null);
  }

  setSelectability(enable: boolean): void {
    utils.onSetSelectivity(this.htmlElemStartPoint, enable);
    utils.onSetSelectivity(this.htmlElemEndPoint, enable);
    utils.onSetSelectivity(this.htmlElemLine, enable);
    utils.onSetSelectivity(this.htmlElemTitle, enable);
  }
}
