// Tooth condition classes

class Polygon {
  name = "Polygon";
  pos?: string;
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = options;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length <= 0) {
      // console.warn(
      //   "Polygon render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    ctx.fillStyle = this.options.fillStyle;
    ctx.beginPath();

    const vertices = [...this.vertices];
    const fpos = vertices.shift();
    if (!fpos || fpos.x === undefined || fpos.y === undefined) {
      // console.warn(
      //   "Polygon render: first vertex missing required properties",
      //   fpos
      // );
      return;
    }
    ctx.moveTo(fpos.x, fpos.y);

    while (vertices.length > 0) {
      const pos = vertices.shift();
      if (pos && pos.x !== undefined && pos.y !== undefined) {
        ctx.lineTo(pos.x, pos.y);
      }
    }
    ctx.lineTo(fpos.x, fpos.y);
    ctx.closePath();
    ctx.fill();
  }
}

class AMF {
  name = "AMF";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { fillStyle: "rgba(255, 0, 0, 0.7)", ...options };
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length <= 0) {
      // console.warn(
      //   "AMF render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    // console.log("Rendering AMF with fillStyle:", this.options.fillStyle);
    ctx.fillStyle = this.options.fillStyle;
    ctx.beginPath();

    const vertices = [...this.vertices];
    const fpos = vertices.shift();
    if (!fpos || fpos.x === undefined || fpos.y === undefined) {
      // console.warn(
      //   "AMF render: first vertex missing required properties",
      //   fpos
      // );
      return;
    }
    ctx.moveTo(fpos.x, fpos.y);

    while (vertices.length > 0) {
      const pos = vertices.shift();
      if (pos && pos.x !== undefined && pos.y !== undefined) {
        ctx.lineTo(pos.x, pos.y);
      }
    }
    ctx.lineTo(fpos.x, fpos.y);
    ctx.closePath();
    ctx.fill();
  }
}

class COF {
  name = "COF";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { fillStyle: "rgba(0, 255, 0, 0.7)", ...options };
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length <= 0) {
      // console.warn(
      //   "COF render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    // console.log("Rendering COF with fillStyle:", this.options.fillStyle);
    ctx.fillStyle = this.options.fillStyle;
    ctx.beginPath();

    const vertices = [...this.vertices];
    const fpos = vertices.shift();
    if (!fpos || fpos.x === undefined || fpos.y === undefined) {
      // console.warn(
      //   "COF render: first vertex missing required properties",
      //   fpos
      // );
      return;
    }
    ctx.moveTo(fpos.x, fpos.y);

    while (vertices.length > 0) {
      const pos = vertices.shift();
      if (pos && pos.x !== undefined && pos.y !== undefined) {
        ctx.lineTo(pos.x, pos.y);
      }
    }
    ctx.lineTo(fpos.x, fpos.y);
    ctx.closePath();
    ctx.fill();
  }
}

class FIS {
  name = "FIS";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { fillStyle: "rgba(255, 0, 255, 0.7)", ...options };
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length <= 0) {
      // console.warn(
      //   "FIS render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    // console.log("Rendering FIS with fillStyle:", this.options.fillStyle);
    ctx.fillStyle = this.options.fillStyle;
    ctx.beginPath();

    const vertices = [...this.vertices];
    const fpos = vertices.shift();
    if (!fpos || fpos.x === undefined || fpos.y === undefined) {
      // console.warn(
      //   "FIS render: first vertex missing required properties",
      //   fpos
      // );
      return;
    }
    ctx.moveTo(fpos.x, fpos.y);

    while (vertices.length > 0) {
      const pos = vertices.shift();
      if (pos && pos.x !== undefined && pos.y !== undefined) {
        ctx.lineTo(pos.x, pos.y);
      }
    }
    ctx.lineTo(fpos.x, fpos.y);
    ctx.closePath();
    ctx.fill();
  }
}

class NVT {
  name = "NVT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#303030ff", height: 25, ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        // console.warn(
        //   "NVT render: vertices array too short or undefined",
        //   this.vertices
        // );
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].x === undefined ||
        this.vertices[0].y === undefined ||
        this.vertices[1].y === undefined
      ) {
        // console.warn(
        //   "NVT render: vertices missing required properties",
        //   this.vertices
        // );
        return;
      }
      const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
      const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
      const y1 = parseFloat(this.vertices[0].y.toString()) + 1;
      const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
      const size = x2 - x1;
      const height = parseFloat(this.options.height.toString());

      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = 4;
      ctx.beginPath();
      ctx.moveTo(x1 + size / 4, y2);
      ctx.lineTo(x1 + size / 2, y2 + height);
      ctx.lineTo(x2 - size / 4, y2);
      ctx.closePath();
      ctx.stroke();
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        // console.warn(
        //   "NVT render: vertices array too short or undefined",
        //   this.vertices
        // );
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].x === undefined ||
        this.vertices[0].y === undefined ||
        this.vertices[1].y === undefined
      ) {
        // console.warn(
        //   "NVT render: vertices missing required properties",
        //   this.vertices
        // );
        return;
      }
      const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
      const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
      const y1 = parseFloat(this.vertices[0].y.toString()) - 1;
      const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
      const size = x2 - x1;
      const height = parseFloat(this.options.height.toString());

      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = 4;
      ctx.beginPath();
      ctx.moveTo(x1 + size / 4, y1);
      ctx.lineTo(x1 + size / 2, y1 - height);
      ctx.lineTo(x2 - size / 4, y1);
      ctx.closePath();
      ctx.stroke();
    }
  }
}

// RCT class (Perawatan Saluran Akar)
class RCT {
  name = "RCT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = {
      strokeStyle: "#303030ff",
      fillStyle: "#303030ff",
      height: 25,
      ...options
    };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }
  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        // // console.warn(
        //   "RCT render: vertices array too short or undefined",
        //   this.vertices
        // );
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].x === undefined ||
        this.vertices[0].y === undefined ||
        this.vertices[1].y === undefined
      ) {
        // // console.warn(
        //   "RCT render: vertices missing required properties",
        //   this.vertices
        // );
        return;
      }
      const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
      const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
      const y1 = parseFloat(this.vertices[0].y.toString()) + 1;
      const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
      const size = x2 - x1;
      const height = parseFloat(this.options.height);

      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.beginPath();
      ctx.moveTo(x1 + size / 4, y2);
      ctx.lineTo(x1 + size / 2, y2 + height);
      ctx.lineTo(x2 - size / 4, y2);
      ctx.closePath();
      ctx.fill();
      ctx.stroke();
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        // // console.warn(
        //   "RCT render: vertices array too short or undefined",
        //   this.vertices
        // );
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].x === undefined ||
        this.vertices[0].y === undefined ||
        this.vertices[1].y === undefined
      ) {
        // // console.warn(
        //   "RCT render: vertices missing required properties",
        //   this.vertices
        // );
        return;
      }
      const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
      const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
      const y1 = parseFloat(this.vertices[0].y.toString()) - 1;
      const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
      const size = x2 - x1;
      const height = parseFloat(this.options.height);

      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.beginPath();
      ctx.moveTo(x1 + size / 4, y1);
      ctx.lineTo(x1 + size / 2, y1 - height);
      ctx.lineTo(x2 - size / 4, y1);
      ctx.closePath();
      ctx.fill();
      ctx.stroke();
    }
  }
}

class NON {
  name = "NON";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = {
      fillStyle: "#303030ff",
      fontsize: 14,
      offsetX: 25,
      offsetY: 5,
      ...options
    };
    // Extract surface from options if provided
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    // Log tooth position information
    // console.log("IPX Tooth Information:", {
    //   position: this.pos || "unknown",
    //   row: this.row || "unknown",
    //   surface: this.surface || "unknown"
    // });
    // Different validation logic based on row
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
      // For rows 3 and 4, check first and second vertices
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[1].y.toString()) + (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize);

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "top";
      ctx.textAlign = "left";
      ctx.fillText("NON", x, y);
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length <= 0) {
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[0].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[0].y.toString()) - (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize.toString());

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "bottom";
      ctx.textAlign = "left";
      ctx.fillText("NON", x, y);
    }
  }
}

class UNE {
  name = "UNE";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = {
      fillStyle: "#303030ff",
      fontsize: 14,
      offsetX: 25,
      offsetY: 5,
      ...options
    };
    // Extract surface from options if provided
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    // Log tooth position information
    // console.log("IPX Tooth Information:", {
    //   position: this.pos || "unknown",
    //   row: this.row || "unknown",
    //   surface: this.surface || "unknown"
    // });
    // Different validation logic based on row
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
      // For rows 3 and 4, check first and second vertices
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[1].y.toString()) + (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize);

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "top";
      ctx.textAlign = "left";
      ctx.fillText("UNE", x, y);
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length <= 0) {
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[0].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[0].y.toString()) - (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize.toString());

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "bottom";
      ctx.textAlign = "left";
      ctx.fillText("UNE", x, y);
    }
  }
}

class PRE {
  name = "PRE";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = {
      fillStyle: "#303030ff",
      fontsize: 14,
      offsetX: 25,
      offsetY: 5,
      ...options
    };
    // Extract surface from options if provided
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    // Log tooth position information
    // console.log("IPX Tooth Information:", {
    //   position: this.pos || "unknown",
    //   row: this.row || "unknown",
    //   surface: this.surface || "unknown"
    // });
    // Different validation logic based on row
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
      // For rows 3 and 4, check first and second vertices
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[1].y.toString()) + (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize);

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "top";
      ctx.textAlign = "left";
      ctx.fillText("PRE", x, y);
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length <= 0) {
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[0].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[0].y.toString()) - (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize.toString());

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "bottom";
      ctx.textAlign = "left";
      ctx.fillText("PRE", x, y);
    }
  }
}

class ANO {
  name = "ANO";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = {
      fillStyle: "#303030ff",
      fontsize: 14,
      offsetX: 25,
      offsetY: 5,
      ...options
    };
    // Extract surface from options if provided
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    // Log tooth position information
    // console.log("IPX Tooth Information:", {
    //   position: this.pos || "unknown",
    //   row: this.row || "unknown",
    //   surface: this.surface || "unknown"
    // });
    // Different validation logic based on row
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
      // For rows 3 and 4, check first and second vertices
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[1].y.toString()) + (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize);

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "top";
      ctx.textAlign = "left";
      ctx.fillText("ANO", x, y);
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length <= 0) {
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[0].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[0].y.toString()) - (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize.toString());

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "bottom";
      ctx.textAlign = "left";
      ctx.fillText("ANO", x, y);
    }
  }
}

class CARIES {
  name = "CARIES";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#303030ff", ...options };
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length <= 0) {
      // console.warn(
      //   "CARIES render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    ctx.strokeStyle = this.options.strokeStyle;
    ctx.lineWidth = 4;
    ctx.fillStyle = "rgba(0, 0, 0, 0.1)"; // Subtle fill to soften appearance
    ctx.beginPath();

    // const vertices = [...this.vertices];
    const inset = 0;
    const vertices = this.vertices.map((v) => {
      if (v == null || v.x === undefined || v.y === undefined) {
        // console.warn("CARIES render: vertex missing required properties", v);
        return { x: 0, y: 0 };
      }
      return {
        x: v.x + inset,
        y: v.y + inset
      };
    });
    const fpos = vertices.shift();
    if (!fpos) {
      // console.warn(
      //   "CARIES render: first vertex missing required properties",
      //   fpos
      // );
      return;
    }
    ctx.moveTo(fpos.x, fpos.y - 2);

    while (vertices.length > 0) {
      const pos = vertices.shift();

      if (pos && pos.x !== undefined && pos.y !== undefined) {
        ctx.lineTo(pos.x, pos.y - 2);
      }
    }
    ctx.lineTo(fpos.x, fpos.y - 2);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
  }
}

class CFR {
  name = "CFR";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { fillStyle: "#303030ff", ...options };
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length < 2) {
      // console.warn(
      //   "CFR render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    if (
      this.vertices[0] == null ||
      this.vertices[1] == null ||
      this.vertices[0].x === undefined ||
      this.vertices[1].x === undefined ||
      this.vertices[0].y === undefined ||
      this.vertices[1].y === undefined
    ) {
      // console.warn(
      //   "CFR render: vertices missing required properties",
      //   this.vertices
      // );
      return;
    }
    const x1 = parseFloat(this.vertices[0].x.toString());
    const y1 = parseFloat(this.vertices[0].y.toString());
    const x2 = parseFloat(this.vertices[1].x.toString());
    const y2 = parseFloat(this.vertices[1].y.toString());
    const boxSize = x2 - x1;
    const fontsize = parseInt(boxSize.toString());

    const x = x1 + boxSize / 2;
    const y = y1 + boxSize / 2;

    ctx.fillStyle = "#000";
    ctx.font = "bold " + fontsize + "px Algerian";
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.fillText("#", x, y);
  }
}

class FMC {
  name = "FMC";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#303030ff", ...options };
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length < 2) {
      // console.warn(
      //   "FMC render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    if (
      this.vertices[0] == null ||
      this.vertices[1] == null ||
      this.vertices[0].x === undefined ||
      this.vertices[1].x === undefined ||
      this.vertices[0].y === undefined ||
      this.vertices[1].y === undefined
    ) {
      // console.warn(
      //   "FMC render: vertices missing required properties",
      //   this.vertices
      // );
      return;
    }
    const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
    const y1 = parseFloat(this.vertices[0].y.toString()) + 1;
    const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
    const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
    const vertices = [
      { x: x1, y: y1 },
      { x: x2, y: y1 },
      { x: x2, y: y2 },
      { x: x1, y: y2 }
    ];

    ctx.strokeStyle = this.options.strokeStyle;
    ctx.lineWidth = 6;
    ctx.beginPath();

    const fpos = vertices.shift();
    if (!fpos) {
      // console.warn(
      //   "FMC render: first vertex missing required properties",
      //   fpos
      // );
      return;
    }
    ctx.moveTo(fpos.x, fpos.y);

    while (vertices.length > 0) {
      const pos = vertices.shift();
      if (pos && pos.x !== undefined && pos.y !== undefined) {
        ctx.lineTo(pos.x, pos.y);
      }
    }
    ctx.lineTo(fpos.x, fpos.y);
    ctx.closePath();
    ctx.stroke();
  }
}

class POC {
  name = "POC";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#303030ff", ...options };
  }

  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length < 2) {
      // console.warn(
      //   "POC render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    if (
      this.vertices[0] == null ||
      this.vertices[1] == null ||
      this.vertices[0].x === undefined ||
      this.vertices[1].x === undefined ||
      this.vertices[0].y === undefined ||
      this.vertices[1].y === undefined
    ) {
      // console.warn(
      //   "POC render: vertices missing required properties",
      //   this.vertices
      // );
      return;
    }
    const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
    const y1 = parseFloat(this.vertices[0].y.toString()) + 1;
    const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
    const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
    const vertices = [
      { x: x1, y: y1 },
      { x: x2, y: y1 },
      { x: x2, y: y2 },
      { x: x1, y: y2 }
    ];

    ctx.strokeStyle = this.options.strokeStyle;
    ctx.lineWidth = 6;
    ctx.beginPath();

    const fpos = vertices.shift();
    if (!fpos) {
      // console.warn(
      //   "POC render: first vertex missing required properties",
      //   fpos
      // );
      return;
    }
    ctx.moveTo(fpos.x, fpos.y);

    while (vertices.length > 0) {
      const pos = vertices.shift();
      if (pos && pos.x !== undefined && pos.y !== undefined) {
        ctx.lineTo(pos.x, pos.y);
      }
    }
    ctx.lineTo(fpos.x, fpos.y);
    ctx.closePath();
    ctx.stroke();

    // Draw Lines
    ctx.lineWidth = 1;
    for (let xpos = x1; xpos < x2; xpos += (x2 - x1) / 15) {
      xpos = Math.min(xpos, x2);

      ctx.beginPath();
      ctx.moveTo(xpos, y1);
      ctx.lineTo(xpos, y2);
      ctx.stroke();
    }
  }
}
class RRX {
  name = "RRX";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#303030ff", ...options };
  }
  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length < 2) {
      // console.warn(
      //   "RRX render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    if (
      this.vertices[0] == null ||
      this.vertices[1] == null ||
      this.vertices[0].x === undefined ||
      this.vertices[1].x === undefined ||
      this.vertices[0].y === undefined ||
      this.vertices[1].y === undefined
    ) {
      // console.warn(
      //   "RRX render: vertices missing required properties",
      //   this.vertices
      // );
      return;
    }
    const x1 = parseFloat(this.vertices[0].x.toString());
    const y1 = parseFloat(this.vertices[0].y.toString()) + 1;
    const x2 = parseFloat(this.vertices[1].x.toString());
    const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
    const bigBoxSize = x2 - x1;
    const smallBoxSize = bigBoxSize / 2;
    const lines = [
      {
        x1: x1 + smallBoxSize / 3,
        y1: y1 - smallBoxSize / 2,
        x2: x1 + smallBoxSize,
        y2: y2 + smallBoxSize / 2
      },
      {
        x1: x1 + smallBoxSize,
        y1: y2 + smallBoxSize / 2,
        x2: x1 + smallBoxSize * 2,
        y2: y1 - smallBoxSize
      }
    ];

    ctx.strokeStyle = this.options.strokeStyle;
    ctx.lineWidth = 4;
    for (const line of lines) {
      ctx.beginPath();
      ctx.moveTo(line.x1, line.y1);
      ctx.lineTo(line.x2, line.y2);
      ctx.stroke();
    }
  }
}

class MIS {
  name = "MIS";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#303030ff", ...options };
  }
  render(ctx: CanvasRenderingContext2D) {
    if (!this.vertices || this.vertices.length < 2) {
      // console.warn(
      //   "MIS render: vertices array too short or undefined",
      //   this.vertices
      // );
      return;
    }
    if (
      this.vertices[0] == null ||
      this.vertices[1] == null ||
      this.vertices[0].x === undefined ||
      this.vertices[1].x === undefined ||
      this.vertices[0].y === undefined ||
      this.vertices[1].y === undefined
    ) {
      // console.warn(
      //   "MIS render: vertices missing required properties",
      //   this.vertices
      // );
      return;
    }
    const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
    const y1 = parseFloat(this.vertices[0].y.toString()) + 1;
    const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
    const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
    const bigBoxSize = x2 - x1;
    const smallBoxSize = bigBoxSize / 2;
    const lines = [
      {
        x1: x1 + smallBoxSize * 0.5,
        y1: y1 - smallBoxSize / 2,
        x2: x1 + smallBoxSize * 1.5,
        y2: y2 + smallBoxSize / 2
      },
      {
        x1: x1 + smallBoxSize * 1.5,
        y1: y1 - smallBoxSize / 2,
        x2: x1 + smallBoxSize * 0.5,
        y2: y2 + smallBoxSize / 2
      }
    ];

    ctx.strokeStyle = this.options.strokeStyle;
    ctx.lineWidth = 4;
    for (const line of lines) {
      ctx.beginPath();
      ctx.moveTo(line.x1, line.y1);
      ctx.lineTo(line.x2, line.y2);
      ctx.stroke();
    }
  }
}

// IPX class (Implant + Porcelain crown)
class IPX {
  name = "IPX";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = {
      fillStyle: "#303030ff",
      fontsize: 14,
      offsetX: 25,
      offsetY: 5,
      ...options
    };
    // Extract surface from options if provided
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    // Log tooth position information
    // console.log("IPX Tooth Information:", {
    //   position: this.pos || "unknown",
    //   row: this.row || "unknown",
    //   surface: this.surface || "unknown"
    // });
    // Different validation logic based on row
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
      // For rows 3 and 4, check first and second vertices
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[1].y.toString()) + (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize);

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "top";
      ctx.textAlign = "left";
      ctx.fillText("IPX", x, y);
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length <= 0) {
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[0].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[0].y.toString()) - (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize.toString());

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "bottom";
      ctx.textAlign = "left";
      ctx.fillText("IPX", x, y);
    }
  }
}

// FRM_ACR class (Partial Denture/ Full Denture)
class FRM_ACR {
  name = "FRM_ACR";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = {
      fillStyle: "#303030ff",
      fontsize: 14,
      offsetX: 10,
      offsetY: 5,
      ...options
    };
    // Extract surface from options if provided
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    // Log tooth position information
    // console.log("IPX Tooth Information:", {
    //   position: this.pos || "unknown",
    //   row: this.row || "unknown",
    //   surface: this.surface || "unknown"
    // });
    // Different validation logic based on row
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
      // For rows 3 and 4, check first and second vertices
      if (
        this.vertices[0] == null ||
        this.vertices[1] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[1].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[1].y.toString()) + (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize);

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "top";
      ctx.textAlign = "left";
      ctx.fillText("PFD/FLD", x, y);
    } else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length <= 0) {
        return;
      }
      if (
        this.vertices[0] == null ||
        this.vertices[0].x === undefined ||
        this.vertices[0].y === undefined
      ) {
        return;
      }
      const x =
        parseFloat(this.vertices[0].x.toString()) + (this.options.offsetX || 0);
      const y =
        parseFloat(this.vertices[0].y.toString()) - (this.options.offsetY || 0);
      const fontsize = parseInt(this.options.fontsize.toString());

      ctx.fillStyle = "#000";
      ctx.font = "bold " + fontsize + "px Algerian";
      ctx.textBaseline = "bottom";
      ctx.textAlign = "left";
      ctx.fillText("PFD/FLD", x, y);
    }
  }
}

class BRIDGE {
  name = "BRIDGE";
  mode?: number;
  startVert: { x: number; y: number }[];
  endVert: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(
    startVert: { x: number; y: number }[],
    endVert: { x: number; y: number }[],
    options: any = {}
  ) {
    this.startVert = startVert;
    this.endVert = endVert;
    this.options = { strokeStyle: "#303030ff", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    let vert0, vert1;
    if (this.row === 1 || this.row === 2) {
      if (
        this.startVert &&
        this.startVert.length >= 2 &&
        this.startVert[0] != null &&
        this.startVert[1] != null &&
        this.startVert[0].x !== undefined &&
        this.startVert[1].x !== undefined &&
        this.startVert[0].y !== undefined &&
        this.startVert[1].y !== undefined
      ) {
        vert0 = {
          x1: parseFloat(this.startVert[0].x.toString()) + 1,
          y1: parseFloat(this.startVert[0].y.toString()) + 1,
          x2: parseFloat(this.startVert[1].x.toString()) - 1,
          y2: parseFloat(this.startVert[1].y.toString()) + 1,
          size: 0,
          cx: 0,
          cy: 0
        };
        vert0.size = vert0.x2 - vert0.x1;
        vert0.cx = vert0.x1 + vert0.size / 2;
        vert0.cy = vert0.y1 + vert0.size / 2;
      }

      if (
        this.endVert &&
        this.endVert.length >= 2 &&
        this.endVert[0] != null &&
        this.endVert[1] != null &&
        this.endVert[0].x !== undefined &&
        this.endVert[1].x !== undefined &&
        this.endVert[0].y !== undefined &&
        this.endVert[1].y !== undefined
      ) {
        vert1 = {
          x1: parseFloat(this.endVert[0].x.toString()) + 1,
          y1: parseFloat(this.endVert[0].y.toString()) + 1,
          x2: parseFloat(this.endVert[1].x.toString()) - 1,
          y2: parseFloat(this.endVert[1].y.toString()) + 1,
          size: 0,
          cx: 0,
          cy: 0
        };
        vert1.size = vert1.x2 - vert1.x1;
        vert1.cx = vert1.x1 + vert1.size / 2;
        vert1.cy = vert1.y1 + vert1.size / 2;
      }

      // Draw Bridge in vert0 as U shape with vertical line in middle bottom
      if (vert0) {
        const x1 = vert0.x1;
        const y1 = vert0.y1;
        const x2 = vert0.x2;
        const y2 = vert0.y2;

        ctx.strokeStyle = this.options.strokeStyle;
        ctx.lineWidth = 6;
        ctx.beginPath();

        // Draw left line
        ctx.moveTo(x1, y1);
        ctx.lineTo(x1, y2);
        // Draw bottom line
        ctx.lineTo(x2, y2);
        // Draw right line
        ctx.lineTo(x2, y1);
        // Draw vertical line in middle bottom
        ctx.moveTo((x1 + x2) / 2, y2);
        ctx.lineTo((x1 + x2) / 2, y2 + vert0.size / 4);

        ctx.stroke();
      }

      // Draw Bridge in vert1 as U shape with vertical line in middle bottom
      if (vert1) {
        const x1 = vert1.x1;
        const y1 = vert1.y1;
        const x2 = vert1.x2;
        const y2 = vert1.y2;

        ctx.strokeStyle = this.options.strokeStyle;
        ctx.lineWidth = 6;
        ctx.beginPath();

        // Draw left line
        ctx.moveTo(x1, y1);
        ctx.lineTo(x1, y2);
        // Draw bottom line
        ctx.lineTo(x2, y2);
        // Draw right line
        ctx.lineTo(x2, y1);
        // Draw vertical line in middle bottom
        ctx.moveTo((x1 + x2) / 2, y2);
        ctx.lineTo((x1 + x2) / 2, y2 + vert1.size / 4);

        ctx.stroke();
      }

      // JOIN BRIDGE
      if (vert0 && vert1) {
        const lor = vert1.cx - vert0.cx > 0 ? 1 : -1;
        ctx.strokeStyle = this.options.strokeStyle;
        ctx.lineWidth = 6;
        ctx.beginPath();
        ctx.moveTo(vert0.cx - 3 * lor, vert0.y2 + vert0.size / 4);
        ctx.lineTo(vert1.cx + 3 * lor, vert1.y2 + vert1.size / 4);
        ctx.stroke();
      }
    } else if (this.row === 3 || this.row === 4) {
      if (
        this.startVert &&
        this.startVert.length >= 2 &&
        this.startVert[0] != null &&
        this.startVert[1] != null &&
        this.startVert[0].x !== undefined &&
        this.startVert[1].x !== undefined &&
        this.startVert[0].y !== undefined &&
        this.startVert[1].y !== undefined
      ) {
        vert0 = {
          x1: parseFloat(this.startVert[0].x.toString()) + 1,
          y1: parseFloat(this.startVert[0].y.toString()) - 1,
          x2: parseFloat(this.startVert[1].x.toString()) - 1,
          y2: parseFloat(this.startVert[1].y.toString()) - 1,
          size: 0,
          cx: 0,
          cy: 0
        };
        vert0.size = vert0.x2 - vert0.x1;
        vert0.cx = vert0.x2 - vert0.size / 2;
        vert0.cy = vert0.y2 - vert0.size / 2;
      } else {
        // // console.warn(
        //   "BRIDGE render: startVert missing or invalid",
        //   this.startVert
        // );
      }

      if (
        this.endVert &&
        this.endVert.length >= 2 &&
        this.endVert[0] != null &&
        this.endVert[1] != null &&
        this.endVert[0].x !== undefined &&
        this.endVert[1].x !== undefined &&
        this.endVert[0].y !== undefined &&
        this.endVert[1].y !== undefined
      ) {
        vert1 = {
          x1: parseFloat(this.endVert[0].x.toString()) + 1,
          y1: parseFloat(this.endVert[0].y.toString()) - 1,
          x2: parseFloat(this.endVert[1].x.toString()) - 1,
          y2: parseFloat(this.endVert[1].y.toString()) - 1,
          size: 0,
          cx: 0,
          cy: 0
        };
        vert1.size = vert1.x2 - vert1.x1;
        vert1.cx = vert1.x2 - vert1.size / 2;
        vert1.cy = vert1.y2 - vert1.size / 2;
      } else {
        // // console.warn("BRIDGE render: endVert missing or invalid", this.endVert);
      }

      // Draw Bridge in vert0 as U shape with vertical line in middle top
      if (vert0) {
        const x1 = vert0.x1;
        const y1 = vert0.y1;
        const x2 = vert0.x2;
        const y2 = vert0.y2;

        ctx.strokeStyle = this.options.strokeStyle;
        ctx.lineWidth = 6;
        ctx.beginPath();

        // Draw left line
        ctx.moveTo(x1, y2);
        ctx.lineTo(x1, y1);
        // Draw top line
        ctx.lineTo(x2, y1);
        // Draw right line
        ctx.lineTo(x2, y2);
        // Draw vertical line in middle top
        ctx.moveTo((x1 + x2) / 2, y1);
        ctx.lineTo((x1 + x2) / 2, y1 - vert0.size / 4);

        ctx.stroke();
      }

      // Draw Bridge in vert1 as U shape with vertical line in middle top
      if (vert1) {
        const x1 = vert1.x1;
        const y1 = vert1.y1;
        const x2 = vert1.x2;
        const y2 = vert1.y2;

        ctx.strokeStyle = this.options.strokeStyle;
        ctx.lineWidth = 6;
        ctx.beginPath();

        // Draw left line
        ctx.moveTo(x1, y2);
        ctx.lineTo(x1, y1);
        // Draw top line
        ctx.lineTo(x2, y1);
        // Draw right line
        ctx.lineTo(x2, y2);
        // Draw vertical line in middle top
        ctx.moveTo((x1 + x2) / 2, y1);
        ctx.lineTo((x1 + x2) / 2, y1 - vert1.size / 4);

        ctx.stroke();
      }

      // JOIN BRIDGE
      if (vert0 && vert1) {
        const lor = vert1.cx - vert0.cx > 0 ? 1 : -1;
        ctx.strokeStyle = this.options.strokeStyle;
        ctx.lineWidth = 6;
        ctx.beginPath();
        ctx.moveTo(vert0.cx - 3 * lor, vert0.y1 - vert0.size / 4);
        ctx.lineTo(vert1.cx + 3 * lor, vert1.y1 - vert1.size / 4);
        ctx.stroke();
      }
    }
  }
}

class HAPUS {
  name = "HAPUS";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { fillStyle: "rgba(200, 200, 200, 0.8)", ...options };
        // Extract surface from options if provided
        this.surface = options.surface;
        // Extract row from options if provided
        this.row = options.row;
        // Extract pos from options if provided
        this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    const x1 = parseFloat(this.vertices[0].x.toString()) + 1;
    const y1 = parseFloat(this.vertices[0].y.toString()) + 1;
    const x2 = parseFloat(this.vertices[1].x.toString()) + 1;
    const y2 = parseFloat(this.vertices[1].y.toString()) + 1;
    const x = x1;
    const y = y1;
    const size = x2 - x1;

    ctx.beginPath();
    ctx.fillStyle = this.options.fillStyle;
    ctx.rect(x, y, size, size);
    ctx.fill();
  }
}

// Arrow classes for tooth condition visualization

class ARROW_TOP_LEFT {
  name = "ARROW_TOP_LEFT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    // Extract surface from options if provided
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x2 - (x2 - x1) / 3;
      const fromy = y1 - 15;
      const tox = fromx - 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x2 - (x2 - x1) / 3;
      const fromy = y2 + 15;
      const tox = fromx - 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

class ARROW_TOP_RIGHT {
  name = "ARROW_TOP_RIGHT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x1 + (x2 - x1) / 3;
      const fromy = y1 - 15;
      const tox = fromx + 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x1 + (x2 - x1) / 3;
      const fromy = y2 + 15;
      const tox = fromx + 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

class ARROW_TOP_TURN_LEFT {
  name = "ARROW_TOP_TURN_LEFT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x1 + (x2 - x1) / 2) - 50;
      const fromy = y1 - 15;
      const tox = fromx + 20;
      const toy = fromy ;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx + 55, fromy + 5, 5, 1.5 * Math.PI, 0.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox + 20, toy);
      ctx.lineTo(fromx + 55, toy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox);
  
      const newTox = tox +10;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.stroke();
      ctx.fill();
      
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x2 - (x2 - x1) / 2)+10;
      const fromy = y2 + 15;
      const tox = fromx - 20;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx, fromy - 5, 5, 1.5 * Math.PI, 0.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(fromx, toy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox);
  
      const newTox = tox - 5;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

class ARROW_TOP_TURN_RIGHT {
  name = "ARROW_TOP_TURN_RIGHT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x2 - (x2 - x1) /2) - 10;
      const fromy = y1 - 15;
      const tox = fromx + 20;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx, fromy + 5, 5, 0.38 * Math.PI, 1.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, fromy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
      const newTox = tox + 5;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        newTox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );

      ctx.stroke();
      ctx.fill();
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x2 - (x2 - x1) / 2) + 10;
      const fromy = y2 + 15;
      const tox = fromx - 20;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx, fromy - 5 , 5, 1.5 * Math.PI, 0.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(fromx, toy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox);
  
      const newTox = tox - 5;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

class ARROW_BOTTOM_LEFT {
  name = "ARROW_BOTTOM_LEFT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number
  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x2 - (x2 - x1) / 3;
      const fromy = y2 + 15;
      const tox = fromx - 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x2 - (x2 - x1) / 3;
      const fromy = y1 - 15;
      const tox = fromx - 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

class ARROW_BOTTOM_RIGHT {
  name = "ARROW_BOTTOM_RIGHT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x1 + (x2 - x1) / 3;
      const fromy = y2 + 15;
      const tox = fromx + 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = x1 + (x2 - x1) / 3;
      const fromy = y1 - 15;
      const tox = fromx + 25;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, toy);
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        tox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(tox, toy);
      ctx.lineTo(
        tox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

class ARROW_BOTTOM_TURN_LEFT {
  name = "ARROW_BOTTOM_TURN_LEFT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number

  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x2 - (x2 - x1) / 2) + 10;
      const fromy = y2 + 15;
      const tox = fromx - 20;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx, fromy - 5 , 5, 1.5 * Math.PI, 0.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox, toy);
      ctx.lineTo(fromx, toy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox);
  
      const newTox = tox - 5;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.stroke();
      ctx.fill();
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x1 + (x2 - x1) / 2) - 45;
      const fromy = y1 - 15;
      const tox = fromx + 20;
      const toy = fromy ;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx + 55, fromy + 5, 5, 1.5 * Math.PI, 0.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(tox + 20, toy);
      ctx.lineTo(fromx + 55, toy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox);
  
      const newTox = tox +10;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox + headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

class ARROW_BOTTOM_TURN_RIGHT {
  name = "ARROW_BOTTOM_TURN_RIGHT";
  mode?: number;
  vertices: { x: number; y: number }[];
  options: any;
  surface?: string; // Tooth surface (T, R, B, L, M)
  row?: number; // Tooth row (1, 2, 3, or 4)
  pos?: string; // Tooth position/number
  constructor(vertices: { x: number; y: number }[], options: any = {}) {
    this.vertices = vertices;
    this.options = { strokeStyle: "#000000", fillStyle: "#000000", ...options };
    this.surface = options.surface;
    // Extract row from options if provided
    this.row = options.row;
    // Extract pos from options if provided
    this.pos = options.pos;
  }

  render(ctx: CanvasRenderingContext2D) {
    if (this.row === 1 || this.row === 2) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x2 - (x2 - x1) / 2)-10 ;
      const fromy = y2 + 10;
      const tox = fromx + 20;
      const toy = fromy + 5;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx, fromy, 5, 0.38 * Math.PI, 1.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(fromx, toy);
      ctx.lineTo(tox, toy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox);
  
      const newTox = tox + 5;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        newTox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.stroke();
      ctx.fill();
    }else if (this.row === 3 || this.row === 4) {
      if (!this.vertices || this.vertices.length < 2) {
        return;
      }
  
      const x1 = parseFloat(this.vertices[0].x.toString());
      const y1 = parseFloat(this.vertices[0].y.toString());
      const x2 = parseFloat(this.vertices[1].x.toString());
      const y2 = parseFloat(this.vertices[1].y.toString());
  
      const fromx = (x2 - (x2 - x1) /2) - 10;
      const fromy = y1 - 15;
      const tox = fromx + 20;
      const toy = fromy;
  
      const headlen = 12;
      const lineWidth = 4;
  
      ctx.strokeStyle = this.options.strokeStyle;
      ctx.fillStyle = this.options.fillStyle;
      ctx.lineWidth = lineWidth;
  
      ctx.beginPath();
      ctx.arc(fromx, fromy + 5, 5, 0.38 * Math.PI, 1.5 * Math.PI);
      ctx.stroke();
  
      ctx.beginPath();
      ctx.moveTo(fromx, fromy);
      ctx.lineTo(tox, fromy);
      ctx.stroke();
  
      const angle = Math.atan2(toy - fromy, tox - fromx);
  
      
      const newTox = tox + 5;
      ctx.beginPath();
      ctx.moveTo(newTox, toy);
      ctx.lineTo(
        newTox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.lineTo(
        newTox - headlen * Math.cos(angle + Math.PI / 7),
        toy - headlen * Math.sin(angle + Math.PI / 7)
      );
      ctx.lineTo(newTox, toy);
      ctx.lineTo(
        newTox - headlen * Math.cos(angle - Math.PI / 7),
        toy - headlen * Math.sin(angle - Math.PI / 7)
      );
      ctx.stroke();
      ctx.fill();
    }
    
  }
}

export {
  Polygon,
  AMF,
  COF,
  FIS,
  NVT,
  UNE,
  PRE,
  NON,
  ANO,
  MIS,
  IPX,
  FRM_ACR,
  BRIDGE,
  HAPUS,
  RCT,
  CARIES,
  CFR,
  FMC,
  POC,
  RRX,
  ARROW_TOP_TURN_LEFT,
  ARROW_TOP_TURN_RIGHT,
  ARROW_BOTTOM_TURN_LEFT,
  ARROW_BOTTOM_TURN_RIGHT,
  ARROW_TOP_LEFT,
  ARROW_TOP_RIGHT,
  ARROW_BOTTOM_LEFT,
  ARROW_BOTTOM_RIGHT,
  useToothRenderer
};

function useToothRenderer() {
  function renderCondition(
    ctx: CanvasRenderingContext2D,
    conditionName: string,
    vertices: { x: number; y: number }[],
    options: any = {}
  ) {
    const conditionClasses: Record<string, any> = {
      Polygon,
      AMF,
      COF,
      FIS,
      NVT,
      UNE,
      PRE,
      NON,
      ANO,
      MIS,
      IPX,
      FRM_ACR,
      BRIDGE,
      HAPUS,
      RCT,
      CARIES,
      CFR,
      FMC,
      POC,
      RRX,
      ARROW_TOP_TURN_LEFT,
      ARROW_TOP_TURN_RIGHT,
      ARROW_BOTTOM_TURN_LEFT,
      ARROW_BOTTOM_TURN_RIGHT,
      ARROW_TOP_LEFT,
      ARROW_TOP_RIGHT,
      ARROW_BOTTOM_LEFT,
      ARROW_BOTTOM_RIGHT
    };

    const ConditionClass = conditionClasses[conditionName];
    if (!ConditionClass) {
      console.warn(`Unknown condition class: ${conditionName}`);
      return;
    }
    const conditionInstance = new ConditionClass(vertices, options);
    conditionInstance.render(ctx);
  }

  return {
    renderCondition
  };
}
