import { pond } from "protobuf-ts/pond";
import { cloneDeep } from "lodash";
import { or } from "utils";
import { Block } from "./Block";
import { Circle } from "./Circle";
import {} from "../Editor";

const remap = (value: number, sourceMin: number, sourceMax: number, destMin = 0.5, destMax = 1) => {
  let sourceRange = sourceMax - sourceMin;
  let destRange = destMax - destMin;
  let newValue = ((value - sourceMin) * destRange) / sourceRange + destMin;
  //console.log("Value: " + value + "\nmin: " + sourceMin + "\nmax: " + sourceMax)
  return newValue;
};
export class VentBezier extends Block {
  public circle = Circle.create(0, 0, 6);
  public settings: pond.BlockSettings = pond.BlockSettings.create();
  public selected = false;
  public img = undefined;
  public mouseOffsetX = 0;
  public mouseOffsetY = 0;
  public startSnap: Block | undefined = undefined;
  public endSnap: Block | undefined = undefined;

  public ctrl = false;
  public shift = false;

  public static create(pb?: pond.Block): VentBezier {
    let my = new VentBezier();
    if (pb) {
      my.settings = pond.BlockSettings.fromObject(cloneDeep(or(pb.settings, {})));
      // For some reason, negative x and y values don't clone correctly
      if (pb.settings) {
        my.settings.x = pb.settings.x;
        my.settings.y = pb.settings.y;
        my.settings.width = pb.settings.width;
        my.settings.height = pb.settings.height;
        //my.circle.radius = 6
      }
    }
    my.ctrl = false;
    my.shift = false;
    return my;
  }

  public magnitude(): number {
    return 0;
  }

  public clone() {
    return new Block();
  }

  private drawBezier(
    context: CanvasRenderingContext2D,
    x: number,
    y: number,
    endx: number,
    endy: number,
    offsetx: number,
    offsety: number
  ) {
    if (this.startSnap === undefined) return;

    if (this.startSnap?.settings.angle === 0) {
      this.startSnap.settings.angle = 360;
    }

    let h = this.distance(x, endx, y, endy);
    let theta1 = Math.asin((endy - y) / h);

    if (endx - x < 0 && this.settings.angle < 180 && this.settings.angle >= 0) {
      if (theta1 < 0) {
        let diff = 90 - Math.abs(theta1 / (Math.PI / 180));
        theta1 = (theta1 / (Math.PI / 180) - diff * 2) * (Math.PI / 180);
      } else {
        let diff = 90 - Math.abs(theta1 / (Math.PI / 180));
        theta1 = (theta1 / (Math.PI / 180) + diff * 2) * (Math.PI / 180);
      }
    }

    let angleRatio = remap(
      Math.abs(theta1),
      this.startSnap?.radians(),
      this.startSnap?.radians() - 45 * (Math.PI / 180)
    );
    if (this.startSnap.settings.angle > 180 && this.startSnap.settings.angle <= 270) {
      x = remap(
        Math.abs(theta1),
        this.startSnap.radians() - 135 * (Math.PI / 180),
        this.startSnap.radians() - 225 * (Math.PI / 180)
      );
    }

    angleRatio = Math.abs(angleRatio);
    if (angleRatio < 0.5) {
      angleRatio = 0.5 + (0.5 - angleRatio);
    }
    if (angleRatio > 1) {
      angleRatio = 1;
    }

    let theta = theta1 + this.startSnap.radians();
    let a = Math.cos(theta);
    a = a * h;
    a = a * angleRatio;
    let dx = Math.cos(this.radians(this.startSnap.settings.angle)) * a;
    let dy = Math.sin(this.radians(this.startSnap.settings.angle)) * a;

    if (this.endSnap) {
    }

    // Bezier curve offset points
    /*context.beginPath();
    context.moveTo(x - offsetx, y - offsety);
    context.lineTo(x + dx - offsetx, y - dy - offsety);
    context.stroke();*/

    let dx1 = x + dx * 0.8;
    let dy1 = y - dy * 0.8;

    /*context.beginPath();
    context.arc(dx1 - offsetx, dy1 - offsety, 6, 0, 2 * Math.PI);
    context.stroke();

    context.beginPath();
    context.moveTo(x + dx - offsetx, y - dy - offsety);
    context.lineTo(endx - offsetx, endy - offsety);
    context.stroke();*/

    let dx2 = endx - (x + dx);
    dx2 = x + dx + dx2 * 0.2;
    let dy2 = endy - (y - dy);
    dy2 = y - dy + dy2 * 0.2;

    /*context.beginPath();
    context.arc(0 - offsetx, 0 - offsety, 0, 0, 2 * Math.PI);
    context.stroke();

    context.beginPath();
    context.arc(dx2 - offsetx, dy2 - offsety, 6, 0, 2 * Math.PI);
    context.stroke();

    context.beginPath();
    context.arc(x + dx - offsetx, y - dy - offsety, 6, 0, 2 * Math.PI);
    context.stroke();*/

    context.moveTo(x - offsetx, y - offsety);
    context.strokeStyle = "rgb(255, 255, 255)";
    context.bezierCurveTo(
      dx1 - offsetx,
      dy1 - offsety,
      dx2 - offsetx,
      dy2 - offsety,
      endx - offsetx,
      endy - offsety
    );
    context.stroke();
  }

  public draw(context: CanvasRenderingContext2D, offsetX?: number, offsetY?: number) {
    let ox = offsetX ? offsetX : 0;
    let oy = offsetY ? offsetY : 0;

    if (this.startSnap) {
      if (this.startSnap.settings.angle === 0) {
        this.startSnap.settings.angle = 360;
      }

      if (this.circle.selected) {
        this.drawBezier(context, this.x(), this.y(), this.width(), this.height(), ox, oy);
      } else if (this.startSnap) {
        let radius = this.startSnap.height() / 2;
        let dy1 = Math.cos(this.startSnap.radians()) * radius;
        let dx1 = Math.sin(this.startSnap.radians()) * radius;
        if (this.endSnap) {
          let dy2 = Math.cos(this.endSnap.radians()) * radius;
          let dx2 = Math.sin(this.endSnap.radians()) * radius;
          this.drawBezier(
            context,
            this.x() - dx1,
            this.y() - dy1,
            this.width() - dx2,
            this.height() - dy2,
            ox,
            oy
          );
          this.drawBezier(
            context,
            this.x() + dx1,
            this.y() + dy1,
            this.width() + dx2,
            this.height() + dy2,
            ox,
            oy
          );
        } else {
          this.drawBezier(
            context,
            this.x() - dx1,
            this.y() - dy1,
            this.width(),
            this.height(),
            ox,
            oy
          );
        }
        /*console.log(dx1 + ", " + dy1);
        context.strokeStyle = "rgb(255, 255, 255)";
        context.beginPath();
        context.strokeStyle = "rgb(255, 255, 255)";
        context.arc(this.x() - dx1 - ox, this.y() - dy1 - oy, 6, 0, 2 * Math.PI);
        context.stroke();*/
      }
    }

    /*context.strokeStyle = "rgb(255, 255, 255)";
    context.beginPath();
    context.strokeStyle = "rgb(255, 255, 255)";
    context.arc(this.x() - ox, this.y() - oy, 6, 0, 2 * Math.PI);
    context.stroke();*/

    context.beginPath();
    context.arc(this.width() - ox, this.height() - oy, 6, 0, 2 * Math.PI);
    context.stroke();
  }

  public highlight(context: CanvasRenderingContext2D, ox: number, oy: number) {
    context.fillStyle = "rgb(255, 255, 255)";
    context.globalAlpha = 0.3;
    context.beginPath();
    context.arc(this.width() - ox, this.height() - oy, 6, 0, 2 * Math.PI);
    context.fill();
    context.globalAlpha = 1;
  }

  public clickCheck(x: number, y: number, extend?: number) {
    if (this.distance(x, this.endX(), y, this.endY()) <= this.circle.radius) {
      this.circle.selected = true;
      return true;
    }
    return false;
  }

  public getSnap(rect: Block, snapDistance: number = 6) {
    if (rect.x() === this.startSnap?.x() && rect.y() === this.startSnap?.y()) return undefined;
    if (
      Math.abs(this.endX() - rect.x()) < snapDistance &&
      Math.abs(this.endY() - rect.y()) < snapDistance
    ) {
      this.settings.width = rect.x();
      this.settings.height = rect.y();
      this.endSnap = rect;
    }
    return undefined;
  }

  public drag = (x: number, y: number) => {
    if (this.circle.selected) {
      this.settings.width = x;
      this.settings.height = y;
      let dx = this.settings.width - this.settings.x;
      let dy = this.settings.height - this.settings.y;
      if (Math.abs(dx) > Math.abs(dy)) {
        dy = dx;
      } else {
        dx = dy;
      }
      if (this.endSnap) {
        this.endSnap.settings.x = x;
        this.endSnap.settings.y = y;
      }
      this.settings.angle = Math.abs((Math.atan(dy / dx) * 180) / Math.PI);
    } /*else {
      let dx = this.settings.width - this.settings.x;
      let dy = this.settings.height - this.settings.y;
      this.settings.x = x;
      this.settings.y = y;
      this.settings.width = dx;
      this.settings.height = dy;
    }*/
  };

  public x(): number {
    return this.settings.x;
  }

  public y(): number {
    return this.settings.y;
  }

  public endX(): number {
    return this.settings.width;
  }

  public endY(): number {
    return this.settings.height;
  }

  public width(): number {
    return this.settings.width;
  }

  public height(): number {
    return this.settings.height;
  }

  public getCtrl(): boolean {
    return this.ctrl;
  }

  public getShift(): boolean {
    return this.shift;
  }

  public setCtrl(ctrl: boolean) {
    this.ctrl = ctrl;
  }

  public setShift(shift: boolean) {
    this.shift = shift;
  }

  public setSelected(s: boolean) {
    this.selected = s;
    this.circle.selected = s;
  }
}
