import React = require('react');
import { IWallProps } from "./PhysicsSimulationWall";

export interface IForce {
  description: string;
  magnitude: number;
  directionInDegrees: number;
}
export interface IWeightProps {
  adjustPendulumAngle: { angle: number; length: number };
  color: string;
  displayXPosition: number;
  displayYPosition: number;
  displayXVelocity: number;
  displayYVelocity: number;
  elasticCollisions: boolean;
  startForces: IForce[];
  incrementTime: number;
  mass: number;
  paused: boolean;
  pendulum: boolean;
  pendulumLength: number;
  wedge: boolean;
  radius: number;
  reset: boolean;
  setDisplayXAcceleration: (val: number) => any;
  setDisplayXPosition: (val: number) => any;
  setDisplayXVelocity: (val: number) => any;
  setDisplayYAcceleration: (val: number) => any;
  setDisplayYPosition: (val: number) => any;
  setDisplayYVelocity: (val: number) => any;
  setPaused: (bool: boolean) => any;
  setPendulumAngle: (val: number) => any;
  setPendulumLength: (val: number) => any;
  setStartPendulumAngle: (val: number) => any;
  showAcceleration: boolean;
  pendulumAngle: number;
  setSketching: (val: boolean) => any;
  showForces: boolean;
  showForceMagnitudes: boolean;
  showVelocity: boolean;
  startPosX: number;
  startPosY: number;
  startVelX?: number;
  startVelY?: number;
  timestepSize: number;
  updateDisplay: { xDisplay: number; yDisplay: number };
  updatedForces: IForce[];
  setUpdatedForces: (val: IForce[]) => any;
  walls: IWallProps[];
  coefficientOfKineticFriction: number;
  wedgeWidth: number;
  wedgeHeight: number;
  xMax: number;
  yMax: number;
  xMin: number;
  yMin: number;
}

interface IState {
  clickPositionX: number,
  clickPositionY: number,
  dragging: boolean,
  kineticFriction: boolean,
  updatedStartPosX: number,
  updatedStartPosY: number,
  xPosition: number,
  yPosition: number,
  xVelocity: number,
  yVelocity: number,
}
export default class Weight extends React.Component<IWeightProps, IState> {

  constructor(props: any) {
    super(props)
    this.state = {
      clickPositionX: 0,
      clickPositionY: 0,
      dragging: false,
      kineticFriction: false,
      updatedStartPosX: this.props.startPosX,
      updatedStartPosY: this.props.startPosY,
      xPosition: this.props.startPosX,
      yPosition: this.props.startPosY,
      xVelocity: this.props.startVelX ? this.props.startVelX: 0,
      yVelocity: this.props.startVelY ? this.props.startVelY: 0,
    }
  }

  // Constants
  draggable = !this.props.wedge;
  epsilon = 0.0001;
  forceOfGravity: IForce = {
    description: "Gravity",
    magnitude: this.props.mass * 9.81,
    directionInDegrees: 270,
  };

  // Var
  weightStyle = {
    backgroundColor: this.props.color,
    borderStyle: "solid",
    borderColor: "black",
    position: "absolute" as "absolute",
    left: this.props.startPosX + "px",
    top: this.props.startPosY + "px",
    width: 2 * this.props.radius + "px",
    height: 2 * this.props.radius + "px",
    zIndex: 5,
    borderRadius: 50 + "%",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    touchAction: "none",
  };

  // Helper function to go between display and real values
  getDisplayYPos = (yPos: number) => {
    return this.props.yMax - yPos - 2 * this.props.radius + 5;
  };
  getYPosFromDisplay = (yDisplay: number) => {
    return this.props.yMax - yDisplay - 2 * this.props.radius + 5;
  };

  // Set display values based on real values
  setYPosDisplay = (yPos: number) => {
    const displayPos = this.getDisplayYPos(yPos);
    this.props.setDisplayYPosition(Math.round(displayPos * 100) / 100)
  };
  setXPosDisplay = (xPos: number) => {
    this.props.setDisplayXPosition(Math.round(xPos * 100) / 100);
  };
  setYVelDisplay = (yVel: number) => {
    this.props.setDisplayYVelocity((-1 * Math.round(yVel * 100)) / 100);
  };
  setXVelDisplay = (xVel: number) => {
    this.props.setDisplayXVelocity(Math.round(xVel * 100) / 100);
  };

  setDisplayValues = (
    xPos: number = this.state.xPosition,
    yPos: number = this.state.yPosition,
    xVel: number = this.state.xVelocity,
    yVel: number = this.state.yVelocity
  ) => {
    this.setYPosDisplay(yPos);
    this.setXPosDisplay(xPos);
    this.setYVelDisplay(yVel);
    this.setXVelDisplay(xVel);
    this.props.setDisplayYAcceleration(
      (-1 * Math.round(this.getNewAccelerationY(this.props.updatedForces) * 100)) / 100
    );
    this.props.setDisplayXAcceleration(
      Math.round(this.getNewAccelerationX(this.props.updatedForces) * 100) / 100
    );
  };

  componentDidUpdate(prevProps: Readonly<IWeightProps>, prevState: Readonly<IState>, snapshot?: any): void {
    // When display values updated by user, update real values
    if (this.props.updateDisplay != prevProps.updateDisplay) {
      if (this.props.updateDisplay.xDisplay != this.state.xPosition) {
        let x = this.props.updateDisplay.xDisplay;
        x = Math.max(0, x);
        x = Math.min(x, this.props.xMax - 2 * this.props.radius);
        this.setState({updatedStartPosX: x})
        this.setState({xPosition: x})
        this.props.setDisplayXPosition(x);
      }
  
      if (this.props.updateDisplay.yDisplay != this.getDisplayYPos(this.state.yPosition)) {
        let y = this.props.updateDisplay.yDisplay;
        y = Math.max(0, y);
        y = Math.min(y, this.props.yMax - 2 * this.props.radius);
        this.props.setDisplayYPosition(y);
        let coordinatePosition = this.getYPosFromDisplay(y);
        this.setState({updatedStartPosY: coordinatePosition})
        this.setState({yPosition: coordinatePosition})
      }
  
      if (this.props.displayXVelocity != this.state.xVelocity) {
        let x = this.props.displayXVelocity;
        this.setState({xVelocity: x})
        this.props.setDisplayXVelocity(x);
      }
  
      if (this.props.displayYVelocity != this.state.yVelocity) {
        let y = this.props.displayYVelocity;
        this.setState({yVelocity: -y})
        this.props.setDisplayXVelocity(y);
      }
    }
    // Update sim
    if (this.props.incrementTime != prevProps.incrementTime) {
      if (!this.props.paused) {
        let collisions = false;
        if (!this.props.pendulum) {
          const collisionsWithGround = this.checkForCollisionsWithGround();
          const collisionsWithWalls = this.checkForCollisionsWithWall();
          collisions = collisionsWithGround || collisionsWithWalls;
        }
        if (!collisions) {
          this.update();
        }
        this.setDisplayValues();
      }
    }
    this.weightStyle = {
      backgroundColor: this.props.color,
      borderStyle: "solid",
      borderColor: this.state.dragging ? "lightblue" : "black",
      position: "absolute" as "absolute",
      left: this.state.xPosition + "px",
      top: this.state.yPosition + "px",
      width: 2 * this.props.radius + "px",
      height: 2 * this.props.radius + "px",
      borderRadius: 50 + "%",
      display: "flex",
      zIndex: 5,
      justifyContent: "center",
      alignItems: "center",
      touchAction: "none",
    };

    if (this.props.reset != prevProps.reset) {
      this.resetEverything();
    }
    if (this.props.startForces != prevProps.startForces) {
      this.setState({xVelocity: this.props.startVelX ?? 0})
      this.setState({yVelocity: this.props.startVelY ?? 0})
      this.setDisplayValues();
    }
    if (this.props.adjustPendulumAngle != prevProps.adjustPendulumAngle) {
      // Change pendulum angle based on input field
      let length = this.props.adjustPendulumAngle.length;
      const x =
        length * Math.cos(((90 - this.props.adjustPendulumAngle.angle) * Math.PI) / 180);
      const y =
        length * Math.sin(((90 - this.props.adjustPendulumAngle.angle) * Math.PI) / 180);
      const xPos = this.props.xMax / 2 - x - this.props.radius;
      const yPos = y - this.props.radius - 5;
      this.setState({xPosition: xPos})
      this.setState({yPosition: yPos})
      this.setState({updatedStartPosX: xPos})
      this.setState({updatedStartPosY: yPos})
      this.props.setPendulumAngle(this.props.adjustPendulumAngle.angle);
      this.props.setPendulumLength(this.props.adjustPendulumAngle.length);
    }
    // Update x start position
    if (this.props.startPosX != prevProps.startPosX) {
      this.setState({updatedStartPosX: this.props.startPosX})
      this.setState({xPosition: this.props.startPosX})
      this.setXPosDisplay(this.props.startPosX);
    }
    // Update y start position
    if (this.props.startPosY != prevProps.startPosY) {
      this.setState({updatedStartPosY: this.props.startPosY})
      this.setState({yPosition: this.props.startPosY})
      this.setYPosDisplay(this.props.startPosY);
    }
    if (this.state.xVelocity != prevState.xVelocity) {
      if (this.props.wedge && this.state.xVelocity != 0 && !this.state.kineticFriction) {
        this.setState({kineticFriction: true});
        //switch from static to kinetic friction
        const normalForce: IForce = {
          description: "Normal Force",
          magnitude:
            this.forceOfGravity.magnitude *
            Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
          directionInDegrees:
            180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
        };
        let frictionForce: IForce = {
          description: "Kinetic Friction Force",
          magnitude:
          this.props.coefficientOfKineticFriction *
            this.forceOfGravity.magnitude *
            Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
          directionInDegrees:
            180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
        };
        // reduce magnitude of friction force if necessary such that block cannot slide up plane
        let yForce = -this.forceOfGravity.magnitude;
        yForce +=
          normalForce.magnitude *
          Math.sin((normalForce.directionInDegrees * Math.PI) / 180);
        yForce +=
          frictionForce.magnitude *
          Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
        if (yForce > 0) {
          frictionForce.magnitude =
            (-normalForce.magnitude *
              Math.sin((normalForce.directionInDegrees * Math.PI) / 180) +
              this.forceOfGravity.magnitude) /
            Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
        }
        if (this.props.coefficientOfKineticFriction != 0) {
          this.props.setUpdatedForces([this.forceOfGravity, normalForce, frictionForce]);
        } else {
          this.props.setUpdatedForces([this.forceOfGravity, normalForce]);
        }
      }
    }
  }

  resetEverything = () => {
    this.setState({kineticFriction: false})
    this.setState({xPosition: this.state.updatedStartPosX})
    this.setState({yPosition: this.state.updatedStartPosY})
    this.setState({xVelocity: this.props.startVelX ?? 0})
    this.setState({yVelocity: this.props.startVelY ?? 0})
    this.props.setUpdatedForces(this.props.startForces)
    this.setDisplayValues();
  };

  getNewAccelerationX = (forceList: IForce[]) => {
    let newXAcc = 0;
    forceList.forEach((force) => {
      newXAcc +=
        (force.magnitude *
          Math.cos((force.directionInDegrees * Math.PI) / 180)) /
        this.props.mass;
    });
    return newXAcc;
  };

  getNewAccelerationY = (forceList: IForce[]) => {
    let newYAcc = 0;
    forceList.forEach((force) => {
      newYAcc +=
        (-1 *
          (force.magnitude *
            Math.sin((force.directionInDegrees * Math.PI) / 180))) /
          this.props.mass;
    });
    return newYAcc;
  };

  getNewForces = (
    xPos: number,
    yPos: number,
    xVel: number,
    yVel: number
  ) => {
    if (!this.props.pendulum) {
      return this.props.updatedForces;
    }
    const x = this.props.xMax / 2 - xPos - this.props.radius;
    const y = yPos + this.props.radius + 5;
    let angle = (Math.atan(y / x) * 180) / Math.PI;
    if (angle < 0) {
      angle += 180;
    }
    let oppositeAngle = 90 - angle;
    if (oppositeAngle < 0) {
      oppositeAngle = 90 - (180 - angle);
    }

    const pendulumLength = Math.sqrt(x * x + y * y);
    this.props.setPendulumAngle(oppositeAngle);
    this.props.setPendulumLength(Math.sqrt(x * x + y * y));

    const mag =
      this.props.mass * 9.81 * Math.cos((oppositeAngle * Math.PI) / 180) +
      (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength;

    const forceOfTension: IForce = {
      description: "Tension",
      magnitude: mag,
      directionInDegrees: angle,
    };

    return [this.forceOfGravity, forceOfTension];
  };

  getNewPosition = (pos: number, vel: number) => {
    return pos + vel * this.props.timestepSize;
  };

  getNewVelocity = (vel: number, acc: number) => {
    return vel + acc * this.props.timestepSize;
  };

  checkForCollisionsWithWall = () => {
    let collision = false;
    const minX = this.state.xPosition;
    const maxX = this.state.xPosition + 2 * this.props.radius;
    const containerWidth = 300;
    if (this.state.xVelocity != 0) {
      this.props.walls.forEach((wall) => {
        if (wall.angleInDegrees == 90) {
          const wallX = (wall.xPos / 100) * 300;
          if (wall.xPos < 0.35) {
            if (minX <= wallX) {
              if (this.props.elasticCollisions) {
                this.setState({xVelocity: -this.state.xVelocity});
              } else {
                this.setState({xVelocity: 0});
                this.setState({xPosition: wallX+5});
              }
              collision = true;
            }
          } else {
            if (maxX >= wallX) {
              if (this.props.elasticCollisions) {
                this.setState({xVelocity: -this.state.xVelocity});
              } else {
                this.setState({xVelocity: 0});
                this.setState({xPosition: wallX - 2 * this.props.radius + 5});
              }
              collision = true;
            }
          }
        }
      });
    }
    return collision;
  };

  checkForCollisionsWithGround = () => {
    let collision = false;
    const maxY = this.state.yPosition + 2 * this.props.radius;
    if (this.state.yVelocity > 0) {
      this.props.walls.forEach((wall) => {
        if (wall.angleInDegrees == 0) {
          const groundY = (wall.yPos / 100) * this.props.yMax;
          if (maxY >= groundY) {
            if (this.props.elasticCollisions) {
              this.setState({yVelocity: -this.state.yVelocity})
            } else {
              this.setState({yVelocity: 0})
              this.setState({yPosition: groundY - 2 * this.props.radius + 5})
              const forceOfGravity: IForce = {
                description: "Gravity",
                magnitude: 9.81 * this.props.mass,
                directionInDegrees: 270,
              };
              const normalForce: IForce = {
                description: "Normal force",
                magnitude: 9.81 * this.props.mass,
                directionInDegrees: wall.angleInDegrees + 90,
              };
              this.props.setUpdatedForces([forceOfGravity, normalForce]);
            }
            collision = true;
          }
        }
      });
    }
    return collision;
  };

  update = () => {
    // RK4 update
    let xPos = this.state.xPosition;
    let yPos = this.state.yPosition;
    let xVel = this.state.xVelocity;
    let yVel = this.state.yVelocity;
    for (let i = 0; i < 60; i++) {
      let forces1 = this.getNewForces(xPos, yPos, xVel, yVel);
      const xAcc1 = this.getNewAccelerationX(forces1);
      const yAcc1 = this.getNewAccelerationY(forces1);
      const xVel1 = this.getNewVelocity(xVel, xAcc1);
      const yVel1 = this.getNewVelocity(yVel, yAcc1);

      let xVel2 = this.getNewVelocity(xVel, xAcc1 / 2);
      let yVel2 = this.getNewVelocity(yVel, yAcc1 / 2);
      let xPos2 = this.getNewPosition(xPos, xVel1 / 2);
      let yPos2 = this.getNewPosition(yPos, yVel1 / 2);
      const forces2 = this.getNewForces(xPos2, yPos2, xVel2, yVel2);
      const xAcc2 = this.getNewAccelerationX(forces2);
      const yAcc2 = this.getNewAccelerationY(forces2);
      xVel2 = this.getNewVelocity(xVel2, xAcc2);
      yVel2 = this.getNewVelocity(yVel2, yAcc2);
      xPos2 = this.getNewPosition(xPos2, xVel2);
      yPos2 = this.getNewPosition(yPos2, yVel2);

      let xVel3 = this.getNewVelocity(xVel, xAcc2 / 2);
      let yVel3 = this.getNewVelocity(yVel, yAcc2 / 2);
      let xPos3 = this.getNewPosition(xPos, xVel2 / 2);
      let yPos3 = this.getNewPosition(yPos, yVel2 / 2);
      const forces3 = this.getNewForces(xPos3, yPos3, xVel3, yVel3);
      const xAcc3 = this.getNewAccelerationX(forces3);
      const yAcc3 = this.getNewAccelerationY(forces3);
      xVel3 = this.getNewVelocity(xVel3, xAcc3);
      yVel3 = this.getNewVelocity(yVel3, yAcc3);
      xPos3 = this.getNewPosition(xPos3, xVel3);
      yPos3 = this.getNewPosition(yPos3, yVel3);

      let xVel4 = this.getNewVelocity(xVel, xAcc3);
      let yVel4 = this.getNewVelocity(yVel, yAcc3);
      let xPos4 = this.getNewPosition(xPos, xVel3);
      let yPos4 = this.getNewPosition(yPos, yVel3);
      const forces4 = this.getNewForces(xPos4, yPos4, xVel4, yVel4);
      const xAcc4 = this.getNewAccelerationX(forces4);
      const yAcc4 = this.getNewAccelerationY(forces4);
      xVel4 = this.getNewVelocity(xVel4, xAcc4);
      yVel4 = this.getNewVelocity(yVel4, yAcc4);
      xPos4 = this.getNewPosition(xPos4, xVel4);
      yPos4 = this.getNewPosition(yPos4, yVel4);

      xVel +=
        this.props.timestepSize * (xAcc1 / 6.0 + xAcc2 / 3.0 + xAcc3 / 3.0 + xAcc4 / 6.0);
      yVel +=
        this.props.timestepSize * (yAcc1 / 6.0 + yAcc2 / 3.0 + yAcc3 / 3.0 + yAcc4 / 6.0);
      xPos +=
        this.props.timestepSize * (xVel1 / 6.0 + xVel2 / 3.0 + xVel3 / 3.0 + xVel4 / 6.0);
      yPos +=
        this.props.timestepSize * (yVel1 / 6.0 + yVel2 / 3.0 + yVel3 / 3.0 + yVel4 / 6.0);
    }

    this.setState({xVelocity: xVel});
    this.setState({yVelocity: yVel});
    this.setState({xPosition: xPos});
    this.setState({yPosition: yPos});
    this.props.setUpdatedForces(this.getNewForces(xPos, yPos, xVel, yVel));
  };

  labelBackgroundColor = `rgba(255,255,255,0.5)`;

  render () {
    return (
    <div>
      <div
        className="weightContainer"
        onPointerDown={(e) => {
          if (this.draggable) {
            e.preventDefault();
            this.props.setPaused(true);
            this.setState({dragging: true});
            this.setState({clickPositionX: e.clientX})
            this.setState({clickPositionY: e.clientY})
          }
        }}
        onPointerMove={(e) => {
          e.preventDefault();
          if (this.state.dragging) {
            let newY = this.state.yPosition + e.clientY - this.state.clickPositionY;
            if (newY > this.props.yMax - 2 * this.props.radius) {
              newY = this.props.yMax - 2 * this.props.radius;
            }

            let newX = this.state.xPosition + e.clientX - this.state.clickPositionX;
            if (newX > this.props.xMax - 2 * this.props.radius) {
              newX = this.props.xMax - 2 * this.props.radius;
            } else if (newX < 0) {
              newX = 0;
            }
            this.setState({xPosition: newX})
            this.setState({yPosition: newY})
            this.setState({updatedStartPosX: newX})
            this.setState({updatedStartPosY: newY})
            this.props.setDisplayYPosition(Math.round((this.props.yMax - 2 * this.props.radius - newY + 5) * 100) / 100)
            this.setState({clickPositionX: e.clientX})
            this.setState({clickPositionY: e.clientY})
            this.setDisplayValues();
          }
        }}
        onPointerUp={(e) => {
          if (this.state.dragging) {
            e.preventDefault();
            if (!this.props.pendulum) {
              this.resetEverything();
            }
            this.setState({dragging: false});
            let newY = this.state.yPosition + e.clientY - this.state.clickPositionY;
            if (newY > this.props.yMax - 2 * this.props.radius) {
              newY = this.props.yMax - 2 * this.props.radius;
            }

            let newX = this.state.xPosition + e.clientX - this.state.clickPositionX;
            if (newX > this.props.xMax - 2 * this.props.radius) {
              newX = this.props.xMax - 2 * this.props.radius;
            } else if (newX < 0) {
              newX = 0;
            }
            if (this.props.pendulum) {
              const x = this.props.xMax / 2 - newX - this.props.radius;
              const y = newY + this.props.radius + 5;
              let angle = (Math.atan(y / x) * 180) / Math.PI;
              if (angle < 0) {
                angle += 180;
              }
              let oppositeAngle = 90 - angle;
              if (oppositeAngle < 0) {
                oppositeAngle = 90 - (180 - angle);
              }

              const pendulumLength = Math.sqrt(x * x + y * y);
              this.props.setPendulumAngle(oppositeAngle);
              this.props.setPendulumLength(Math.sqrt(x * x + y * y));
              const mag = 9.81 * Math.cos((oppositeAngle * Math.PI) / 180);
              const forceOfTension: IForce = {
                description: "Tension",
                magnitude: mag,
                directionInDegrees: angle,
              };
              this.setState({kineticFriction: false})
              this.setState({xVelocity: this.props.startVelX ?? 0})
              this.setState({yVelocity: this.props.startVelY ?? 0})
              this.setDisplayValues();
              this.props.setUpdatedForces([this.forceOfGravity, forceOfTension]);
            }
          }
        }}
      >
        <div className="weight" style={this.weightStyle}>
          <p className="weightLabel">{this.props.mass} kg</p>
        </div>
      </div>
      {this.props.pendulum && (
        <div
          className="rod"
          style={{
            pointerEvents: "none",
            position: "absolute",
            left: 0,
            top: 0,
          }}
        >
          <svg width={this.props.xMax + "px"} height={300 + "px"}>
            <line
              x1={this.state.xPosition + this.props.radius}
              y1={this.state.yPosition + this.props.radius}
              x2={this.props.xMax / 2}
              y2={-5}
              stroke={"#deb887"}
              strokeWidth="10"
            />
          </svg>
          {!this.state.dragging && (
            <div>
              {/* <p
                style={{
                  position: "absolute",
                  zIndex: 5,
                  left: this.state.xPosition + "px",
                  top: this.state.yPosition - 70 + "px",
                  backgroundColor: this.labelBackgroundColor,
                }}
              >
                {Math.round(this.props.pendulumLength)} m
              </p> */}
              <p
                style={{
                  position: "absolute",
                  left: this.props.xMax / 2 + "px",
                  top: 30 + "px",
                  backgroundColor: this.labelBackgroundColor,
                }}
              >
                {Math.round(this.props.pendulumAngle * 100) / 100}°
              </p>
            </div>
          )}
        </div>
      )}
      {!this.state.dragging && this.props.showAcceleration && (
        <div>
          <div
            style={{
              pointerEvents: "none",
              position: "absolute",
              left: 0,
              top: 0,
            }}
          >
            <svg width={this.props.xMax + "px"} height={300 + "px"}>
              <defs>
                <marker
                  id="accArrow"
                  markerWidth="10"
                  markerHeight="10"
                  refX="0"
                  refY="3"
                  orient="auto"
                  markerUnits="strokeWidth"
                >
                  <path d="M0,0 L0,6 L9,3 z" fill="green" />
                </marker>
              </defs>
              <line
                x1={this.state.xPosition + this.props.radius}
                y1={this.state.yPosition + this.props.radius}
                x2={this.state.xPosition + this.props.radius + this.getNewAccelerationX(this.props.updatedForces) * 5}
                y2={this.state.yPosition + this.props.radius + this.getNewAccelerationY(this.props.updatedForces) * 5}
                stroke={"green"}
                strokeWidth="5"
                markerEnd="url(#accArrow)"
              />
            </svg>
            <div
              style={{
                pointerEvents: "none",
                position: "absolute",
                left:
                  this.state.xPosition +
                  this.props.radius +
                  this.getNewAccelerationX(this.props.updatedForces) * 5 +
                  25 +
                  "px",
                top:
                  this.state.yPosition +
                  this.props.radius +
                  this.getNewAccelerationY(this.props.updatedForces) * 5 +
                  25 +
                  "px",
                lineHeight: 0.5,
              }}
            >
              <p>
                {Math.round(
                  100 *
                    Math.sqrt(
                      Math.pow(this.getNewAccelerationX(this.props.updatedForces) * 3, 2) +
                        Math.pow(this.getNewAccelerationY(this.props.updatedForces) * 3, 2)
                    )
                ) / 100}{" "}
                m/s<sup>2</sup>
              </p>
            </div>
          </div>
        </div>
      )}
      {!this.state.dragging && this.props.showVelocity && (
        <div>
          <div
            style={{
              pointerEvents: "none",
              position: "absolute",
              left: 0,
              top: 0,
            }}
          >
            <svg width={this.props.xMax + "px"} height={300 + "px"}>
              <defs>
                <marker
                  id="velArrow"
                  markerWidth="10"
                  markerHeight="10"
                  refX="0"
                  refY="3"
                  orient="auto"
                  markerUnits="strokeWidth"
                >
                  <path d="M0,0 L0,6 L9,3 z" fill="blue" />
                </marker>
              </defs>
              <line
                x1={this.state.xPosition + this.props.radius}
                y1={this.state.yPosition + this.props.radius}
                x2={this.state.xPosition + this.props.radius + this.state.xVelocity * 3}
                y2={this.state.yPosition + this.props.radius + this.state.yVelocity * 3}
                stroke={"blue"}
                strokeWidth="5"
                markerEnd="url(#velArrow)"
              />
            </svg>
            <div
              style={{
                pointerEvents: "none",
                position: "absolute",
                left: this.state.xPosition + this.props.radius + this.state.xVelocity * 3 + 25 + "px",
                top: this.state.yPosition + this.props.radius + this.state.yVelocity * 3 + "px",
                lineHeight: 0.5,
              }}
            >
              <p>
                {Math.round(
                  100 * Math.sqrt(this.state.xVelocity**2 + this.state.yVelocity**2)
                ) / 100}{" "}
                m/s
              </p>
            </div>
          </div>
        </div>
      )}
      {!this.state.dragging &&
        this.props.showForces &&
        this.props.updatedForces.map((force, index) => {
          if (force.magnitude < this.epsilon) {
            return;
          }
          let arrowStartY: number = this.state.yPosition + this.props.radius;
          const arrowStartX: number = this.state.xPosition + this.props.radius;
          let arrowEndY: number =
            arrowStartY -
            Math.abs(force.magnitude) *
              20 *
              Math.sin((force.directionInDegrees * Math.PI) / 180);
          const arrowEndX: number =
            arrowStartX +
            Math.abs(force.magnitude) *
              20 *
              Math.cos((force.directionInDegrees * Math.PI) / 180);

          let color = "#0d0d0d";

          let labelTop = arrowEndY;
          let labelLeft = arrowEndX;
          if (force.directionInDegrees > 90 && force.directionInDegrees < 270) {
            labelLeft -= 120;
          } else {
            labelLeft += 30;
          }
          if (force.directionInDegrees >= 0 && force.directionInDegrees < 180) {
            labelTop += 40;
          } else {
            labelTop -= 40;
          }
          labelTop = Math.min(labelTop, this.props.yMax + 50);
          labelTop = Math.max(labelTop, this.props.yMin);
          labelLeft = Math.min(labelLeft, this.props.xMax - 60);
          labelLeft = Math.max(labelLeft, this.props.xMin);

          return (
            <div key={index}>
              <div
                style={{
                  pointerEvents: "none",
                  position: "absolute",
                  left: this.props.xMin,
                  top: this.props.yMin,
                }}
              >
                <svg
                  width={this.props.xMax - this.props.xMin + "px"}
                  height={300 + "px"}
                >
                  <defs>
                    <marker
                      id="forceArrow"
                      markerWidth="10"
                      markerHeight="10"
                      refX="0"
                      refY="3"
                      orient="auto"
                      markerUnits="strokeWidth"
                    >
                      <path d="M0,0 L0,6 L9,3 z" fill={color} />
                    </marker>
                  </defs>
                  <line
                    x1={arrowStartX}
                    y1={arrowStartY}
                    x2={arrowEndX}
                    y2={arrowEndY}
                    stroke={color}
                    strokeWidth="5"
                    markerEnd="url(#forceArrow)"
                  />
                </svg>
              </div>
              <div
                style={{
                  pointerEvents: "none",
                  position: "absolute",
                  left: labelLeft + "px",
                  top: labelTop + "px",
                  // zIndex: -1,
                  lineHeight: 0.5,
                  backgroundColor: this.labelBackgroundColor,
                }}
              >
                {force.description && <p>{force.description}</p>}
                {!force.description && <p>Force</p>}
                {this.props.showForceMagnitudes && (
                  <p>{Math.round(100 * force.magnitude) / 100} N</p>
                )}
              </div>
            </div>
          );
        })}
    </div>
    );
  }
};
