import { pond } from "protobuf-ts/pond";
import { Placeable } from "./Placeable";
import { Component, Device, User } from "models";
import { UnitMeasurement } from "models/UnitMeasurement";
import { SnapPoint } from "./geometry";

export class Sensor implements Placeable {
  public debug = false;
  public x = 0;
  public y = 0;
  public angle = 0;
  public ctrl = false;
  public shift = false;
  public selected = false;
  public type = pond.PlaceableType.PLACEABLE_TYPE_SENSOR;
  public direction = 0;
  public subtype = pond.ShaftType.SHAFT_TYPE_DEFAULT;
  public index = 0;

  private sensor_radius = 8;

  //private circle = Circle.create(0, 0, 0);
  private mouseOffsetX = 0;
  private mouseOffsetY = 0;
  private measurements: UnitMeasurement[] = [];

  private component: Component = new Component();
  private device: Device = new Device();
  private preferences: pond.MineSensorPreferences = new pond.MineSensorPreferences();

  public getComponent() {
    return this.component;
  }

  public name() {
    return this.preferences.nickname;
  }

  public getDevice() {
    return this.device;
  }

  public getMeasurements() {
    return this.measurements;
  }

  public getPreferences() {
    return this.preferences;
  }

  public magnitude(): number {
    return this.sensor_radius;
  }

  public radius(): number {
    return this.sensor_radius;
  }

  public static fromPond(shaft: pond.Placeable) {
    let v = new Sensor();
    v.x = shaft.x;
    v.y = shaft.y;
    v.angle = shaft.angle;

    document.body.addEventListener("keydown", function(event) {
      if (event.key === "Control") {
        v.ctrl = true;
      } else if (event.key === "Shift") {
        v.shift = true;
      }
    });

    document.body.addEventListener("keyup", function(event) {
      if (event.key === "Control") {
        v.ctrl = false;
      } else if (event.key === "Shift") {
        v.shift = false;
      }
    });

    return v;
  }

  public static create(
    component: Component,
    device: Device,
    preferences: pond.MineSensorPreferences,
    measurements: UnitMeasurement[],
    index: number,
    user?: User
  ): Sensor {
    let v = new Sensor();

    v.x = preferences.x;
    v.y = preferences.y;
    v.index = index;

    v.device = device;
    v.component = component;
    v.preferences = preferences;
    v.measurements = measurements;

    /*component.lastMeasurement.forEach(m => {
      let um = UnitMeasurement.create(m, user);
      v.measurements.push(um);
    });*/

    document.body.addEventListener("keydown", function(event) {
      if (event.key === "Control") {
        v.ctrl = true;
      } else if (event.key === "Shift") {
        v.shift = true;
      }
    });

    document.body.addEventListener("keyup", function(event) {
      if (event.key === "Control") {
        v.ctrl = false;
      } else if (event.key === "Shift") {
        v.shift = false;
      }
    });

    return v;
  }

  public draw(context: CanvasRenderingContext2D, offsetX?: number, offsetY?: number, scale = 1) {
    let x = this.x - (offsetX ? offsetX : 0);
    let y = this.y - (offsetY ? offsetY : 0);
    let r = this.radius() * scale;

    context.fillStyle = "rgba(0, 200, 0, 255)";
    context.beginPath();
    context.arc(x * scale, y * scale, r + 0.1, 2 * Math.PI, 0, false);
    context.closePath();
    context.fill();
  }

  public getEndXAndY = () => {
    return { x: this.endX(), y: this.endY() };
  };

  public clickCheck = (x: number, y: number, scale = 1) => {
    let dx = Math.abs(x - this.x);
    let dy = Math.abs(y - this.y);
    let distance = Math.sqrt(dx * dx + dy * dy);
    if (distance <= this.radius()) {
      this.selected = true;
      return true;
    }
    this.selected = false;
    return false;
  };

  public highlight(
    context: CanvasRenderingContext2D,
    offsetX?: number,
    offsetY?: number,
    scale = 1
  ) {
    let x = this.x - (offsetX ? offsetX : 0);
    let y = this.y - (offsetY ? offsetY : 0);

    //scale it
    x = x * scale;
    y = y * scale;
    let radius = this.radius() * scale;

    // Highlight circle
    context.beginPath();
    context.arc(x + 0.5, y + 0.5, radius + 2, 0, 2 * Math.PI, false);
    context.closePath();
    context.strokeStyle = "rgb(255, 255, 255)";
    context.stroke();

    // Line going up
    context.beginPath();
    context.moveTo(x, y - (radius + 2 * scale));
    context.lineTo(x, y - (radius + 12));
    context.closePath();
    context.stroke();

    // Frame
    let padding = 8;
    let width = 0;
    this.measurements.forEach((m, i) => {
      if (m.values[0].values[this.index]) {
        context.font = "18px Times New Roman";
        width = width + context.measureText(m.values[0].values[this.index].toFixed(1)).width;
        context.font = "14px Times New Roman";
        width = width + context.measureText(m.unit).width;
      }
      if (i < this.measurements.length - 1) width = width + context.measureText(", ").width;
    });
    context.font = "18px Times New Roman";
    width = Math.max(width, context.measureText(this.preferences.nickname).width) + 0.1;
    context.moveTo(x, y - (radius + 12));
    context.clearRect(x, y - (radius + 42 + padding * 2), width + padding * 2, 46);
    context.lineTo(x + width + 1 + padding * 2, y - (radius + 12));
    context.lineTo(x + width + 1 + padding * 2, y - (radius + 42 + padding * 2));
    context.lineTo(x, y - (radius + 42 + padding * 2));
    context.lineTo(x, y - (radius + 12));
    context.stroke();

    // Reading
    let valueString = "";
    let newx = x + padding;
    this.measurements.forEach((m, i) => {
      newx = context.measureText(valueString).width + newx;
      valueString = "";
      if (!m.values[0].values[this.index]) {
        valueString = valueString + "NaN";
      } else {
        valueString = valueString + m.values[0].values[this.index].toFixed(1);
      }
      valueString = valueString + m.unit;
      if (i < this.measurements.length - 1) valueString = valueString + ", ";
      context.fillStyle = m.colour;
      context.font = "14px Times New Roman";
      context.fillText(valueString, newx, y - (radius + 12) - padding);
    });

    // Label
    context.fillStyle = "white";
    context.font = "18px Times New Roman, sans-serif";
    context.fillText(this.preferences.nickname, x + padding, y - (radius + 30 + padding));
  }

  public clone() {
    let v = new Sensor();
    v.x = this.x;
    v.y = this.y;
    v.angle = this.angle;
    //v.circle = this.circle;
    v.ctrl = this.ctrl;
    v.shift = this.shift;
    v.radius = this.radius;
    return v;
  }

  public distance(x1: number, x2: number, y1: number, y2: number) {
    let a = x1 - x2;
    let b = y1 - y2;
    return Math.sqrt(a * a + b * b);
  }

  getSnapPoints() {
    let snaps: SnapPoint[] = [];
    snaps.push({
      x: this.x,
      y: this.y,
      angle: this.angle,
      startPoint: false,
      occupant: undefined
    } as SnapPoint);
    snaps.push({
      x: this.x,
      y: this.y,
      angle: this.angle + 180,
      startPoint: true,
      occupant: undefined
    } as SnapPoint);
    if (snaps[1].angle > 360) snaps[1].angle = snaps[1].angle - 360;
    return snaps;
  }

  public snap = (p: Placeable, d: number): void => {
    d = d ? d : 12;
    p.getSnapPoints().forEach(point => {
      if (this.distance(this.x, point.x, this.y, point.y) < d) {
        this.x = point.x;
        this.y = point.y;
        this.angle = point.angle;
      }
    });
  };

  public drag = (x: number, y: number, scale = 1) => {
    this.x = x - this.mouseOffsetX;
    this.y = y - this.mouseOffsetY;
  };

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

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

  public getEndAngle(): number {
    return this.angle;
  }

  setDirection(direction: number): void {}

  setAngle(angle: number) {
    this.angle = angle;
  }
}
