/* eslint-disable camelcase */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react/no-array-index-key */
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable no-return-assign */
import ArrowLeftIcon from '@mui/icons-material/ArrowLeft';
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
import PauseIcon from '@mui/icons-material/Pause';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
import ReplayIcon from '@mui/icons-material/Replay';
import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControl, FormControlLabel, FormGroup, IconButton, LinearProgress, Stack } from '@mui/material';
import Typography from '@mui/material/Typography';
import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { NumListCast } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
import './PhysicsSimulationBox.scss';
import InputField from './PhysicsSimulationInputField';
import questions from './PhysicsSimulationQuestions.json';
import tutorials from './PhysicsSimulationTutorial.json';
import Wall from './PhysicsSimulationWall';
import Weight from './PhysicsSimulationWeight';
import { Docs } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';

interface IWallProps {
    length: number;
    xPos: number;
    yPos: number;
    angleInDegrees: number;
}
interface IForce {
    description: string;
    magnitude: number;
    directionInDegrees: number;
}
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 class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(PhysicsSimulationBox, fieldKey);
    }

    _widthDisposer: IReactionDisposer | undefined;
    @observable _simReset = 0;

    // semi-Constants
    xMin = 0;
    yMin = 0;
    xMax = this._props.PanelWidth() * 0.6;
    yMax = this._props.PanelHeight();
    color = `rgba(0,0,0,0.5)`;
    radius = 50;
    wallPositions: IWallProps[] = [];

    @computed get circularMotionRadius() {
        return (NumCast(this.dataDoc.circularMotionRadius, 150) * this._props.PanelWidth()) / 1000;
    }
    @computed get gravity() {
        return NumCast(this.dataDoc.simulation_gravity, -9.81);
    }
    @computed get simulationType() {
        return StrCast(this.dataDoc.simulation_type, 'Inclined Plane');
    }
    @computed get simulationMode() {
        return StrCast(this.dataDoc.simulation_mode, 'Freeform');
    }
    // Used for spring simulation
    @computed get springConstant() {
        return NumCast(this.dataDoc.spring_constant, 0.5);
    }
    @computed get springLengthRest() {
        return NumCast(this.dataDoc.spring_lengthRest, 200);
    }
    @computed get springLengthStart() {
        return NumCast(this.dataDoc.spring_lengthStart, 200);
    }

    @computed get pendulumAngle() {
        return NumCast(this.dataDoc.pendulum_angle);
    }
    @computed get pendulumAngleStart() {
        return NumCast(this.dataDoc.pendulum_angleStart);
    }
    @computed get pendulumLength() {
        return NumCast(this.dataDoc.pendulum_length);
    }
    @computed get pendulumLengthStart() {
        return NumCast(this.dataDoc.pendulum_lengthStart);
    }

    // Used for wedge simulation
    @computed get wedgeAngle() {
        return NumCast(this.dataDoc.wedge_angle, 26);
    }
    @computed get wedgeHeight() {
        return NumCast(this.dataDoc.wedge_height, Math.tan((26 * Math.PI) / 180) * this.xMax * 0.5);
    }
    @computed get wedgeWidth() {
        return NumCast(this.dataDoc.wedge_width, this.xMax * 0.5);
    }
    @computed get mass1() {
        return NumCast(this.dataDoc.mass1, 1);
    }
    @computed get mass2() {
        return NumCast(this.dataDoc.mass2, 1);
    }

    @computed get mass1Radius() {
        return NumCast(this.dataDoc.mass1_radius, 30);
    }
    @computed get mass1PosXStart() {
        return NumCast(this.dataDoc.mass1_positionXstart);
    }
    @computed get mass1PosYStart() {
        return NumCast(this.dataDoc.mass1_positionYstart);
    }
    @computed get mass1VelXStart() {
        return NumCast(this.dataDoc.mass1_velocityXstart);
    }
    @computed get mass1VelYStart() {
        return NumCast(this.dataDoc.mass1_velocityYstart);
    }

    @computed get mass2PosXStart() {
        return NumCast(this.dataDoc.mass2_positionXstart);
    }
    @computed get mass2PosYStart() {
        return NumCast(this.dataDoc.mass2_positionYstart);
    }
    @computed get mass2VelXStart() {
        return NumCast(this.dataDoc.mass2_velocityXstart);
    }
    @computed get mass2VelYStart() {
        return NumCast(this.dataDoc.mass2_velocityYstart);
    }

    @computed get selectedQuestion() {
        return this.dataDoc.selectedQuestion ? (JSON.parse(StrCast(this.dataDoc.selectedQuestion)) as QuestionTemplate) : questions.inclinePlane[0];
    }
    @computed get tutorial() {
        return this.dataDoc.tutorial ? (JSON.parse(StrCast(this.dataDoc.tutorial)) as TutorialTemplate) : tutorials.inclinePlane;
    }
    @computed get selectedSolutions() {
        return NumListCast(this.dataDoc.selectedSolutions);
    }
    @computed get questionPartOne() {
        return StrCast(this.dataDoc.questionPartOne);
    }
    @computed get questionPartTwo() {
        return StrCast(this.dataDoc.questionPartTwo);
    }

    componentWillUnmount() {
        this._widthDisposer?.();
    }

    componentDidMount() {
        // Setup and update simulation
        this._widthDisposer = reaction(() => [this._props.PanelWidth(), this._props.PanelHeight()], this.setupSimulation, { fireImmediately: true });

        // Create walls
        this.wallPositions = [
            { length: 100, xPos: 0, yPos: 0, angleInDegrees: 0 },
            { length: 100, xPos: 0, yPos: 100, angleInDegrees: 0 },
            { length: 100, xPos: 0, yPos: 0, angleInDegrees: 90 },
            { length: 100, xPos: (this.xMax / this._props.PanelWidth()) * 100, yPos: 0, angleInDegrees: 90 },
        ];
    }

    componentDidUpdate(prevProps: Readonly<FieldViewProps>) {
        super.componentDidUpdate(prevProps);
        if (this.xMax !== this._props.PanelWidth() * 0.6 || this.yMax !== this._props.PanelHeight()) {
            this.xMax = this._props.PanelWidth() * 0.6;
            this.yMax = this._props.PanelHeight();
            this.setupSimulation();
        }
    }

    gravityForce = (mass: number): IForce => ({
        description: 'Gravity',
        magnitude: mass * Math.abs(this.gravity),
        directionInDegrees: 270,
    });

    @action
    setupSimulation = () => {
        const { simulationType } = this;
        const mode = this.simulationMode;
        this.dataDoc.simulation_paused = true;
        if (simulationType !== 'Circular Motion') {
            this.dataDoc.mass1_velocityXstart = 0;
            this.dataDoc.mass1_velocityYstart = 0;
            this.dataDoc.mass1_velocityX = 0;
            this.dataDoc.mass1_velocityY = 0;
        }
        if (mode === 'Freeform') {
            this.dataDoc.simulation_showForceMagnitudes = true;
            // prettier-ignore
            switch (simulationType) {
                case 'One Weight': 
                    this.dataDoc.simulation_showComponentForces = false;
                    this.dataDoc.mass1_positionYstart = this.yMin + this.mass1Radius;
                    this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius;
                    this.dataDoc.mass1_positionY = this.getDisplayYPos(this.yMin + this.mass1Radius);
                    this.dataDoc.mass1_positionX = (this.xMax + this.xMin) / 2 - this.mass1Radius;
                    this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
                    this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]);
                    break;
                case 'Inclined Plane':  this.setupInclinedPlane(); break;
                case 'Pendulum':        this.setupPendulum(); break;
                case 'Spring':          this.setupSpring(); break;
                case 'Circular Motion': this.setupCircular(20); break;
                case 'Pulley':          this.setupPulley(); break;
                case 'Suspension':      this.setupSuspension();break;
                default:
            }
            this._simReset++;
        } else if (mode === 'Review') {
            this.dataDoc.simulation_showComponentForces = false;
            this.dataDoc.simulation_showForceMagnitudes = true;
            this.dataDoc.simulation_showAcceleration = false;
            this.dataDoc.simulation_showVelocity = false;
            this.dataDoc.simulation_showForces = true;
            this.generateNewQuestion();
            // prettier-ignore
            switch (simulationType) {
                case 'One Weight' : break;// TODO - one weight review problems
                case 'Spring':  this.setupSpring(); break; // TODO - spring review problems
                case 'Inclined Plane': this.dataDoc.mass1_forcesUpdated =  this.dataDoc.mass1_forcesStart = ''; break;
                case 'Pendulum':  this.setupPendulum(); break;  // TODO - pendulum review problems
                case 'Circular Motion':  this.setupCircular(0); break;  // TODO - circular motion review problems
                case 'Pulley':  this.setupPulley(); break; // TODO - pulley tutorial review problems
                case 'Suspension': this.setupSuspension(); break;  // TODO - suspension tutorial review problems
                default:
            }
        } else if (mode === 'Tutorial') {
            this.dataDoc.simulation_showComponentForces = false;
            this.dataDoc.tutorial_stepNumber = 0;
            this.dataDoc.simulation_showAcceleration = false;
            if (this.simulationType !== 'Circular Motion') {
                this.dataDoc.mass1_velocityX = 0;
                this.dataDoc.mass1_velocityY = 0;
                this.dataDoc.simulation_showVelocity = false;
            } else {
                this.dataDoc.mass1_velocityX = 20;
                this.dataDoc.mass1_velocityY = 0;
                this.dataDoc.simulation_showVelocity = true;
            }

            switch (this.simulationType) {
                case 'One Weight':
                    this.dataDoc.simulation_showForces = true;
                    this.dataDoc.mass1_positionYstart = this.yMax - 100;
                    this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius;
                    this.dataDoc.tutorial = JSON.stringify(tutorials.freeWeight);
                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.freeWeight.steps[0].forces);
                    this.dataDoc.simulation_showForceMagnitudes = tutorials.freeWeight.steps[0].showMagnitude;
                    break;
                case 'Spring':
                    this.dataDoc.simulation_showForces = true;
                    this.setupSpring();
                    this.dataDoc.mass1_positionYstart = this.yMin + 200 + 19.62;
                    this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius;
                    this.dataDoc.tutorial = JSON.stringify(tutorials.spring);
                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.spring.steps[0].forces);
                    this.dataDoc.simulation_showForceMagnitudes = tutorials.spring.steps[0].showMagnitude;
                    break;
                case 'Pendulum':
                    this.setupPendulum();
                    this.dataDoc.tutorial = JSON.stringify(tutorials.pendulum);
                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.pendulum.steps[0].forces);
                    this.dataDoc.simulation_showForceMagnitudes = tutorials.pendulum.steps[0].showMagnitude;
                    break;
                case 'Inclined Plane':
                    this.dataDoc.wedge_angle = 26;
                    this.setupInclinedPlane();
                    this.dataDoc.simulation_showForces = true;
                    this.dataDoc.tutorial = JSON.stringify(tutorials.inclinePlane);
                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.inclinePlane.steps[0].forces);
                    this.dataDoc.simulation_showForceMagnitudes = tutorials.inclinePlane.steps[0].showMagnitude;
                    break;
                case 'Circular Motion':
                    this.dataDoc.simulation_showForces = true;
                    this.setupCircular(40);
                    this.dataDoc.tutorial = JSON.stringify(tutorials.circular);
                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.circular.steps[0].forces);
                    this.dataDoc.simulation_showForceMagnitudes = tutorials.circular.steps[0].showMagnitude;
                    break;
                case 'Pulley':
                    this.dataDoc.simulation_showForces = true;
                    this.setupPulley();
                    this.dataDoc.tutorial = JSON.stringify(tutorials.pulley);
                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.pulley.steps[0].forces);
                    this.dataDoc.simulation_showForceMagnitudes = tutorials.pulley.steps[0].showMagnitude;
                    break;
                case 'Suspension':
                    this.dataDoc.simulation_showForces = true;
                    this.setupSuspension();
                    this.dataDoc.tutorial = JSON.stringify(tutorials.suspension);
                    this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.suspension.steps[0].forces);
                    this.dataDoc.simulation_showForceMagnitudes = tutorials.suspension.steps[0].showMagnitude;
                    break;
                default:
            }
            this._simReset++;
        }
    };

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

    // Update forces when coefficient of static friction changes in freeform mode
    updateForcesWithFriction = (coefficient: number, width = this.wedgeWidth, height = this.wedgeHeight) => {
        const normalForce: IForce = {
            description: 'Normal Force',
            magnitude: Math.abs(this.gravity) * Math.cos(Math.atan(height / width)) * this.mass1,
            directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
        };
        const frictionForce: IForce = {
            description: 'Static Friction Force',
            magnitude: coefficient * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)) * this.mass1,
            directionInDegrees: 180 - (Math.atan(height / width) * 180) / Math.PI,
        };
        // reduce magnitude or friction force if necessary such that block cannot slide up plane
        let yForce = -Math.abs(this.gravity) * this.mass1;
        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.mass1) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
        }

        const normalForceComponent: IForce = {
            description: 'Normal Force',
            magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)),
            directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
        };
        const gravityParallel: IForce = {
            description: 'Gravity Parallel Component',
            magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin(Math.PI / 2 - Math.atan(height / width)),
            directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI + 180,
        };
        const gravityPerpendicular: IForce = {
            description: 'Gravity Perpendicular Component',
            magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(Math.PI / 2 - Math.atan(height / width)),
            directionInDegrees: 360 - (Math.atan(height / width) * 180) / Math.PI,
        };
        const gravityForce = this.gravityForce(this.mass1);
        if (coefficient !== 0) {
            this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce, normalForce, frictionForce]);
            this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce, normalForce, frictionForce]);
            this.dataDoc.mass1_componentForces = JSON.stringify([frictionForce, normalForceComponent, gravityParallel, gravityPerpendicular]);
        } else {
            this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce, normalForce]);
            this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce, normalForce]);
            this.dataDoc.mass1_componentForces = JSON.stringify([normalForceComponent, gravityParallel, gravityPerpendicular]);
        }
    };

    // Change wedge height and width and weight position to match new wedge angle
    changeWedgeBasedOnNewAngle = (angle: number) => {
        const radAng = (angle * Math.PI) / 180;
        this.dataDoc.wedge_width = this.xMax * 0.5;
        this.dataDoc.wedge_height = Math.tan(radAng) * this.dataDoc.wedge_width;

        // update weight position based on updated wedge width/height
        const yPos = this.yMax - this.dataDoc.wedge_height - this.mass1Radius * Math.cos(radAng) - this.mass1Radius;
        const xPos = this.xMax * 0.25 + this.mass1Radius * Math.sin(radAng) - this.mass1Radius;

        this.dataDoc.mass1_positionXstart = xPos;
        this.dataDoc.mass1_positionYstart = yPos;
        if (this.simulationMode === 'Freeform') {
            this.updateForcesWithFriction(NumCast(this.dataDoc.coefficientOfStaticFriction), this.dataDoc.wedge_width, Math.tan(radAng) * this.dataDoc.wedge_width);
        }
    };

    // In review mode, update forces when coefficient of static friction changed
    updateReviewForcesBasedOnCoefficient = (coefficient: number) => {
        let theta = this.wedgeAngle;
        const index = this.selectedQuestion.variablesForQuestionSetup.indexOf('theta - max 45');
        if (index >= 0) {
            theta = NumListCast(this.dataDoc.questionVariables)[index];
        }
        if (isNaN(theta)) {
            return;
        }
        this.dataDoc.review_GravityMagnitude = Math.abs(this.gravity);
        this.dataDoc.review_GravityAngle = 270;
        this.dataDoc.review_NormalMagnitude = Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180);
        this.dataDoc.review_NormalAngle = 90 - theta;
        let yForce = -Math.abs(this.gravity);
        yForce += Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180) * Math.sin(((90 - theta) * Math.PI) / 180);
        yForce += coefficient * Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180) * Math.sin(((180 - theta) * Math.PI) / 180);
        let friction = coefficient * Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180);
        if (yForce > 0) {
            friction = (-(Math.abs(this.gravity) * Math.cos((theta * Math.PI) / 180)) * Math.sin(((90 - theta) * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin(((180 - theta) * Math.PI) / 180);
        }
        this.dataDoc.review_StaticMagnitude = friction;
        this.dataDoc.review_StaticAngle = 180 - theta;
    };

    // In review mode, update forces when wedge angle changed
    updateReviewForcesBasedOnAngle = (angle: number) => {
        this.dataDoc.review_GravityMagnitude = Math.abs(this.gravity);
        this.dataDoc.review_GravityAngle = 270;
        this.dataDoc.review_NormalMagnitude = Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180);
        this.dataDoc.review_NormalAngle = 90 - angle;
        let yForce = -Math.abs(this.gravity);
        yForce += Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180) * Math.sin(((90 - angle) * Math.PI) / 180);
        yForce += NumCast(this.dataDoc.review_Coefficient) * Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180) * Math.sin(((180 - angle) * Math.PI) / 180);
        let friction = NumCast(this.dataDoc.review_Coefficient) * Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180);
        if (yForce > 0) {
            friction = (-(Math.abs(this.gravity) * Math.cos((angle * Math.PI) / 180)) * Math.sin(((90 - angle) * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin(((180 - angle) * Math.PI) / 180);
        }
        this.dataDoc.review_StaticMagnitude = friction;
        this.dataDoc.review_StaticAngle = 180 - angle;
    };

    // Solve for the correct answers to the generated problem
    getAnswersToQuestion = (question: QuestionTemplate, questionVars: number[]) => {
        const solutions: number[] = [];

        let theta = this.wedgeAngle;
        let index = question.variablesForQuestionSetup.indexOf('theta - max 45');
        if (index >= 0) {
            theta = questionVars[index];
        }
        let muS: number = NumCast(this.dataDoc.coefficientOfStaticFriction);
        index = question.variablesForQuestionSetup.indexOf('coefficient of static friction');
        if (index >= 0) {
            muS = questionVars[index];
        }

        for (let i = 0; i < question.answerSolutionDescriptions.length; i++) {
            const description = question.answerSolutionDescriptions[i];
            if (!isNaN(NumCast(description))) {
                solutions.push(NumCast(description));
            } else if (description === 'solve normal force angle from wedge angle') {
                solutions.push(90 - theta);
            } else if (description === 'solve normal force magnitude from wedge angle') {
                solutions.push(Math.abs(this.gravity) * Math.cos((theta / 180) * Math.PI));
            } else if (description === 'solve static force magnitude from wedge angle given equilibrium') {
                const normalForceMagnitude = Math.abs(this.gravity) * Math.cos((theta / 180) * Math.PI);
                const normalForceAngle = 90 - theta;
                const frictionForceAngle = 180 - theta;
                const frictionForceMagnitude = (-normalForceMagnitude * Math.sin((normalForceAngle * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin((frictionForceAngle * Math.PI) / 180);
                solutions.push(frictionForceMagnitude);
            } else if (description === 'solve static force angle from wedge angle given equilibrium') {
                solutions.push(180 - theta);
            } else if (description === 'solve minimum static coefficient from wedge angle given equilibrium') {
                const normalForceMagnitude = Math.abs(this.gravity) * Math.cos((theta / 180) * Math.PI);
                const normalForceAngle = 90 - theta;
                const frictionForceAngle = 180 - theta;
                const frictionForceMagnitude = (-normalForceMagnitude * Math.sin((normalForceAngle * Math.PI) / 180) + Math.abs(this.gravity)) / Math.sin((frictionForceAngle * Math.PI) / 180);
                const frictionCoefficient = frictionForceMagnitude / normalForceMagnitude;
                solutions.push(frictionCoefficient);
            } else if (description === 'solve maximum wedge angle from coefficient of static friction given equilibrium') {
                solutions.push((Math.atan(muS) * 180) / Math.PI);
            }
        }
        this.dataDoc.selectedSolutions = new List<number>(solutions);
        return solutions;
    };

    // In review mode, check if input answers match correct answers and optionally generate alert
    checkAnswers = (showAlert: boolean = true) => {
        let error: boolean = false;
        const epsilon: number = 0.01;
        if (this.selectedQuestion) {
            for (let i = 0; i < this.selectedQuestion.answerParts.length; i++) {
                if (this.selectedQuestion.answerParts[i] === 'force of gravity') {
                    if (Math.abs(NumCast(this.dataDoc.review_GravityMagnitude) - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                } else if (this.selectedQuestion.answerParts[i] === 'angle of gravity') {
                    if (Math.abs(NumCast(this.dataDoc.review_GravityAngle) - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                } else if (this.selectedQuestion.answerParts[i] === 'normal force') {
                    if (Math.abs(NumCast(this.dataDoc.review_NormalMagnitude) - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                } else if (this.selectedQuestion.answerParts[i] === 'angle of normal force') {
                    if (Math.abs(NumCast(this.dataDoc.review_NormalAngle) - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                } else if (this.selectedQuestion.answerParts[i] === 'force of static friction') {
                    if (Math.abs(NumCast(this.dataDoc.review_StaticMagnitude) - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                } else if (this.selectedQuestion.answerParts[i] === 'angle of static friction') {
                    if (Math.abs(NumCast(this.dataDoc.review_StaticAngle) - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                } else if (this.selectedQuestion.answerParts[i] === 'coefficient of static friction') {
                    if (Math.abs(NumCast(this.dataDoc.coefficientOfStaticFriction) - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                } else if (this.selectedQuestion.answerParts[i] === 'wedge angle') {
                    if (Math.abs(this.wedgeAngle - this.selectedSolutions[i]) > epsilon) {
                        error = true;
                    }
                }
            }
        }
        if (showAlert) {
            this.dataDoc.simulation_paused = false;
            setTimeout(() => (this.dataDoc.simulation_paused = true), 3000);
        }
        if (this.selectedQuestion.goal === 'noMovement') {
            this.dataDoc.noMovement = !error;
        }
    };

    // Reset all review values to default
    resetReviewValuesToDefault = () => {
        this.dataDoc.review_GravityMagnitude = 0;
        this.dataDoc.review_GravityAngle = 0;
        this.dataDoc.review_NormalMagnitude = 0;
        this.dataDoc.review_NormalAngle = 0;
        this.dataDoc.review_StaticMagnitude = 0;
        this.dataDoc.review_StaticAngle = 0;
        this.dataDoc.coefficientOfKineticFriction = 0;
        this.dataDoc.simulation_paused = true;
    };

    // In review mode, reset problem variables and generate a new question
    generateNewQuestion = () => {
        this.resetReviewValuesToDefault();

        const vars: number[] = [];
        let question: QuestionTemplate = questions.inclinePlane[0];

        if (this.simulationType === 'Inclined Plane') {
            this.dataDoc.questionNumber = (NumCast(this.dataDoc.questionNumber) + 1) % questions.inclinePlane.length;
            question = questions.inclinePlane[NumCast(this.dataDoc.questionNumber)];

            let coefficient = 0;
            let wedge_angle = 0;

            for (let i = 0; i < question.variablesForQuestionSetup.length; i++) {
                if (question.variablesForQuestionSetup[i] === 'theta - max 45') {
                    const randValue = Math.floor(Math.random() * 44 + 1);
                    vars.push(randValue);
                    wedge_angle = randValue;
                } else if (question.variablesForQuestionSetup[i] === 'coefficient of static friction') {
                    const randValue = Math.round(Math.random() * 1000) / 1000;
                    vars.push(randValue);
                    coefficient = randValue;
                }
            }
            this.dataDoc.wedge_angle = wedge_angle;
            this.changeWedgeBasedOnNewAngle(wedge_angle);
            this.dataDoc.coefficientOfStaticFriction = coefficient;
            this.dataDoc.review_Coefficient = coefficient;
        }
        let q = '';
        for (let i = 0; i < question.questionSetup.length; i++) {
            q += question.questionSetup[i];
            if (i !== question.questionSetup.length - 1) {
                q += vars[i];
                if (question.variablesForQuestionSetup[i].includes('theta')) {
                    q += ' degree (≈' + Math.round((1000 * (vars[i] * Math.PI)) / 180) / 1000 + ' rad)';
                }
            }
        }
        this.dataDoc.questionVariables = new List<number>(vars);
        this.dataDoc.selectedQuestion = JSON.stringify(question);
        this.dataDoc.questionPartOne = q;
        this.dataDoc.questionPartTwo = question.question;
        this.dataDoc.answers = new List<number>(this.getAnswersToQuestion(question, vars));
        // this.dataDoc.simulation_reset = (!this.dataDoc.simulation_reset);
    };

    // Default setup for uniform circular motion simulation
    @action
    setupCircular = (value: number) => {
        this.dataDoc.simulation_showComponentForces = false;
        this.dataDoc.mass1_velocityYstart = 0;
        this.dataDoc.mass1_velocityXstart = value;
        const xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius;
        const yPos = (this.yMax + this.yMin) / 2 + this.circularMotionRadius - this.mass1Radius;
        this.dataDoc.mass1_positionYstart = yPos;
        this.dataDoc.mass1_positionXstart = xPos;
        const tensionForce: IForce = {
            description: 'Centripetal Force',
            magnitude: (this.dataDoc.mass1_velocityXstart ** 2 * this.mass1) / this.circularMotionRadius,
            directionInDegrees: 90,
        };
        this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce]);
        this.dataDoc.mass1_forcesStart = JSON.stringify([tensionForce]);
        this._simReset++;
    };

    setupInclinedPlane = () => {
        this.changeWedgeBasedOnNewAngle(this.wedgeAngle);
        this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]);
        this.updateForcesWithFriction(NumCast(this.dataDoc.coefficientOfStaticFriction));
    };

    // Default setup for pendulum simulation
    setupPendulum = () => {
        const length = (300 * this._props.PanelWidth()) / 1000;
        const angle = 30;
        const x = length * Math.cos(((90 - angle) * Math.PI) / 180);
        const y = length * Math.sin(((90 - angle) * Math.PI) / 180);
        const xPos = this.xMax / 2 - x - this.mass1Radius;
        const yPos = y - this.mass1Radius - 5;
        this.dataDoc.mass1_positionXstart = xPos;
        this.dataDoc.mass1_positionYstart = yPos;
        const forceOfTension: IForce = {
            description: 'Tension',
            magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin((60 * Math.PI) / 180),
            directionInDegrees: 90 - angle,
        };
        const gravityParallel: IForce = {
            description: 'Gravity Parallel Component',
            magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin(((90 - angle) * Math.PI) / 180),
            directionInDegrees: -angle - 90,
        };
        const gravityPerpendicular: IForce = {
            description: 'Gravity Perpendicular Component',
            magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(((90 - angle) * Math.PI) / 180),
            directionInDegrees: -angle,
        };

        this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]);
        this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
        this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
        this.dataDoc.pendulum_angle = this.dataDoc.pendulum_angleStart = 30;
        this.dataDoc.pendulum_length = this.dataDoc.pendulum_lengthStart = 300;
    };

    // Default setup for spring simulation
    @action
    setupSpring = () => {
        this.dataDoc.simulation_showComponentForces = false;
        this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
        this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]);
        this.dataDoc.mass1_positionXstart = this.xMax / 2 - this.mass1Radius;
        this.dataDoc.mass1_positionYstart = 200;
        this.dataDoc.spring_constant = 0.5;
        this.dataDoc.spring_lengthRest = 200;
        this.dataDoc.spring_lengthStart = 200;
        this._simReset++;
    };

    // Default setup for suspension simulation
    @action
    setupSuspension = () => {
        const xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius;
        const yPos = this.yMin + 200;
        this.dataDoc.mass1_positionYstart = yPos;
        this.dataDoc.mass1_positionXstart = xPos;
        this.dataDoc.mass1_positionY = this.getDisplayYPos(yPos);
        this.dataDoc.mass1_positionX = xPos;
        const tensionMag = (this.mass1 * Math.abs(this.gravity)) / (2 * Math.sin(Math.PI / 4));
        const tensionForce1: IForce = {
            description: 'Tension',
            magnitude: tensionMag,
            directionInDegrees: 45,
        };
        const tensionForce2: IForce = {
            description: 'Tension',
            magnitude: tensionMag,
            directionInDegrees: 135,
        };
        const gravity = this.gravityForce(this.mass1);
        this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]);
        this.dataDoc.mass1_forcesStart = JSON.stringify([tensionForce1, tensionForce2, gravity]);
        this._simReset++;
    };

    // Default setup for pulley simulation
    @action
    setupPulley = () => {
        this.dataDoc.simulation_showComponentForces = false;
        this.dataDoc.mass1_positionYstart = (this.yMax + this.yMin) / 2;
        this.dataDoc.mass1_positionXstart = (this.xMin + this.xMax) / 2 - 2 * this.mass1Radius - 5;
        this.dataDoc.mass1_positionY = this.getDisplayYPos((this.yMax + this.yMin) / 2);
        this.dataDoc.mass1_positionX = (this.xMin + this.xMax) / 2 - 2 * this.mass1Radius - 5;
        const a = (-1 * ((this.mass1 - this.mass2) * Math.abs(this.gravity))) / (this.mass1 + this.mass2);
        const gravityForce1 = this.gravityForce(this.mass1);
        const tensionForce1: IForce = {
            description: 'Tension',
            magnitude: this.mass1 * a + this.mass1 * Math.abs(this.gravity),
            directionInDegrees: 90,
        };
        this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce1, tensionForce1]);
        this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce1, tensionForce1]);

        const gravityForce2 = this.gravityForce(this.mass2);
        const tensionForce2: IForce = {
            description: 'Tension',
            magnitude: -this.mass2 * a + this.mass2 * Math.abs(this.gravity),
            directionInDegrees: 90,
        };
        this.dataDoc.mass2_positionYstart = (this.yMax + this.yMin) / 2;
        this.dataDoc.mass2_positionXstart = (this.xMin + this.xMax) / 2 + 5;
        this.dataDoc.mass2_positionY = this.getDisplayYPos((this.yMax + this.yMin) / 2);
        this.dataDoc.mass2_positionX = (this.xMin + this.xMax) / 2 + 5;
        this.dataDoc.mass2_forcesUpdated = JSON.stringify([gravityForce2, tensionForce2]);
        this.dataDoc.mass2_forcesStart = JSON.stringify([gravityForce2, tensionForce2]);
        this._simReset++;
    };

    public static parseJSON(json: string) {
        return !json ? [] : (JSON.parse(json) as IForce[]);
    }

    // Handle force change in review mode
    updateReviewModeValues = () => {
        const forceOfGravityReview: IForce = {
            description: 'Gravity',
            magnitude: NumCast(this.dataDoc.review_GravityMagnitude),
            directionInDegrees: NumCast(this.dataDoc.review_GravityAngle),
        };
        const normalForceReview: IForce = {
            description: 'Normal Force',
            magnitude: NumCast(this.dataDoc.review_NormalMagnitude),
            directionInDegrees: NumCast(this.dataDoc.review_NormalAngle),
        };
        const staticFrictionForceReview: IForce = {
            description: 'Static Friction Force',
            magnitude: NumCast(this.dataDoc.review_StaticMagnitude),
            directionInDegrees: NumCast(this.dataDoc.review_StaticAngle),
        };
        this.dataDoc.mass1_forcesStart = JSON.stringify([forceOfGravityReview, normalForceReview, staticFrictionForceReview]);
        this.dataDoc.mass1_forcesUpdated = JSON.stringify([forceOfGravityReview, normalForceReview, staticFrictionForceReview]);
    };

    pause = () => (this.dataDoc.simulation_paused = true);
    componentForces1 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass1_componentForces));
    setComponentForces1 = (forces: IForce[]) => (this.dataDoc.mass1_componentForces = JSON.stringify(forces));
    componentForces2 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass2_componentForces));
    setComponentForces2 = (forces: IForce[]) => (this.dataDoc.mass2_componentForces = JSON.stringify(forces));
    startForces1 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass1_forcesStart));
    startForces2 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass2_forcesStart));
    forcesUpdated1 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass1_forcesUpdated));
    setForcesUpdated1 = (forces: IForce[]) => (this.dataDoc.mass1_forcesUpdated = JSON.stringify(forces));
    forcesUpdated2 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass2_forcesUpdated));
    setForcesUpdated2 = (forces: IForce[]) => (this.dataDoc.mass2_forcesUpdated = JSON.stringify(forces));
    setPosition1 = (xPos: number | undefined, yPos: number | undefined) => {
        yPos !== undefined && (this.dataDoc.mass1_positionY = Math.round(yPos * 100) / 100);
        xPos !== undefined && (this.dataDoc.mass1_positionX = Math.round(xPos * 100) / 100);
    };
    setPosition2 = (xPos: number | undefined, yPos: number | undefined) => {
        yPos !== undefined && (this.dataDoc.mass2_positionY = Math.round(yPos * 100) / 100);
        xPos !== undefined && (this.dataDoc.mass2_positionX = Math.round(xPos * 100) / 100);
    };
    setVelocity1 = (xVel: number | undefined, yVel: number | undefined) => {
        yVel !== undefined && (this.dataDoc.mass1_velocityY = (-1 * Math.round(yVel * 100)) / 100);
        xVel !== undefined && (this.dataDoc.mass1_velocityX = Math.round(xVel * 100) / 100);
    };
    setVelocity2 = (xVel: number | undefined, yVel: number | undefined) => {
        yVel !== undefined && (this.dataDoc.mass2_velocityY = (-1 * Math.round(yVel * 100)) / 100);
        xVel !== undefined && (this.dataDoc.mass2_velocityX = Math.round(xVel * 100) / 100);
    };
    setAcceleration1 = (xAccel: number, yAccel: number) => {
        this.dataDoc.mass1_accelerationY = yAccel;
        this.dataDoc.mass1_accelerationX = xAccel;
    };
    setAcceleration2 = (xAccel: number, yAccel: number) => {
        this.dataDoc.mass2_accelerationY = yAccel;
        this.dataDoc.mass2_accelerationX = xAccel;
    };
    setPendulumAngle = (angle: number | undefined, length: number | undefined) => {
        angle !== undefined && (this.dataDoc.pendulum_angle = angle);
        length !== undefined && (this.dataDoc.pendulum_length = length);
    };
    setSpringLength = (length: number) => {
        this.dataDoc.spring_lengthStart = length;
    };
    resetRequest = () => this._simReset;
    render() {
        const commonWeightProps = {
            pause: this.pause,
            paused: BoolCast(this.dataDoc.simulation_paused),
            panelWidth: this._props.PanelWidth,
            panelHeight: this._props.PanelHeight,
            resetRequest: this.resetRequest,
            xMax: this.xMax,
            xMin: this.xMin,
            yMax: this.yMax,
            yMin: this.yMin,
            wallPositions: this.wallPositions,
            gravity: Math.abs(this.gravity),
            timestepSize: 0.05,
            showComponentForces: BoolCast(this.dataDoc.simulation_showComponentForces),
            coefficientOfKineticFriction: NumCast(this.dataDoc.coefficientOfKineticFriction),
            elasticCollisions: BoolCast(this.dataDoc.elasticCollisions),
            simulationMode: this.simulationMode,
            noMovement: BoolCast(this.dataDoc.noMovement),
            circularMotionRadius: this.circularMotionRadius,
            wedgeHeight: this.wedgeHeight,
            wedgeWidth: this.wedgeWidth,
            springConstant: this.springConstant,
            springStartLength: this.springLengthStart,
            springRestLength: this.springLengthRest,
            setSpringLength: this.setSpringLength,
            setPendulumAngle: this.setPendulumAngle,
            pendulumAngle: this.pendulumAngle,
            pendulumLength: this.pendulumLength,
            startPendulumAngle: this.pendulumAngleStart,
            startPendulumLength: this.pendulumLengthStart,
            radius: this.mass1Radius,
            simulationSpeed: NumCast(this.dataDoc.simulation_speed, 2),
            showAcceleration: BoolCast(this.dataDoc.simulation_showAcceleration),
            showForceMagnitudes: BoolCast(this.dataDoc.simulation_showForceMagnitudes),
            showForces: BoolCast(this.dataDoc.simulation_showForces),
            showVelocity: BoolCast(this.dataDoc.simulation_showVelocity),
            simulationType: this.simulationType,
        };
        return (
            <div className="physicsSimApp">
                <div className="mechanicsSimulationContainer">
                    <div className="mechanicsSimulationContentContainer">
                        <div className="mechanicsSimulationButtonsAndElements">
                            <div className="mechanicsSimulationButtons">
                                {!this.dataDoc.simulation_paused && (
                                    <div
                                        style={{
                                            position: 'fixed',
                                            left: 0.1 * this._props.PanelWidth() + 'px',
                                            top: 0.95 * this._props.PanelHeight() + 'px',
                                            width: 0.5 * this._props.PanelWidth() + 'px',
                                        }}>
                                        <LinearProgress />
                                    </div>
                                )}
                            </div>
                            <div
                                className="mechanicsSimulationElements"
                                style={{
                                    //
                                    width: '60%',
                                    height: '100%',
                                    position: 'absolute',
                                    background: 'yellow',
                                }}>
                                <Weight
                                    {...commonWeightProps}
                                    color="red"
                                    componentForces={this.componentForces1}
                                    setComponentForces={this.setComponentForces1}
                                    displayXVelocity={NumCast(this.dataDoc.mass1_velocityX)}
                                    displayYVelocity={NumCast(this.dataDoc.mass1_velocityY)}
                                    mass={this.mass1}
                                    startForces={this.startForces1}
                                    startPosX={this.mass1PosXStart}
                                    startPosY={this.mass1PosYStart}
                                    startVelX={this.mass1VelXStart}
                                    startVelY={this.mass1VelYStart}
                                    updateMassPosX={NumCast(this.dataDoc.mass1_xChange)}
                                    updateMassPosY={NumCast(this.dataDoc.mass1_yChange)}
                                    forcesUpdated={this.forcesUpdated1}
                                    setForcesUpdated={this.setForcesUpdated1}
                                    setPosition={this.setPosition1}
                                    setVelocity={this.setVelocity1}
                                    setAcceleration={this.setAcceleration1}
                                />
                                {this.simulationType === 'Pulley' && (
                                    <Weight
                                        {...commonWeightProps}
                                        color="green"
                                        componentForces={this.componentForces2}
                                        setComponentForces={this.setComponentForces2}
                                        displayXVelocity={NumCast(this.dataDoc.mass2_velocityX)}
                                        displayYVelocity={NumCast(this.dataDoc.mass2_velocityY)}
                                        mass={this.mass2}
                                        startForces={this.startForces2}
                                        startPosX={this.mass2PosXStart}
                                        startPosY={this.mass2PosYStart}
                                        startVelX={this.mass2VelXStart}
                                        startVelY={this.mass2VelYStart}
                                        updateMassPosX={NumCast(this.dataDoc.mass2_xChange)}
                                        updateMassPosY={NumCast(this.dataDoc.mass2_yChange)}
                                        forcesUpdated={this.forcesUpdated2}
                                        setForcesUpdated={this.setForcesUpdated2}
                                        setPosition={this.setPosition2}
                                        setVelocity={this.setVelocity2}
                                        setAcceleration={this.setAcceleration2}
                                    />
                                )}
                            </div>
                            <div style={{ position: 'absolute', transformOrigin: 'top left', top: 0, left: 0, width: '100%', height: '100%' }}>
                                {(this.simulationType === 'One Weight' || this.simulationType === 'Inclined Plane') &&
                                    this.wallPositions?.map((element, index) => <Wall key={index} length={element.length} xPos={element.xPos} yPos={element.yPos} angleInDegrees={element.angleInDegrees} />)}
                            </div>
                        </div>
                    </div>
                    <div
                        className="mechanicsSimulationEquationContainer"
                        onWheel={e => this._props.isContentActive() && e.stopPropagation()}
                        style={{ overflow: 'auto', height: `${Math.max(1, 800 / this._props.PanelWidth()) * 100}%`, transform: `scale(${Math.min(1, this._props.PanelWidth() / 850)})` }}>
                        <div className="mechanicsSimulationControls">
                            <Stack direction="row" spacing={1}>
                                {this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && (
                                    <IconButton onClick={() => (this.dataDoc.simulation_paused = false)}>
                                        <PlayArrowIcon />
                                    </IconButton>
                                )}
                                {!this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && (
                                    <IconButton onClick={() => (this.dataDoc.simulation_paused = true)}>
                                        <PauseIcon />
                                    </IconButton>
                                )}
                                {this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && (
                                    <IconButton onClick={action(() => this._simReset++)}>
                                        <ReplayIcon />
                                    </IconButton>
                                )}
                            </Stack>
                            <div className="dropdownMenu">
                                <select
                                    value={StrCast(this.simulationType)}
                                    onChange={event => {
                                        this.dataDoc.simulation_type = event.target.value;
                                        this.setupSimulation();
                                    }}
                                    style={{ height: '2em', width: '100%', fontSize: '16px' }}>
                                    <option value="One Weight">Projectile</option>
                                    <option value="Inclined Plane">Inclined Plane</option>
                                    <option value="Pendulum">Pendulum</option>
                                    <option value="Spring">Spring</option>
                                    <option value="Circular Motion">Circular Motion</option>
                                    <option value="Pulley">Pulley</option>
                                    <option value="Suspension">Suspension</option>
                                </select>
                            </div>
                            <div className="dropdownMenu">
                                <select
                                    value={this.simulationMode}
                                    onChange={event => {
                                        this.dataDoc.simulation_mode = event.target.value;
                                        this.setupSimulation();
                                    }}
                                    style={{ height: '2em', width: '100%', fontSize: '16px' }}>
                                    <option value="Tutorial">Tutorial Mode</option>
                                    <option value="Freeform">Freeform Mode</option>
                                    <option value="Review">Review Mode</option>
                                </select>
                            </div>
                        </div>
                        {this.simulationMode === 'Review' && this.simulationType !== 'Inclined Plane' && (
                            <div className="wordProblemBox">
                                <p>{this.simulationType} review problems in progress!</p>
                                <hr />
                            </div>
                        )}
                        {this.simulationMode === 'Review' && this.simulationType === 'Inclined Plane' && (
                            <div>
                                {!this.dataDoc.hintDialogueOpen && (
                                    <IconButton
                                        onClick={() => (this.dataDoc.hintDialogueOpen = true)}
                                        sx={{
                                            position: 'fixed',
                                            left: this.xMax - 50 + 'px',
                                            top: this.yMin + 14 + 'px',
                                        }}>
                                        <QuestionMarkIcon />
                                    </IconButton>
                                )}
                                <Dialog maxWidth="sm" fullWidth open={BoolCast(this.dataDoc.hintDialogueOpen)} onClose={() => (this.dataDoc.hintDialogueOpen = false)}>
                                    <DialogTitle>Hints</DialogTitle>
                                    <DialogContent>
                                        {this.selectedQuestion.hints?.map((hint: { description: string; content: string }, index: number) => (
                                            <div key={index}>
                                                <DialogContentText>
                                                    <details>
                                                        <summary>
                                                            <b>
                                                                Hint {index + 1}: {hint.description}
                                                            </b>
                                                        </summary>
                                                        {hint.content}
                                                    </details>
                                                </DialogContentText>
                                            </div>
                                        ))}
                                    </DialogContent>
                                    <DialogActions>
                                        <Button onClick={() => (this.dataDoc.hintDialogueOpen = false)}>Close</Button>
                                    </DialogActions>
                                </Dialog>
                                <div className="wordProblemBox">
                                    <div className="question">
                                        <p>{this.questionPartOne}</p>
                                        <p>{this.questionPartTwo}</p>
                                    </div>
                                    <div className="answers">
                                        {this.selectedQuestion.answerParts.includes('force of gravity') && (
                                            <InputField
                                                label={<p>Gravity magnitude</p>}
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="review_GravityMagnitude"
                                                step={0.1}
                                                unit="N"
                                                upperBound={50}
                                                value={NumCast(this.dataDoc.review_GravityMagnitude)}
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('force of gravity')]}
                                                labelWidth="7em"
                                            />
                                        )}
                                        {this.selectedQuestion.answerParts.includes('angle of gravity') && (
                                            <InputField
                                                label={<p>Gravity angle</p>}
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="review_GravityAngle"
                                                step={1}
                                                unit="°"
                                                upperBound={360}
                                                value={NumCast(this.dataDoc.review_GravityAngle)}
                                                radianEquivalent
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of gravity')]}
                                                labelWidth="7em"
                                            />
                                        )}
                                        {this.selectedQuestion.answerParts.includes('normal force') && (
                                            <InputField
                                                label={<p>Normal force magnitude</p>}
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="review_NormalMagnitude"
                                                step={0.1}
                                                unit="N"
                                                upperBound={50}
                                                value={NumCast(this.dataDoc.review_NormalMagnitude)}
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('normal force')]}
                                                labelWidth="7em"
                                            />
                                        )}
                                        {this.selectedQuestion.answerParts.includes('angle of normal force') && (
                                            <InputField
                                                label={<p>Normal force angle</p>}
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="review_NormalAngle"
                                                step={1}
                                                unit="°"
                                                upperBound={360}
                                                value={NumCast(this.dataDoc.review_NormalAngle)}
                                                radianEquivalent
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of normal force')]}
                                                labelWidth="7em"
                                            />
                                        )}
                                        {this.selectedQuestion.answerParts.includes('force of static friction') && (
                                            <InputField
                                                label={<p>Static friction magnitude</p>}
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="review_StaticMagnitude"
                                                step={0.1}
                                                unit="N"
                                                upperBound={50}
                                                value={NumCast(this.dataDoc.review_StaticMagnitude)}
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('force of static friction')]}
                                                labelWidth="7em"
                                            />
                                        )}
                                        {this.selectedQuestion.answerParts.includes('angle of static friction') && (
                                            <InputField
                                                label={<p>Static friction angle</p>}
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="review_StaticAngle"
                                                step={1}
                                                unit="°"
                                                upperBound={360}
                                                value={NumCast(this.dataDoc.review_StaticAngle)}
                                                radianEquivalent
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('angle of static friction')]}
                                                labelWidth="7em"
                                            />
                                        )}
                                        {this.selectedQuestion.answerParts.includes('coefficient of static friction') && (
                                            <InputField
                                                label={
                                                    <Box>
                                                        &mu;<sub>s</sub>
                                                    </Box>
                                                }
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="coefficientOfStaticFriction"
                                                step={0.1}
                                                unit=""
                                                upperBound={1}
                                                value={NumCast(this.dataDoc.coefficientOfStaticFriction)}
                                                effect={this.updateReviewForcesBasedOnCoefficient}
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('coefficient of static friction')]}
                                            />
                                        )}
                                        {this.selectedQuestion.answerParts.includes('wedge angle') && (
                                            <InputField
                                                label={<Box>&theta;</Box>}
                                                lowerBound={0}
                                                dataDoc={this.dataDoc}
                                                prop="wedge_angle"
                                                step={1}
                                                unit="°"
                                                upperBound={49}
                                                value={this.wedgeAngle}
                                                effect={(val: number) => {
                                                    this.changeWedgeBasedOnNewAngle(val);
                                                    this.updateReviewForcesBasedOnAngle(val);
                                                }}
                                                radianEquivalent
                                                showIcon={BoolCast(this.dataDoc.simulation_showIcon)}
                                                correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('wedge angle')]}
                                            />
                                        )}
                                    </div>
                                </div>
                            </div>
                        )}
                        {this.simulationMode === 'Tutorial' && (
                            <div className="wordProblemBox">
                                <div className="question">
                                    <h2>Problem</h2>
                                    <p>{this.tutorial.question}</p>
                                </div>
                                <div
                                    style={{
                                        display: 'flex',
                                        justifyContent: 'spaceBetween',
                                        width: '100%',
                                    }}>
                                    <IconButton
                                        onClick={() => {
                                            let step = NumCast(this.dataDoc.tutorial_stepNumber) - 1;
                                            step = Math.max(step, 0);
                                            step = Math.min(step, this.tutorial.steps.length - 1);
                                            this.dataDoc.tutorial_stepNumber = step;
                                            this.dataDoc.mass1_forcesStart = JSON.stringify(this.tutorial.steps[step].forces);
                                            this.dataDoc.mass1_forcesUpdated = JSON.stringify(this.tutorial.steps[step].forces);
                                            this.dataDoc.simulation_showForceMagnitudes = this.tutorial.steps[step].showMagnitude;
                                        }}
                                        disabled={this.dataDoc.tutorial_stepNumber === 0}>
                                        <ArrowLeftIcon />
                                    </IconButton>
                                    <div>
                                        <h3>
                                            Step {NumCast(this.dataDoc.tutorial_stepNumber) + 1}: {this.tutorial.steps[NumCast(this.dataDoc.tutorial_stepNumber)].description}
                                        </h3>
                                        <p>{this.tutorial.steps[NumCast(this.dataDoc.tutorial_stepNumber)].content}</p>
                                    </div>
                                    <IconButton
                                        onClick={() => {
                                            let step = NumCast(this.dataDoc.tutorial_stepNumber) + 1;
                                            step = Math.max(step, 0);
                                            step = Math.min(step, this.tutorial.steps.length - 1);
                                            this.dataDoc.tutorial_stepNumber = step;
                                            this.dataDoc.mass1_forcesStart = JSON.stringify(this.tutorial.steps[step].forces);
                                            this.dataDoc.mass1_forcesUpdated = JSON.stringify(this.tutorial.steps[step].forces);
                                            this.dataDoc.simulation_showForceMagnitudes = this.tutorial.steps[step].showMagnitude;
                                        }}
                                        disabled={this.dataDoc.tutorial_stepNumber === this.tutorial.steps.length - 1}>
                                        <ArrowRightIcon />
                                    </IconButton>
                                </div>
                                <div>
                                    {(this.simulationType === 'One Weight' || this.simulationType === 'Inclined Plane' || this.simulationType === 'Pendulum') && <p>Resources</p>}
                                    {this.simulationType === 'One Weight' && (
                                        <ul>
                                            <li>
                                                <a
                                                    href="https://www.khanacademy.org/science/physics/one-dimensional-motion"
                                                    target="_blank"
                                                    rel="noreferrer"
                                                    style={{
                                                        color: 'blue',
                                                        textDecoration: 'underline',
                                                    }}>
                                                    Khan Academy - One Dimensional Motion
                                                </a>
                                            </li>
                                            <li>
                                                <a
                                                    href="https://www.khanacademy.org/science/physics/two-dimensional-motion"
                                                    target="_blank"
                                                    rel="noreferrer"
                                                    style={{
                                                        color: 'blue',
                                                        textDecoration: 'underline',
                                                    }}>
                                                    Khan Academy - Two Dimensional Motion
                                                </a>
                                            </li>
                                        </ul>
                                    )}
                                    {this.simulationType === 'Inclined Plane' && (
                                        <ul>
                                            <li>
                                                <a
                                                    href="https://www.khanacademy.org/science/physics/forces-newtons-laws#normal-contact-force"
                                                    target="_blank"
                                                    rel="noreferrer"
                                                    style={{
                                                        color: 'blue',
                                                        textDecoration: 'underline',
                                                    }}>
                                                    Khan Academy - Normal Force
                                                </a>
                                            </li>
                                            <li>
                                                <a
                                                    href="https://www.khanacademy.org/science/physics/forces-newtons-laws#inclined-planes-friction"
                                                    target="_blank"
                                                    rel="noreferrer"
                                                    style={{
                                                        color: 'blue',
                                                        textDecoration: 'underline',
                                                    }}>
                                                    Khan Academy - Inclined Planes
                                                </a>
                                            </li>
                                        </ul>
                                    )}
                                    {this.simulationType === 'Pendulum' && (
                                        <ul>
                                            <li>
                                                <a
                                                    href="https://www.khanacademy.org/science/physics/forces-newtons-laws#tension-tutorial"
                                                    target="_blank"
                                                    rel="noreferrer"
                                                    style={{
                                                        color: 'blue',
                                                        textDecoration: 'underline',
                                                    }}>
                                                    Khan Academy - Tension
                                                </a>
                                            </li>
                                        </ul>
                                    )}
                                </div>
                            </div>
                        )}
                        {this.simulationMode === 'Review' && this.simulationType === 'Inclined Plane' && (
                            <div
                                style={{
                                    display: 'flex',
                                    justifyContent: 'space-between',
                                    marginTop: '10px',
                                }}>
                                <p
                                    style={{
                                        color: 'blue',
                                        textDecoration: 'underline',
                                        cursor: 'pointer',
                                    }}
                                    onClick={() => (this.dataDoc.simulation_mode = 'Tutorial')}>
                                    {' '}
                                    Go to walkthrough{' '}
                                </p>
                                <div style={{ display: 'flex', flexDirection: 'column' }}>
                                    <Button
                                        onClick={action(() => {
                                            this._simReset++;
                                            this.checkAnswers();
                                            this.dataDoc.simulation_showIcon = true;
                                        })}
                                        variant="outlined">
                                        <p>Submit</p>
                                    </Button>
                                    <Button
                                        onClick={() => {
                                            this.generateNewQuestion();
                                            this.dataDoc.simulation_showIcon = false;
                                        }}
                                        variant="outlined">
                                        <p>New question</p>
                                    </Button>
                                </div>
                            </div>
                        )}
                        {this.simulationMode === 'Freeform' && (
                            <div className="vars">
                                <FormControl component="fieldset">
                                    <FormGroup>
                                        {this.simulationType === 'One Weight' && (
                                            <FormControlLabel
                                                control={<Checkbox checked={BoolCast(this.dataDoc.elasticCollisions)} onChange={() => (this.dataDoc.elasticCollisions = !this.dataDoc.elasticCollisions)} />}
                                                label="Make collisions elastic"
                                                labelPlacement="start"
                                            />
                                        )}
                                        <FormControlLabel
                                            control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showForces)} onChange={() => (this.dataDoc.simulation_showForces = !this.dataDoc.simulation_showForces)} />}
                                            label="Show force vectors"
                                            labelPlacement="start"
                                        />
                                        {(this.simulationType === 'Inclined Plane' || this.simulationType === 'Pendulum') && (
                                            <FormControlLabel
                                                control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showComponentForces)} onChange={() => (this.dataDoc.simulation_showComponentForces = !this.dataDoc.simulation_showComponentForces)} />}
                                                label="Show component force vectors"
                                                labelPlacement="start"
                                            />
                                        )}
                                        <FormControlLabel
                                            control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showAcceleration)} onChange={() => (this.dataDoc.simulation_showAcceleration = !this.dataDoc.simulation_showAcceleration)} />}
                                            label="Show acceleration vector"
                                            labelPlacement="start"
                                        />
                                        <FormControlLabel
                                            control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showVelocity)} onChange={() => (this.dataDoc.simulation_showVelocity = !this.dataDoc.simulation_showVelocity)} />}
                                            label="Show velocity vector"
                                            labelPlacement="start"
                                        />
                                        <InputField label={<Box>Speed</Box>} lowerBound={1} dataDoc={this.dataDoc} prop="simulation_speed" step={1} unit="x" upperBound={10} value={NumCast(this.dataDoc.simulation_speed, 2)} labelWidth="5em" />
                                        {this.dataDoc.simulation_paused && this.simulationType !== 'Circular Motion' && (
                                            <InputField
                                                label={<Box>Gravity</Box>}
                                                lowerBound={-30}
                                                dataDoc={this.dataDoc}
                                                prop="gravity"
                                                step={0.01}
                                                unit="m/s2"
                                                upperBound={0}
                                                value={NumCast(this.dataDoc.simulation_gravity, -9.81)}
                                                effect={(val: number) => this.setupSimulation()}
                                                labelWidth="5em"
                                            />
                                        )}
                                        {this.dataDoc.simulation_paused && this.simulationType !== 'Pulley' && (
                                            <InputField
                                                label={<Box>Mass</Box>}
                                                lowerBound={1}
                                                dataDoc={this.dataDoc}
                                                prop="mass1"
                                                step={0.1}
                                                unit="kg"
                                                upperBound={5}
                                                value={this.mass1 ?? 1}
                                                effect={(val: number) => this.setupSimulation()}
                                                labelWidth="5em"
                                            />
                                        )}
                                        {this.dataDoc.simulation_paused && this.simulationType === 'Pulley' && (
                                            <InputField
                                                label={<Box>Red mass</Box>}
                                                lowerBound={1}
                                                dataDoc={this.dataDoc}
                                                prop="mass1"
                                                step={0.1}
                                                unit="kg"
                                                upperBound={5}
                                                value={this.mass1 ?? 1}
                                                effect={(val: number) => this.setupSimulation()}
                                                labelWidth="5em"
                                            />
                                        )}
                                        {this.dataDoc.simulation_paused && this.simulationType === 'Pulley' && (
                                            <InputField
                                                label={<Box>Blue mass</Box>}
                                                lowerBound={1}
                                                dataDoc={this.dataDoc}
                                                prop="mass2"
                                                step={0.1}
                                                unit="kg"
                                                upperBound={5}
                                                value={this.mass2 ?? 1}
                                                effect={(val: number) => this.setupSimulation()}
                                                labelWidth="5em"
                                            />
                                        )}
                                        {this.dataDoc.simulation_paused && this.simulationType === 'Circular Motion' && (
                                            <InputField
                                                label={<Box>Rod length</Box>}
                                                lowerBound={100}
                                                dataDoc={this.dataDoc}
                                                prop="circularMotionRadius"
                                                step={5}
                                                unit="m"
                                                upperBound={250}
                                                value={this.circularMotionRadius}
                                                effect={(val: number) => this.setupSimulation()}
                                                labelWidth="5em"
                                            />
                                        )}
                                    </FormGroup>
                                </FormControl>
                                {this.simulationType === 'Spring' && this.dataDoc.simulation_paused && (
                                    <div>
                                        <InputField
                                            label={<Typography color="inherit">Spring stiffness</Typography>}
                                            lowerBound={0.1}
                                            dataDoc={this.dataDoc}
                                            prop="spring_constant"
                                            step={1}
                                            unit="N/m"
                                            upperBound={500}
                                            value={this.springConstant}
                                            effect={action(() => this._simReset++)}
                                            radianEquivalent={false}
                                            mode="Freeform"
                                            labelWidth="7em"
                                        />
                                        <InputField
                                            label={<Typography color="inherit">Rest length</Typography>}
                                            lowerBound={10}
                                            dataDoc={this.dataDoc}
                                            prop="spring_lengthRest"
                                            step={100}
                                            unit=""
                                            upperBound={500}
                                            value={this.springLengthRest}
                                            effect={action(() => this._simReset++)}
                                            radianEquivalent={false}
                                            mode="Freeform"
                                            labelWidth="7em"
                                        />
                                        <InputField
                                            label={<Typography color="inherit">Starting displacement</Typography>}
                                            lowerBound={-(this.springLengthRest - 10)}
                                            dataDoc={this.dataDoc}
                                            prop=""
                                            step={10}
                                            unit=""
                                            upperBound={this.springLengthRest}
                                            value={this.springLengthStart - this.springLengthRest}
                                            effect={action((val: number) => {
                                                this.dataDoc.mass1_positionYstart = this.springLengthRest + val;
                                                this.dataDoc.spring_lengthStart = this.springLengthRest + val;
                                                this._simReset++;
                                            })}
                                            radianEquivalent={false}
                                            mode="Freeform"
                                            labelWidth="7em"
                                        />
                                    </div>
                                )}
                                {this.simulationType === 'Inclined Plane' && this.dataDoc.simulation_paused && (
                                    <div>
                                        <InputField
                                            label={<Box>&theta;</Box>}
                                            lowerBound={0}
                                            dataDoc={this.dataDoc}
                                            prop="wedge_angle"
                                            step={1}
                                            unit="°"
                                            upperBound={49}
                                            value={this.wedgeAngle}
                                            effect={action((val: number) => {
                                                this.changeWedgeBasedOnNewAngle(val);
                                                this._simReset++;
                                            })}
                                            radianEquivalent
                                            mode="Freeform"
                                            labelWidth="2em"
                                        />
                                        <InputField
                                            label={
                                                <Box>
                                                    &mu;<sub>s</sub>
                                                </Box>
                                            }
                                            lowerBound={0}
                                            dataDoc={this.dataDoc}
                                            prop="coefficientOfStaticFriction"
                                            step={0.1}
                                            unit=""
                                            upperBound={1}
                                            value={NumCast(this.dataDoc.coefficientOfStaticFriction) ?? 0}
                                            effect={action((val: number) => {
                                                this.updateForcesWithFriction(val);
                                                if (val < NumCast(this.dataDoc.coefficientOfKineticFriction)) {
                                                    this.dataDoc.soefficientOfKineticFriction = val;
                                                }
                                                this._simReset++;
                                            })}
                                            mode="Freeform"
                                            labelWidth="2em"
                                        />
                                        <InputField
                                            label={
                                                <Box>
                                                    &mu;<sub>k</sub>
                                                </Box>
                                            }
                                            lowerBound={0}
                                            dataDoc={this.dataDoc}
                                            prop="coefficientOfKineticFriction"
                                            step={0.1}
                                            unit=""
                                            upperBound={NumCast(this.dataDoc.coefficientOfStaticFriction)}
                                            value={NumCast(this.dataDoc.coefficientOfKineticFriction) ?? 0}
                                            effect={action(() => this._simReset++)}
                                            mode="Freeform"
                                            labelWidth="2em"
                                        />
                                    </div>
                                )}
                                {this.simulationType === 'Inclined Plane' && !this.dataDoc.simulation_paused && (
                                    <Typography>
                                        <>
                                            &theta;: {Math.round(this.wedgeAngle * 100) / 100}° ≈ {Math.round(((this.wedgeAngle * Math.PI) / 180) * 100) / 100} rad
                                            <br />
                                            &mu; <sub>s</sub>: {this.dataDoc.coefficientOfStaticFriction}
                                            <br />
                                            &mu; <sub>k</sub>: {this.dataDoc.coefficientOfKineticFriction}
                                        </>
                                    </Typography>
                                )}
                                {this.simulationType === 'Pendulum' && !this.dataDoc.simulation_paused && (
                                    <Typography>
                                        &theta;: {Math.round(this.pendulumAngle * 100) / 100}° ≈ {Math.round(((this.pendulumAngle * Math.PI) / 180) * 100) / 100} rad
                                    </Typography>
                                )}
                                {this.simulationType === 'Pendulum' && this.dataDoc.simulation_paused && (
                                    <div>
                                        <InputField
                                            label={<Box>Angle</Box>}
                                            lowerBound={0}
                                            dataDoc={this.dataDoc}
                                            prop="pendulum_angle"
                                            step={1}
                                            unit="°"
                                            upperBound={59}
                                            value={NumCast(this.dataDoc.pendulum_angle, 30)}
                                            effect={action(value => {
                                                this.dataDoc.pendulum_angleStart = value;
                                                this.dataDoc.pendulum_lengthStart = this.dataDoc.pendulum_length;
                                                if (this.simulationType === 'Pendulum') {
                                                    const mag = this.mass1 * Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180);

                                                    const forceOfTension: IForce = {
                                                        description: 'Tension',
                                                        magnitude: mag,
                                                        directionInDegrees: 90 - value,
                                                    };
                                                    const gravityParallel: IForce = {
                                                        description: 'Gravity Parallel Component',
                                                        magnitude: Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180),
                                                        directionInDegrees: 270 - value,
                                                    };
                                                    const gravityPerpendicular: IForce = {
                                                        description: 'Gravity Perpendicular Component',
                                                        magnitude: Math.abs(this.gravity) * Math.sin((value * Math.PI) / 180),
                                                        directionInDegrees: -value,
                                                    };

                                                    const length = this.pendulumLength;
                                                    const x = length * Math.cos(((90 - value) * Math.PI) / 180);
                                                    const y = length * Math.sin(((90 - value) * Math.PI) / 180);
                                                    const xPos = this.xMax / 2 - x - NumCast(this.dataDoc.radius);
                                                    const yPos = y - NumCast(this.dataDoc.radius) - 5;
                                                    this.dataDoc.mass1_positionXstart = xPos;
                                                    this.dataDoc.mass1_positionYstart = yPos;

                                                    this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
                                                    this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
                                                    this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]);
                                                    this._simReset++;
                                                }
                                            })}
                                            radianEquivalent
                                            mode="Freeform"
                                            labelWidth="5em"
                                        />
                                        <InputField
                                            label={<Box>Rod length</Box>}
                                            lowerBound={0}
                                            dataDoc={this.dataDoc}
                                            prop="pendulum_length"
                                            step={1}
                                            unit="m"
                                            upperBound={400}
                                            value={Math.round(this.pendulumLength)}
                                            effect={action(value => {
                                                if (this.simulationType === 'Pendulum') {
                                                    this.dataDoc.pendulum_angleStart = this.pendulumAngle;
                                                    this.dataDoc.pendulum_lengthStart = value;
                                                    this._simReset++;
                                                }
                                            })}
                                            radianEquivalent={false}
                                            mode="Freeform"
                                            labelWidth="5em"
                                        />
                                    </div>
                                )}
                            </div>
                        )}
                        <div className="mechanicsSimulationEquation">
                            {this.simulationMode === 'Freeform' && (
                                <table>
                                    <tbody>
                                        <tr>
                                            <td>{this.simulationType === 'Pulley' ? 'Red Weight' : ''}</td>
                                            <td>X</td>
                                            <td>Y</td>
                                        </tr>
                                        <tr>
                                            <td
                                                style={{ cursor: 'help' }}
                                                // onClick={() => {
                                                //   window.open(
                                                //     "https://www.khanacademy.org/science/physics/two-dimensional-motion"
                                                //   );
                                                // }}
                                            >
                                                <Box>Position</Box>
                                            </td>
                                            {(!this.dataDoc.simulation_paused || this.simulationType === 'Inclined Plane' || this.simulationType === 'Circular Motion' || this.simulationType === 'Pulley') && (
                                                <td style={{ cursor: 'default' }}>{this.dataDoc.mass1_positionX + ''} m</td>
                                            )}{' '}
                                            {this.dataDoc.simulation_paused && this.simulationType !== 'Inclined Plane' && this.simulationType !== 'Circular Motion' && this.simulationType !== 'Pulley' && (
                                                <td
                                                    style={{
                                                        cursor: 'default',
                                                    }}>
                                                    <InputField
                                                        lowerBound={this.simulationType === 'Projectile' ? 1 : (this.xMax + this.xMin) / 4 - this.radius - 15}
                                                        dataDoc={this.dataDoc}
                                                        prop="mass1_positionX"
                                                        step={1}
                                                        unit="m"
                                                        upperBound={this.simulationType === 'Projectile' ? this.xMax - 110 : (3 * (this.xMax + this.xMin)) / 4 - this.radius / 2 - 15}
                                                        value={NumCast(this.dataDoc.mass1_positionX)}
                                                        effect={value => {
                                                            this.dataDoc.mass1_xChange = value;
                                                            if (this.simulationType === 'Suspension') {
                                                                const x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200;
                                                                const x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius;
                                                                const deltaX1 = value + this.radius - x1rod;
                                                                const deltaX2 = x2rod - (value + this.radius);
                                                                const deltaY = this.getYPosFromDisplay(NumCast(this.dataDoc.mass1_positionY)) + this.radius;
                                                                let dir1T = Math.PI - Math.atan(deltaY / deltaX1);
                                                                let dir2T = Math.atan(deltaY / deltaX2);
                                                                const tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T));
                                                                const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T);
                                                                dir1T = (dir1T * 180) / Math.PI;
                                                                dir2T = (dir2T * 180) / Math.PI;
                                                                const tensionForce1: IForce = {
                                                                    description: 'Tension',
                                                                    magnitude: tensionMag1,
                                                                    directionInDegrees: dir1T,
                                                                };
                                                                const tensionForce2: IForce = {
                                                                    description: 'Tension',
                                                                    magnitude: tensionMag2,
                                                                    directionInDegrees: dir2T,
                                                                };
                                                                const gravity = this.gravityForce(this.mass1);
                                                                this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]);
                                                            }
                                                        }}
                                                        small
                                                        mode="Freeform"
                                                    />
                                                </td>
                                            )}{' '}
                                            {(!this.dataDoc.simulation_paused || this.simulationType === 'Inclined Plane' || this.simulationType === 'Circular Motion' || this.simulationType === 'Pulley') && (
                                                <td style={{ cursor: 'default' }}>{`${NumCast(this.dataDoc.mass1_positionY)} m`}</td>
                                            )}{' '}
                                            {this.dataDoc.simulation_paused && this.simulationType !== 'Inclined Plane' && this.simulationType !== 'Circular Motion' && this.simulationType !== 'Pulley' && (
                                                <td
                                                    style={{
                                                        cursor: 'default',
                                                    }}>
                                                    <InputField
                                                        lowerBound={1}
                                                        dataDoc={this.dataDoc}
                                                        prop="mass1_positionY"
                                                        step={1}
                                                        unit="m"
                                                        upperBound={this.yMax - 110}
                                                        value={NumCast(this.dataDoc.mass1_positionY)}
                                                        effect={value => {
                                                            this.dataDoc.mass1_yChange = value;
                                                            if (this.simulationType === 'Suspension') {
                                                                const x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200;
                                                                const x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius;
                                                                const deltaX1 = NumCast(this.dataDoc.mass1_positionX) + this.radius - x1rod;
                                                                const deltaX2 = x2rod - (NumCast(this.dataDoc.mass1_positionX) + this.radius);
                                                                const deltaY = this.getYPosFromDisplay(value) + this.radius;
                                                                let dir1T = Math.PI - Math.atan(deltaY / deltaX1);
                                                                let dir2T = Math.atan(deltaY / deltaX2);
                                                                const tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T));
                                                                const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T);
                                                                dir1T = (dir1T * 180) / Math.PI;
                                                                dir2T = (dir2T * 180) / Math.PI;
                                                                const tensionForce1: IForce = {
                                                                    description: 'Tension',
                                                                    magnitude: tensionMag1,
                                                                    directionInDegrees: dir1T,
                                                                };
                                                                const tensionForce2: IForce = {
                                                                    description: 'Tension',
                                                                    magnitude: tensionMag2,
                                                                    directionInDegrees: dir2T,
                                                                };
                                                                const gravity = this.gravityForce(this.mass1);
                                                                this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]);
                                                            }
                                                        }}
                                                        small
                                                        mode="Freeform"
                                                    />
                                                </td>
                                            )}{' '}
                                        </tr>
                                        <tr>
                                            <td
                                                style={{ cursor: 'help' }}
                                                // onClick={() => {
                                                //   window.open(
                                                //     "https://www.khanacademy.org/science/physics/two-dimensional-motion"
                                                //   );
                                                // }}
                                            >
                                                <Box>Velocity</Box>
                                            </td>
                                            {(!this.dataDoc.simulation_paused || (this.simulationType !== 'One Weight' && this.simulationType !== 'Circular Motion')) && (
                                                <td style={{ cursor: 'default' }}>{`${NumCast(this.dataDoc.mass1_velocityX)} m/s`}</td>
                                            )}{' '}
                                            {this.dataDoc.simulation_paused && (this.simulationType === 'One Weight' || this.simulationType === 'Circular Motion') && (
                                                <td
                                                    style={{
                                                        cursor: 'default',
                                                    }}>
                                                    <InputField
                                                        lowerBound={-50}
                                                        dataDoc={this.dataDoc}
                                                        prop="mass1_velocityX"
                                                        step={1}
                                                        unit="m/s"
                                                        upperBound={50}
                                                        value={NumCast(this.dataDoc.mass1_velocityX)}
                                                        effect={action(value => {
                                                            this.dataDoc.mass1_velocityXstart = value;
                                                            this._simReset++;
                                                        })}
                                                        small
                                                        mode="Freeform"
                                                    />
                                                </td>
                                            )}{' '}
                                            {(!this.dataDoc.simulation_paused || this.simulationType !== 'One Weight') && <td style={{ cursor: 'default' }}>{this.dataDoc.mass1_velocityY + ''} m/s</td>}{' '}
                                            {this.dataDoc.simulation_paused && this.simulationType === 'One Weight' && (
                                                <td
                                                    style={{
                                                        cursor: 'default',
                                                    }}>
                                                    <InputField
                                                        lowerBound={-50}
                                                        dataDoc={this.dataDoc}
                                                        prop="mass1_velocityY"
                                                        step={1}
                                                        unit="m/s"
                                                        upperBound={50}
                                                        value={NumCast(this.dataDoc.mass1_velocityY)}
                                                        effect={value => {
                                                            this.dataDoc.mass1_velocityYstart = -value;
                                                        }}
                                                        small
                                                        mode="Freeform"
                                                    />
                                                </td>
                                            )}{' '}
                                        </tr>
                                        <tr>
                                            <td
                                                style={{ cursor: 'help' }}
                                                // onClick={() => {
                                                //   window.open(
                                                //     "https://www.khanacademy.org/science/physics/two-dimensional-motion"
                                                //   );
                                                // }}
                                            >
                                                <Box>Acceleration</Box>
                                            </td>
                                            <td style={{ cursor: 'default' }}>
                                                {this.dataDoc.mass1_accelerationX + ''} m/s<sup>2</sup>
                                            </td>
                                            <td style={{ cursor: 'default' }}>
                                                {this.dataDoc.mass1_accelerationY + ''} m/s<sup>2</sup>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td>
                                                <Box>Momentum</Box>
                                            </td>
                                            <td>{Math.round(NumCast(this.dataDoc.mass1_velocityX) * this.mass1 * 10) / 10} kg*m/s</td>
                                            <td>{Math.round(NumCast(this.dataDoc.mass1_velocityY) * this.mass1 * 10) / 10} kg*m/s</td>
                                        </tr>
                                    </tbody>
                                </table>
                            )}
                            {this.simulationMode === 'Freeform' && this.simulationType === 'Pulley' && (
                                <table>
                                    <tbody>
                                        <tr>
                                            <td>Blue Weight</td>
                                            <td>X</td>
                                            <td>Y</td>
                                        </tr>
                                        <tr>
                                            <td>
                                                <Box>Position</Box>
                                            </td>
                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionX} m`}</td>
                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionY} m`}</td>
                                        </tr>
                                        <tr>
                                            <td>
                                                <Box>Velocity</Box>
                                            </td>
                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionX} m/s`}</td>
                                            <td style={{ cursor: 'default' }}>{`${this.dataDoc.mass2_positionY} m/s`}</td>
                                        </tr>
                                        <tr>
                                            <td>
                                                <Box>Acceleration</Box>
                                            </td>
                                            <td style={{ cursor: 'default' }}>
                                                {this.dataDoc.mass2_accelerationX + ''} m/s<sup>2</sup>
                                            </td>
                                            <td style={{ cursor: 'default' }}>
                                                {this.dataDoc.mass2_accelerationY + ''} m/s<sup>2</sup>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td>
                                                <Box>Momentum</Box>
                                            </td>
                                            <td>{Math.round(NumCast(this.dataDoc.mass2_velocityX) * this.mass1 * 10) / 10} kg*m/s</td>
                                            <td>{Math.round(NumCast(this.dataDoc.mass2_velocityY) * this.mass1 * 10) / 10} kg*m/s</td>
                                        </tr>
                                    </tbody>
                                </table>
                            )}
                        </div>
                        {this.simulationType !== 'Pendulum' && this.simulationType !== 'Spring' && (
                            <div>
                                <p>Kinematic Equations</p>
                                <ul>
                                    <li>
                                        Position: x<sub>1</sub>=x<sub>0</sub>+v<sub>0</sub>t+
                                        <sup>1</sup>&frasl;
                                        <sub>2</sub>at
                                        <sup>2</sup>
                                    </li>
                                    <li>
                                        Velocity: v<sub>1</sub>=v<sub>0</sub>+at
                                    </li>
                                    <li>Acceleration: a = F/m</li>
                                </ul>
                            </div>
                        )}
                        {this.simulationType === 'Spring' && (
                            <div>
                                <p>Harmonic Motion Equations: Spring</p>
                                <ul>
                                    <li>
                                        Spring force: F<sub>s</sub>=kd
                                    </li>
                                    <li>
                                        Spring period: T<sub>s</sub>=2&pi;&#8730;<sup>m</sup>&frasl;
                                        <sub>k</sub>
                                    </li>
                                    <li>Equilibrium displacement for vertical spring: d = mg/k</li>
                                    <li>
                                        Elastic potential energy: U<sub>s</sub>=<sup>1</sup>&frasl;
                                        <sub>2</sub>kd<sup>2</sup>
                                    </li>
                                    <ul>
                                        <li>Maximum when system is at maximum displacement, 0 when system is at 0 displacement</li>
                                    </ul>
                                    <li>
                                        Translational kinetic energy: K=<sup>1</sup>&frasl;
                                        <sub>2</sub>mv<sup>2</sup>
                                    </li>
                                    <ul>
                                        <li>Maximum when system is at maximum/minimum velocity (at 0 displacement), 0 when velocity is 0 (at maximum displacement)</li>
                                    </ul>
                                </ul>
                            </div>
                        )}
                        {this.simulationType === 'Pendulum' && (
                            <div>
                                <p>Harmonic Motion Equations: Pendulum</p>
                                <ul>
                                    <li>
                                        Pendulum period: T<sub>p</sub>=2&pi;&#8730;<sup>l</sup>&frasl;
                                        <sub>g</sub>
                                    </li>
                                </ul>
                            </div>
                        )}
                    </div>
                </div>
                <div
                    style={{
                        position: 'fixed',
                        top: this.yMax - 120 + 20 + 'px',
                        left: this.xMin + 90 - 80 + 'px',
                        zIndex: -10000,
                    }}>
                    <svg width={100 + 'px'} height={100 + 'px'}>
                        <defs>
                            <marker id="miniArrow" markerWidth="20" markerHeight="20" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
                                <path d="M0,0 L0,6 L9,3 z" fill="#000000" />
                            </marker>
                        </defs>
                        <line x1={20} y1={70} x2={70} y2={70} stroke="#000000" strokeWidth="2" markerEnd="url(#miniArrow)" />
                        <line x1={20} y1={70} x2={20} y2={20} stroke="#000000" strokeWidth="2" markerEnd="url(#miniArrow)" />
                    </svg>
                    <p
                        style={{
                            position: 'fixed',
                            top: this.yMax - 120 + 40 + 'px',
                            left: this.xMin + 90 - 80 + 'px',
                        }}>
                        {this.simulationType === 'Circular Motion' ? 'Z' : 'Y'}
                    </p>
                    <p
                        style={{
                            position: 'fixed',
                            top: this.yMax - 120 + 80 + 'px',
                            left: this.xMin + 90 - 40 + 'px',
                        }}>
                        X
                    </p>
                </div>
            </div>
        );
    }
}

Docs.Prototypes.TemplateMap.set(DocumentType.SIMULATION, {
    layout: { view: PhysicsSimulationBox, dataField: 'data' },
    options: {
        acl: '',
        _width: 1000,
        _height: 800,
        _layout_nativeDimEditable: true,
        systemIcon: 'BsShareFill',
        // mass1: '', mass2: '', position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: ''
    },
});
