import React = require('react');
import "./PhysicsSimulationBox.scss";
import { IForce, Weight } from "./PhysicsSimulationWeight";
import {Wall, IWallProps } from "./PhysicsSimulationWall"
import {Wedge} from "./PhysicsSimulationWedge"
import { props, any } from 'bluebird';
import { render } from 'react-dom';

interface PhysicsVectorTemplate {
  top: number;
  left: number;
  width: number;
  height: number;
  x1: number;
  y1: number;
  x2: number;
  y2: number;
  weightX: number;
  weightY: number;
}

interface IState {
    accelerationXDisplay: number,
    accelerationYDisplay: number,
    adjustPendulumAngle: {angle: number, length: number},
    coefficientOfKineticFriction: number,
    coefficientOfStaticFriction: number,
    currentForceSketch: PhysicsVectorTemplate | null,
    deleteMode: boolean,
    displayChange: {xDisplay: number, yDisplay: number},
    elasticCollisions: boolean,
    forceSketches: PhysicsVectorTemplate[],
    pendulum: boolean,
    pendulumAngle: number,
    pendulumLength: number,
    positionXDisplay: number,
    positionYDisplay: number,
    showAcceleration: boolean,
    showForceMagnitudes: boolean,
    showForces: boolean,
    showVelocity: boolean,
    simulationPaused: boolean,
    simulationReset: boolean,
    simulationType: "Inclined Plane",
    sketching: boolean,
    startForces: IForce[],
    startPendulumAngle: number,
    startPosX: number,
    startPosY: number,
    stepNumber: number,
    timer: number,
    updatedForces: IForce[],
    velocityXDisplay: number,
    velocityYDisplay: number,
    wallPositions: IWallProps[],
    wedge: boolean,
    wedgeAngle: number,
    wedgeHeight: number,
    wedgeWidth: number,
    weight: boolean,
}
export default class App extends React.Component<{}, IState> {

  // Constants
  gravityMagnitude = 9.81;
  forceOfGravity: IForce = {
    description: "Gravity",
    magnitude: this.gravityMagnitude,
    directionInDegrees: 270,
  };
  xMin = 0;
  yMin = 0;
  xMax = window.innerWidth * 0.7;
  yMax = window.innerHeight * 0.8;
  color = `rgba(0,0,0,0.5)`;
  radius = 50


  constructor(props: any) {
      super(props)
      this.state = {
          accelerationXDisplay: 0,
          accelerationYDisplay: 0,
          adjustPendulumAngle: {angle: 0, length: 0},
          coefficientOfKineticFriction: 0,
          coefficientOfStaticFriction: 0,
          currentForceSketch: null,
          deleteMode: false,
          displayChange: {xDisplay: 0, yDisplay: 0},
          elasticCollisions: false,
          forceSketches: [],
          pendulum: false,
          pendulumAngle: 0,
          pendulumLength: 300,
          positionXDisplay: 0,
          positionYDisplay: 0,
          showAcceleration: false,
          showForceMagnitudes: false,
          showForces: false,
          showVelocity: false,
          simulationPaused: false,
          simulationReset: false,
          simulationType: "Inclined Plane",
          sketching: false,
          startForces: [this.forceOfGravity],
          startPendulumAngle: 0,
          startPosX: 0,
          startPosY: 0,
          stepNumber: 0,
          timer: 0,
          updatedForces: [this.forceOfGravity],
          velocityXDisplay: 0,
          velocityYDisplay: 0,
          wallPositions: [],
          wedge: false,
          wedgeAngle: 26,
          wedgeHeight: Math.tan((26 * Math.PI) / 180) * 400,
          wedgeWidth: 400,
          weight: false,
      }
  }

  // Add one weight to the simulation
  addWeight () {
    this.setState({weight: true})
    this.setState({wedge: false})
    this.setState({pendulum: false})
    this.setState({startPosY: this.yMin+this.radius})
    this.setState({startPosX: (this.xMax+this.xMin-this.radius)/2})
    this.setState({updatedForces: [this.forceOfGravity]})
    this.setState({startForces: [this.forceOfGravity]})
    this.addWalls();
    this.setState({simulationReset: !this.state.simulationReset})
  };

  // Add a wedge with a One Weight to the simulation
  addWedge () {
    this.setState({weight: true})
    this.setState({wedge: true})
    this.setState({pendulum: false})
    this.changeWedgeBasedOnNewAngle(26);
    this.addWalls();
    this.setState({startForces: [this.forceOfGravity]})
    this.updateForcesWithFriction(this.state.coefficientOfStaticFriction);
  };

  // Add a simple pendulum to the simulation
  addPendulum = () => {
    this.setState({weight: true})
    this.setState({wedge: false})
    this.setState({pendulum: true})
    let length = 300;
    let angle = 50;
    let x = length * Math.cos(((90 - angle) * Math.PI) / 180);
    let y = length * Math.sin(((90 - angle) * Math.PI) / 180);
    let xPos = xMax / 2 - x - 50;
    let yPos = y - 50 - 5;
    this.addPendulum();
    this.setState({startPosX: xPos})
    this.setState({startPosY: yPos})
    let mag = 9.81 * Math.cos((50 * Math.PI) / 180);
    let forceOfTension: IForce = {
      description: "Tension",
      magnitude: mag,
      directionInDegrees: 90 - angle,
    };
    this.setState({updatedForces: [this.forceOfGravity, forceOfTension]})
    this.setState({startForces: [this.forceOfGravity, forceOfTension]})
    this.setState({pendulumAngle: 50})
    this.setState({pendulumLength: 300})
    this.setState({adjustPendulumAngle: {angle: 50, length: 300}})
    this.removeWalls();
  };

  // Update forces when coefficient of static friction changes in freeform mode
  updateForcesWithFriction (
    coefficient: number,
    width: number = this.state.wedgeWidth,
    height: number = this.state.wedgeHeight
  ) {
    let normalForce = {
      description: "Normal Force",
      magnitude: this.forceOfGravity.magnitude * Math.cos(Math.atan(height / width)),
      directionInDegrees:
        180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
    };
    let frictionForce: IForce = {
      description: "Static Friction Force",
      magnitude:
        coefficient *
        this.forceOfGravity.magnitude *
        Math.cos(Math.atan(height / width)),
      directionInDegrees: 180 - (Math.atan(height / width) * 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 (coefficient != 0) {
      this.setState({startForces: [this.forceOfGravity, normalForce, frictionForce]})
      this.setState({updatedForces: [this.forceOfGravity, normalForce, frictionForce]});
    } else {
      this.setState({startForces: [this.forceOfGravity, normalForce]})
      this.setState({updatedForces: [this.forceOfGravity, normalForce]});
    }
  };

  // Change wedge height and width and weight position to match new wedge angle
  changeWedgeBasedOnNewAngle = (angle: number) => {
    let width = 0;
    let height = 0;
    if (angle < 50) {
      width = 400;
      height = Math.tan((angle * Math.PI) / 180) * 400;
      this.setState({wedgeWidth: width})
      this.setState({wedgeHeight: height})
    } else if (angle < 70) {
      width = 200;
      height = Math.tan((angle * Math.PI) / 180) * 200;
      this.setState({wedgeWidth: width})
      this.setState({wedgeHeight: height})
    } else {
      width = 100;
      height = Math.tan((angle * Math.PI) / 180) * 100;
      this.setState({wedgeWidth: width})
      this.setState({wedgeHeight: height})
    }

    // update weight position based on updated wedge width/height
    let yPos = (width - 50) * Math.tan((angle * Math.PI) / 180);
    if (angle < 40) {
      yPos += Math.sqrt(angle);
    } else if (angle < 58) {
      yPos += angle / 2;
    } else if (angle < 68) {
      yPos += angle;
    } else if (angle < 70) {
      yPos += angle * 1.3;
    } else if (angle < 75) {
      yPos += angle * 1.5;
    } else if (angle < 78) {
      yPos += angle * 2;
    } else if (angle < 79) {
      yPos += angle * 2.25;
    } else if (angle < 80) {
      yPos += angle * 2.6;
    } else {
      yPos += angle * 3;
    }

    this.setState({startPosX: Math.round((this.xMax * 0.5 - 200) * 10) / 10});
    this.setState({startPosY: this.getDisplayYPos(yPos)});
    this.updateForcesWithFriction(
      Number(this.state.coefficientOfStaticFriction),
      width,
      height
    );
  };

  // Helper function to go between display and real values
  getDisplayYPos = (yPos: number) => {
    return this.yMax - yPos - 2 * 50 + 5;
  };
 
  // In review mode, edit force arrow sketch on mouse movement
  editForce = (element: PhysicsVectorTemplate) => {
    if (!this.state.sketching) {
      let sketches = this.state.forceSketches.filter((sketch) => sketch != element);
      this.setState({forceSketches: sketches})
      this.setState({currentForceSketch: element})
      this.setState({sketching: true})
    }
  };

  // In review mode, used to delete force arrow sketch on SHIFT+click
  deleteForce = (element: PhysicsVectorTemplate) => {
    if (!this.state.sketching) {
      let sketches = this.state.forceSketches.filter((sketch) => sketch != element);
      this.setState({forceSketches: sketches})
    }
  };

  // Remove floor and walls from simulation
  removeWalls = () => {
    this.setState({wallPositions: []})
  };

  // Add floor and walls to simulation
  addWalls = () => {
    if (this.state.wallPositions.length == 0) {
      let walls = [];
      walls.push({ length: 70, xPos: 0, yPos: 80, angleInDegrees: 0 });
      walls.push({ length: 80, xPos: 0, yPos: 0, angleInDegrees: 90 });
      walls.push({ length: 80, xPos: 69.5, yPos: 0, angleInDegrees: 90 });
      this.setState({wallPositions: walls})
    }
  };

  
  componentDidMount() {
    // Add listener for SHIFT key, which determines if sketch force arrow will be edited or deleted on click
    document.addEventListener("keydown", (e) => {
      if (e.shiftKey) {
        this.setState({deleteMode: true})
      }
    });
    document.addEventListener("keyup", (e) => {
      if (e.shiftKey) {
        this.setState({deleteMode: false})
      }
    });

    // Timer for animating the simulation
    setInterval(() => {
      this.setState({timer: this.state.timer+1})
    }, 60);
  }

  render () { 
    return (
     <div><p>Hello world!</p></div>
    );
  }
    // <div>
    //   <div className="mechanicsSimulationContainer">
    //     <div
    //       className="mechanicsSimulationContentContainer"
    //       onPointerMove={(e) => {
    //         // if (sketching) {
    //         //   x1 = positionXDisplay + 50;
    //         //   y1 = yMax - positionYDisplay - 2 * 50 + 5 + 50;
    //         //   x2 = e.clientX;
    //         //   y2 = e.clientY;
    //         //   height = Math.abs(y1 - y2) + 120;
    //         //   width = Math.abs(x1 - x2) + 120;
    //         //   top = Math.min(y1, y2) - 60;
    //         //   left = Math.min(x1, x2) - 60;
    //         //   x1Updated = x1 - left;
    //         //   x2Updated = x2 - left;
    //         //   y1Updated = y1 - top;
    //         //   y2Updated = y2 - top;
    //         //   setCurrentForceSketch({
    //         //     top: top,
    //         //     left: left,
    //         //     width: width,
    //         //     height: height,
    //         //     x1: x1Updated,
    //         //     y1: y1Updated,
    //         //     x2: x2Updated,
    //         //     y2: y2Updated,
    //         //     weightX: positionXDisplay,
    //         //     weightY: positionYDisplay,
    //         //   });
    //         // }
    //       }}
    //       onPointerDown={(e) => {
    //         // if (sketching && currentForceSketch) {
    //         //   setSketching(false);
    //         //   sketches = forceSketches;
    //         //   sketches.push(currentForceSketch);
    //         //   setForceSketches(sketches);
    //         //   setCurrentForceSketch(null);
    //         // }
    //       }}
    //     >
    //       <div className="mechanicsSimulationButtonsAndElements">
    //         <div className="mechanicsSimulationElements">
    //           {/* {showForces && currentForceSketch && simulationPaused && (
    //             <div
    //               style={{
    //                 position: "fixed",
    //                 top: currentForceSketch.top,
    //                 left: currentForceSketch.left,
    //               }}
    //             >
    //               <svg
    //                 width={currentForceSketch.width + "px"}
    //                 height={currentForceSketch.height + "px"}
    //               >
    //                 <defs>
    //                   <marker
    //                     id="sketchArrow"
    //                     markerWidth="10"
    //                     markerHeight="10"
    //                     refX="0"
    //                     refY="2"
    //                     orient="auto"
    //                     markerUnits="strokeWidth"
    //                   >
    //                     <path d="M0,0 L0,4 L6,2 z" fill={color} />
    //                   </marker>
    //                 </defs>
    //                 <line
    //                   x1={currentForceSketch.x1}
    //                   y1={currentForceSketch.y1}
    //                   x2={currentForceSketch.x2}
    //                   y2={currentForceSketch.y2}
    //                   stroke={color}
    //                   strokeWidth="10"
    //                   markerEnd="url(#sketchArrow)"
    //                 />
    //               </svg>
    //             </div>
    //           )} */}
    //           {/* {showForces &&
    //             forceSketches.length > 0 &&
    //             simulationPaused &&
    //             forceSketches.map((element: PhysicsVectorTemplate, index) => {
    //               return (
    //                 <div
    //                   key={index}
    //                   style={{
    //                     position: "fixed",
    //                     top: element.top + (positionYDisplay - element.weightY),
    //                     left:
    //                       element.left + (positionXDisplay - element.weightX),
    //                   }}
    //                 >
    //                   <svg
    //                     width={element.width + "px"}
    //                     height={element.height + "px"}
    //                   >
    //                     <defs>
    //                       <marker
    //                         id="sketchArrow"
    //                         markerWidth="10"
    //                         markerHeight="10"
    //                         refX="0"
    //                         refY="2"
    //                         orient="auto"
    //                         markerUnits="strokeWidth"
    //                       >
    //                         <path d="M0,0 L0,4 L6,2 z" fill={color} />
    //                       </marker>
    //                     </defs>
    //                     <line
    //                       x1={element.x1}
    //                       y1={element.y1}
    //                       x2={element.x2}
    //                       y2={element.y2}
    //                       stroke={color}
    //                       strokeWidth="10"
    //                       markerEnd="url(#sketchArrow)"
    //                       onClick={() => {
    //                         if (deleteMode) {
    //                           deleteForce(element);
    //                         } else {
    //                           editForce(element);
    //                         }
    //                       }}
    //                     />
    //                   </svg>
    //                 </div>
    //               );
    //             })} */}
    //           {weight && (
    //             <Weight
    //               adjustPendulumAngle={adjustPendulumAngle}
    //               color={"red"}
    //               displayXPosition={positionXDisplay}
    //               displayXVelocity={velocityXDisplay}
    //               displayYPosition={positionYDisplay}
    //               displayYVelocity={velocityYDisplay}
    //               elasticCollisions={elasticCollisions}
    //               incrementTime={timer}
    //               mass={1}
    //               mode={mode}
    //               noMovement={noMovement}
    //               paused={simulationPaused}
    //               pendulum={pendulum}
    //               pendulumAngle={pendulumAngle}
    //               pendulumLength={pendulumLength}
    //               radius={50}
    //               reset={simulationReset}
    //               showForceMagnitudes={showForceMagnitudes}
    //               setSketching={setSketching}
    //               setDisplayXAcceleration={setAccelerationXDisplay}
    //               setDisplayXPosition={setPositionXDisplay}
    //               setDisplayXVelocity={setVelocityXDisplay}
    //               setDisplayYAcceleration={setAccelerationYDisplay}
    //               setDisplayYPosition={setPositionYDisplay}
    //               setDisplayYVelocity={setVelocityYDisplay}
    //               setPaused={setSimulationPaused}
    //               setPendulumAngle={setPendulumAngle}
    //               setPendulumLength={setPendulumLength}
    //               setStartPendulumAngle={setStartPendulumAngle}
    //               setUpdatedForces={setUpdatedForces}
    //               showAcceleration={showAcceleration}
    //               showForces={showForces}
    //               showVelocity={showVelocity}
    //               startForces={startForces}
    //               startPosX={startPosX}
    //               startPosY={startPosY}
    //               timestepSize={0.002}
    //               updateDisplay={displayChange}
    //               updatedForces={updatedForces}
    //               walls={wallPositions}
    //               wedge={wedge}
    //               wedgeHeight={wedgeHeight}
    //               wedgeWidth={wedgeWidth}
    //               coefficientOfKineticFriction={Number(
    //                 coefficientOfKineticFriction
    //               )}
    //             />
    //           )}
    //           {wedge && (
    //             <Wedge
    //               startWidth={wedgeWidth}
    //               startHeight={wedgeHeight}
    //               startLeft={xMax * 0.5 - 200}
    //             />
    //           )}
    //         </div>
    //         <div>
    //           {wallPositions.map((element, index) => {
    //             return (
    //               <Wall
    //                 key={index}
    //                 length={element.length}
    //                 xPos={element.xPos}
    //                 yPos={element.yPos}
    //                 angleInDegrees={element.angleInDegrees}
    //               />
    //             );
    //           })}
    //         </div>
    //       </div>
    //     </div>
    //     <div className="mechanicsSimulationEquationContainer">
    //       <div className="mechanicsSimulationControls">
    //        <div>
    //           {simulationPaused && (
    //            <button onClick={() => {
    //             setSimulationPaused(false);
    //           }} >START</button>
    //           )}
    //           {!simulationPaused && (
    //            <button onClick={() => {
    //             setSimulationPaused(true);
    //           }} >PAUSE</button>
    //           )}
    //           {simulationPaused && (
    //            <button onClick={() => {
    //             setSimulationReset(!simulationReset);
    //           }} >RESET</button>
    //           )}
    //           </div>
    //       </div>
    //     </div>
    //   </div>
    // </div>
}
