import "./PhysicsSimulationBox.scss";
import { FieldView, FieldViewProps } from './FieldView';
import React = require('react');
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { observer } from 'mobx-react';
import "./PhysicsSimulationBox.scss";
import Weight from "./PhysicsSimulationWeight";
import Wall  from "./PhysicsSimulationWall"
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CheckBox } from "../search/CheckBox";
import PauseIcon from "@mui/icons-material/Pause";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import ReplayIcon from "@mui/icons-material/Replay";
import QuestionMarkIcon from "@mui/icons-material/QuestionMark";
import ArrowLeftIcon from "@mui/icons-material/ArrowLeft";
import ArrowRightIcon from "@mui/icons-material/ArrowRight";
import EditIcon from "@mui/icons-material/Edit";
import EditOffIcon from "@mui/icons-material/EditOff";
import {
  Box,
  Button,
  Checkbox,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogContentText,
  DialogActions,
  FormControl,
  FormControlLabel,
  FormGroup,
  IconButton,
  LinearProgress,
  Stack,
} from "@mui/material";
import Typography from "@mui/material/Typography";
import React, { useEffect, useState } from "react";
import "./App.scss";
import { InputField } from "./InputField";
import questions from "./PhysicsSimulationQuestions.json";
import tutorials from "./PhysicsSimulationTutorial.json";
import { IWallProps, Wall } from "./Wall";
import { IForce, Weight } from "./Weight";
interface VectorTemplate {
  top: number;
  left: number;
  width: number;
  height: number;
  x1: number;
  y1: number;
  x2: number;
  y2: number;
  weightX: number;
  weightY: number;
}
interface QuestionTemplate {
  questionSetup: string[];
  variablesForQuestionSetup: string[];
  question: string;
  answerParts: string[];
  answerSolutionDescriptions: string[];
  goal: string;
  hints: { description: string; content: string }[];
}

interface TutorialTemplate {
  question: string;
  steps: {
    description: string;
    content: string;
    forces: {
      description: string;
      magnitude: number;
      directionInDegrees: number;
      component: boolean;
    }[];
    showMagnitude: boolean;
  }[];
}

@observer
export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {

  public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PhysicsSimulationBox, fieldKey); }

  constructor(props: any) {
    super(props);
  }

  // Constants
  const xMin = 0;
  const yMin = 0;
  const xMax = window.innerWidth * 0.7;
  const yMax = window.innerHeight * 0.8;
  const color = `rgba(0,0,0,0.5)`;
  const radius = 50;
  const wallPositions: IWallProps[] = [];


  componentDidMount() {
    this.wallPositions.push({ length: 70, xPos: 0, yPos: 0, angleInDegrees: 0 });
    this.wallPositions.push({ length: 70, xPos: 0, yPos: 80, angleInDegrees: 0 });
    this.wallPositions.push({ length: 80, xPos: 0, yPos: 0, angleInDegrees: 90 });
    this.wallPositions.push({ length: 80, xPos: 69.5, yPos: 0, angleInDegrees: 90 });

    // Used throughout sims
    this.xMax = this.layoutDoc._width;
    this.yMax = this.layoutDoc._height;
    this.radius = 0.1*this.layoutDoc._height;
    this.dataDoc.reviewCoefficient = this.dataDoc.reviewCoefficient ?? 0;
    this.dataDoc.questionVariables = this.dataDoc.questionVariables ?? [];
    this.dataDoc.accelerationXDisplay = this.dataDoc.accelerationXDisplay ?? 0;
    this.dataDoc.accelerationYDisplay = this.dataDoc.accelerationYDisplay ?? 0;
    this.dataDoc.componentForces = this.dataDoc.componentForces ?? [];
    this.dataDoc.displayChange = this.dataDoc.displayChange ?? { xDisplay: 0, yDisplay: 0 };
    this.dataDoc.elasticCollisions = this.dataDoc.elasticCollisions ?? false;
    this.dataDoc.gravity = this.dataDoc.gravity ?? -9.81;
    this.dataDoc.mass = this.dataDoc.mass ?? 1;
    this.dataDoc.mode = this.dataDoc.mode ?? "Freeform";
    this.dataDoc.positionXDisplay = this.dataDoc.positionXDisplay ?? 0;
    this.dataDoc.positionYDisplay = this.dataDoc.positionYDisplay ?? 0;
    this.dataDoc.resetAll = this.dataDoc.resetAll ?? true;
    this.dataDoc.showAcceleration = this.dataDoc.showAcceleration ?? false;
    this.dataDoc.showComponentForces = this.dataDoc.showComponentForces ?? false;
    this.dataDoc.showForces = this.dataDoc.showForces ?? true;
    this.dataDoc.showForceMagnitudes = this.dataDoc.showForceMagnitudes ?? true;
    this.dataDoc.showVelocity = this.dataDoc.showVelocity ?? false;
    this.dataDoc.simulationPaused = this.dataDoc.simulationPaused ?? true;
    this.dataDoc.simulationReset = this.dataDoc.simulationReset ?? false;
    this.dataDoc.simulationSpeed = this.dataDoc.simulationSpeed ?? 2;
    this.dataDoc.simulationType = this.dataDoc.simulationType ?? "Inclined Plane";
    this.dataDoc.startForces = this.dataDoc.startForces ?? [];
    this.dataDoc.startPosX = this.dataDoc.startPosX ?? 0;
    this.dataDoc.startPosY = this.dataDoc.startPosY ?? 0;
    this.dataDoc.startVelX = this.dataDoc.startVelX ?? 0;
    this.dataDoc.startVelY = this.dataDoc.startVelY ?? 0;
    this.dataDoc.stepNumber = this.dataDoc.stepNumber ?? 0;
    this.dataDoc.timer = this.dataDoc.timer ?? 0;
    this.dataDoc.updatedForces = this.dataDoc.updatedForces ?? [];
    this.dataDoc.velocityXDisplay = this.dataDoc.velocityXDisplay ?? 0;
    this.dataDoc.velocityYDisplay = this.dataDoc.velocityYDisplay ?? 0;

    // Used for review mode
    this.dataDoc.answerInputFields = this.dataDoc.answerInputFields ?? <div></div>;
    this.dataDoc.currentForceSketch = this.dataDoc.currentForceSketch ?? null;
    this.dataDoc.deleteMode = this.dataDoc.deleteMode ?? false;
    this.dataDoc.forceSketches = this.dataDoc.forceSketches ?? [];
    this.dataDoc.hintDialogueOpen = this.dataDoc.hintDialogueOpen ?? false;
    this.dataDoc.noMovement = this.dataDoc.noMovement ?? false;
    this.dataDoc.questionNumber = this.dataDoc.questionNumber ?? 0;
    this.dataDoc.questionPartOne = this.dataDoc.questionPartOne ?? "";
    this.dataDoc.questionPartTwo = this.dataDoc.questionPartTwo ?? "";
    this.dataDoc.reviewGravityAngle = this.dataDoc.reviewGravityAngle ?? 0;
    this.dataDoc.reviewGravityMagnitude = this.dataDoc.reviewGravityMagnitude ?? 0;
    this.dataDoc.reviewNormalAngle = this.dataDoc.reviewNormalAngle ?? 0;
    this.dataDoc.reviewNormalMagnitude = this.dataDoc.reviewNormalMagnitude ?? 0;
    this.dataDoc.reviewStaticAngle = this.dataDoc.reviewStaticAngle ?? 0;
    this.dataDoc.reviewStaticMagnitude = this.dataDoc.reviewStaticMagnitude ?? 0;
    this.dataDoc.selectedSolutions = this.dataDoc.selectedSolutions ?? [];
    this.dataDoc.selectedQuestion = this.dataDoc.selectedQuestion ?? questions.inclinePlane[0];
    this.dataDoc.sketching = this.dataDoc.sketching ?? false;

    // Used for tutorial mode
    this.dataDoc.selectedTutorial = this.dataDoc.selectedTutorial ?? tutorials.inclinePlane;

    // Used for uniform circular motion
    this.dataDoc.circularMotionRadius = this.dataDoc.circularMotionRadius ?? 150;

    // Used for spring simulation
    this.dataDoc.springConstant = this.dataDoc.springConstant ?? 0.5;
    this.dataDoc.springRestLength = this.dataDoc.springRestLength ?? 200;
    this.dataDoc.springStartLength = this.dataDoc.springStartLength ?? 200;

    // Used for pendulum simulation
    this.dataDoc.adjustPendulumAngle = this.dataDoc.adjustPendulumAngle ?? { angle: 0, length: 0 };
    this.dataDoc.pendulumAngle = this.dataDoc.pendulumAngle ?? 0;
    this.dataDoc.pendulumLength = this.dataDoc.pendulumLength ?? 300;
    this.dataDoc.startPendulumAngle = this.dataDoc.startPendulumAngle ?? 0;

    // Used for wedge simulation
    this.dataDoc.coefficientOfKineticFriction = this.dataDoc.coefficientOfKineticFriction ?? 0;
    this.dataDoc.coefficientOfStaticFriction = this.dataDoc.coefficientOfStaticFriction ?? 0;
    this.dataDoc.wedgeAngle = this.dataDoc.wedgeAngle ?? 26;
    this.dataDoc.wedgeHeight = this.dataDoc.wedgeHeight ?? Math.tan((26 * Math.PI) / 180) * 400;
    this.dataDoc.wedgeWidth = this.dataDoc.wedgeWidth ?? 400;

    // Used for pulley simulation
    this.dataDoc.positionXDisplay2 = this.dataDoc.positionXDisplay2 ?? 0;
    this.dataDoc.velocityXDisplay2 = this.dataDoc.velocityXDisplay2 ?? 0;
    this.dataDoc.accelerationXDisplay2 = this.dataDoc.accelerationXDisplay2 ?? 0;
    this.dataDoc.positionYDisplay2 = this.dataDoc.positionYDisplay2 ?? 0;
    this.dataDoc.velocityYDisplay2 = this.dataDoc.velocityYDisplay2 ?? 0;
    this.dataDoc.accelerationYDisplay2 = this.dataDoc.accelerationYDisplay2 ?? 0;
    this.dataDoc.startPosX2 = this.dataDoc.startPosX2 ?? 0;
    this.dataDoc.startPosY2 = this.dataDoc.startPosY2 ?? 0;
    this.dataDoc.displayChange2 = this.dataDoc.displayChange2 ?? { xDisplay: 0, yDisplay: 0 };
    this.dataDoc.startForces2 = this.dataDoc.startForces2 ?? [];
    this.dataDoc.updatedForces2 = this.dataDoc.updatedForces2 ?? [];
    this.dataDoc.mass2 = this.dataDoc.mass2 ?? 1;
  }

  componentDidUpdate() {
    this.xMax = this.layoutDoc._width;
    this.yMax = this.layoutDoc._height;
    this.radius = 0.1*this.layoutDoc._height;
  }

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

  // Update forces when coefficient of static friction changes in freeform mode
  updateForcesWithFriction = (
    coefficient: number,
    width: number = this.wedgeWidth,
    height: number = this.wedgeHeight
  ) => {
    const normalForce: IForce = {
      description: "Normal Force",
      magnitude: Math.abs(this.gravity) * Math.cos(Math.atan(height / width)) * this.mass,
      directionInDegrees:
        180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
      component: false,
    };
    let frictionForce: IForce = {
      description: "Static Friction Force",
      magnitude:
        coefficient *
        Math.abs(this.gravity) *
        Math.cos(Math.atan(height / width)) *
        this.mass,
      directionInDegrees: 180 - (Math.atan(height / width) * 180) / Math.PI,
      component: false,
    };
    // reduce magnitude or friction force if necessary such that block cannot slide up plane
    let yForce = -Math.abs(this.gravity) * this.mass;
    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) +
          Math.abs(this.gravity) * this.mass) /
        Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
    }
    const frictionForceComponent: IForce = {
      description: "Static Friction Force",
      magnitude:
        coefficient * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)),
      directionInDegrees: 180 - (Math.atan(height / width) * 180) / Math.PI,
      component: true,
    };
    const normalForceComponent: IForce = {
      description: "Normal Force",
      magnitude: Math.abs(this.gravity) * Math.cos(Math.atan(height / width)),
      directionInDegrees:
        180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
      component: true,
    };
    const gravityParallel: IForce = {
      description: "Gravity Parallel Component",
      magnitude:
      this.mass *
        Math.abs(this.gravity) *
        Math.sin(Math.PI / 2 - Math.atan(height / width)),
      directionInDegrees:
        180 - 90 - (Math.atan(height / width) * 180) / Math.PI + 180,
      component: true,
    };
    const gravityPerpendicular: IForce = {
      description: "Gravity Perpendicular Component",
      magnitude:
      this.mass *
        Math.abs(this.gravity) *
        Math.cos(Math.PI / 2 - Math.atan(height / width)),
      directionInDegrees: 360 - (Math.atan(height / width) * 180) / Math.PI,
      component: true,
    };
    const gravityForce: IForce = {
      description: "Gravity",
      magnitude: this.mass * Math.abs(this.gravity),
      directionInDegrees: 270,
      component: false,
    };
    if (coefficient != 0) {
      this.dataDoc.startForces = [gravityForce, normalForce, frictionForce];
      this.dataDoc.updatedForces = [gravityForce, normalForce, frictionForce];
      this.dataDoc.componentForces = [
        frictionForceComponent,
        normalForceComponent,
        gravityParallel,
        gravityPerpendicular,
      ];
    } else {
      this.dataDoc.startForces = [gravityForce, normalForce];
      this.dataDoc.updatedForces = [gravityForce, normalForce];
      this.dataDoc.componentForces = [
        normalForceComponent,
        gravityParallel,
        gravityPerpendicular,
      ];
    }
  };

  // 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.dataDoc.wedgeWidth = width;
      this.dataDoc.wedgeHeight = height;
    } else if (angle < 70) {
      width = 200;
      height = Math.tan((angle * Math.PI) / 180) * 200;
      this.dataDoc.wedgeWidth = width;
      this.dataDoc.wedgeHeight = height;
    } else {
      width = 100;
      height = Math.tan((angle * Math.PI) / 180) * 100;
      this.dataDoc.wedgeWidth = width;
      this.dataDoc.wedgeHeight = height;
    }

    // update weight position based on updated wedge width/height
    let yPos = (width - this.radius) * 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.dataDoc.startPosX = Math.round((this.xMax * 0.5 - 200) * 10) / 10;
    this.dataDoc.startPosY = this.getDisplayYPos(yPos);
    if (this.dataDoc.mode == "Freeform") {
      this.updateForcesWithFriction(
        Number(this.dataDoc.coefficientOfStaticFriction),
        width,
        height
      );
    }
  };

  // In review mode, update forces when coefficient of static friction changed
  updateReviewForcesBasedOnCoefficient = (coefficient: number) => {
    let theta: number = Number(this.dataDoc.wedgeAngle);
    let index =
      this.dataDoc.selectedQuestion.variablesForQuestionSetup.indexOf("theta - max 45");
    if (index >= 0) {
      theta = this.dataDoc.questionVariables[index];
    }
    if (isNaN(theta)) {
      return;
    }
    this.dataDoc.reviewGravityMagnitude = (Math.abs(this.dataDoc.gravity));
    this.dataDoc.reviewGravityAngle = (270);
    this.dataDoc.reviewNormalMagnitude = (
      Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180)
    );
    this.dataDoc.reviewNormalAngle = (90 - theta);
    let yForce = -Math.abs(this.dataDoc.gravity);
    yForce +=
      Math.abs(this.dataDoc.gravity) *
      Math.cos((theta * Math.PI) / 180) *
      Math.sin(((90 - theta) * Math.PI) / 180);
    yForce +=
      coefficient *
      Math.abs(this.dataDoc.gravity) *
      Math.cos((theta * Math.PI) / 180) *
      Math.sin(((180 - theta) * Math.PI) / 180);
    let friction =
      coefficient * Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180);
    if (yForce > 0) {
      friction =
        (-(Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180)) *
          Math.sin(((90 - theta) * Math.PI) / 180) +
          Math.abs(this.dataDoc.gravity)) /
        Math.sin(((180 - theta) * Math.PI) / 180);
    }
    this.dataDoc.reviewStaticMagnitude = (friction);
    this.dataDoc.reviewStaticAngle = (180 - theta);
  };

  // In review mode, update forces when wedge angle changed
  updateReviewForcesBasedOnAngle = (angle: number) => {
    this.dataDoc.reviewGravityMagnitude = (Math.abs(this.dataDoc.gravity));
    this.dataDoc.reviewGravityAngle = (270);
    this.dataDoc.reviewNormalMagnitude = (
      Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180)
    );
    this.dataDoc.reviewNormalAngle = (90 - angle);
    let yForce = -Math.abs(this.dataDoc.gravity);
    yForce +=
      Math.abs(this.dataDoc.gravity) *
      Math.cos((Number(angle) * Math.PI) / 180) *
      Math.sin(((90 - Number(angle)) * Math.PI) / 180);
    yForce +=
      this.dataDoc.reviewCoefficient *
      Math.abs(this.dataDoc.gravity) *
      Math.cos((Number(angle) * Math.PI) / 180) *
      Math.sin(((180 - Number(angle)) * Math.PI) / 180);
    let friction =
      this.dataDoc.reviewCoefficient *
      Math.abs(this.dataDoc.gravity) *
      Math.cos((Number(angle) * Math.PI) / 180);
    if (yForce > 0) {
      friction =
        (-(Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180)) *
          Math.sin(((90 - Number(angle)) * Math.PI) / 180) +
          Math.abs(this.dataDoc.gravity)) /
        Math.sin(((180 - Number(angle)) * Math.PI) / 180);
    }
    this.dataDoc.reviewStaticMagnitude = (friction);
    this.dataDoc.reviewStaticAngle = (180 - angle);
  };


  render () { 
  }