using Sandbox.Game.EntityComponents;
using Sandbox.ModAPI.Ingame;
using Sandbox.ModAPI.Interfaces;

using SpaceEngineers.Game.ModAPI.Ingame;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;

using VRage;
using VRage.Collections;
using VRage.Game;
using VRage.Game.Components;
using VRage.Game.GUI.TextPanel;
using VRage.Game.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;
using VRage.Game.ObjectBuilders.Definitions;

using VRageMath;

namespace IngameScript
{
    partial class Program : MyGridProgram
    {   
        enum State
        {
            DEFAULT,
            PACKING,
            UNPACKING,
        }

        const float EPSILON = 0.05f;
        const float EPSILON_RAD = (float)(Math.PI / 64);

        State currentState = State.DEFAULT;

        IMyMotorAdvancedStator rotor01;
        IMyMotorAdvancedStator rotor02;
        IMyMotorAdvancedStator hinge01;
        IMyMotorAdvancedStator hinge02;
        IMyMotorAdvancedStator hinge03;
        IMyExtendedPistonBase piston01;

        List<IMyExtendedPistonBase> pistons = new List<IMyExtendedPistonBase>();
        List<string> pistonsNames = new List<string> { "Piston 02", "Piston 03", "Piston 04", "Piston 05", "Piston 06", "Piston 07", };

        IMyLandingGear magneticPlateArm;
        IMyLandingGear lockPlate;
        IMyExtendedPistonBase lockPiston;

        IMyTextSurface output;

        public void Print(string text)
        {
            this.output.WriteText((this.output.GetText() != "" ? Environment.NewLine : "") + text, true);
        }

        public void PrintError(string text)
        {
            this.Print($"Error: {text}");
        }

        public Program()
        {
            this.output = this.Me.GetSurface(0);
            this.output.ContentType = ContentType.TEXT_AND_IMAGE;
            this.output.WriteText("");

            this.Me.CustomData = "Packing";

            this.Print("Mining rover system starting...");

            var stators = new List<IMyMotorAdvancedStator>();
            GridTerminalSystem.GetBlocksOfType(stators);

            foreach (var s in stators)
            {
                if (s.DisplayNameText.Contains("Rotor 01"))
                    this.rotor01 = s;
                else if (s.DisplayNameText.Contains("Rotor 02"))
                    this.rotor02 = s;
                else if (s.DisplayNameText.Contains("Hinge 01"))
                    this.hinge01 = s;
                else if (s.DisplayNameText.Contains("Hinge 02"))
                    this.hinge02 = s;
                else if (s.DisplayNameText.Contains("Hinge 03"))
                    this.hinge03 = s;
            }

            var pistons = new List<IMyExtendedPistonBase>();
            GridTerminalSystem.GetBlocksOfType(pistons);

            foreach (var p in pistons)
            {
                if (p.DisplayNameText.Contains("Piston 01"))
                {
                    this.piston01 = p;
                }
                else if (p.DisplayNameText.Contains("Lock Piston"))
                {
                    this.lockPiston = p;
                }
                else
                {
                    foreach (var name in pistonsNames)
                    {
                        if (p.DisplayNameText.Contains(name))
                        {
                            this.pistons.Add(p);
                            break;
                        }
                    }
                }
            }

            var magneticPlates = new List<IMyLandingGear>();
            GridTerminalSystem.GetBlocksOfType(magneticPlates);
            foreach (var magneticPlate in magneticPlates)
            {
                if (magneticPlate.DisplayNameText.Contains("Magnetic Plate arm"))
                {
                    this.magneticPlateArm = magneticPlate;
                }
                else if (magneticPlate.DisplayNameText.Contains("Lock Plate"))
                {                    
                    this.lockPlate = magneticPlate;
                }
            }

            if (this.rotor01 == null)
            {
                this.PrintError("Can't find 'Rotor 01'");
                return;
            }

            if (this.rotor02 == null)
            {
                this.PrintError("Can't find 'Rotor 02'");
                return;
            }

            if (this.hinge01 == null)
            {
                this.PrintError("Can't find 'Hinge 01'");
                return;
            }

            if (this.hinge02 == null)
            {
                this.PrintError("Can't find 'Hinge 02'");
                return;
            }

            if (this.magneticPlateArm == null)
            {
                this.PrintError("Can't find 'Magnetic Plate arm'");
                return;
            }

            if (this.lockPlate == null)
            {
                this.PrintError("Can't find 'Lock Plate'");
                return;
            }

            if (this.piston01 == null)
            {
                this.PrintError("Can't find 'Piston 01'");
                return;
            }

            if (this.lockPiston == null)
            {
                this.PrintError("Can't find 'Lock Piston'");
                return;
            }


            this.Runtime.UpdateFrequency = UpdateFrequency.Update100;

            this.Print("Mining rover system has started");
        }

        public void Save()
        {
        }

        /// <summary>
        /// Always returns an angle between 0 and pi/2.
        /// </summary>
        /// <param name="a1"></param>
        /// <param name="a2"></param>
        /// <returns></returns>
        float SumAngles(float a1, float a2)
        {
            var r = (float)((a1 + a2) % (2 * Math.PI));

            if (r < 0)
                return (float)(r + 2 * Math.PI);
            else
                return r;
        }

        // Return 'true' if the rotor is in place.
        bool SetRotorVelocity(float targetAngle, IMyMotorAdvancedStator rotor, float tolerance = (float)(Math.PI / 64))
        {
            //if (rotor.Angle)
            return false;
        }

        void UpdateState()
        {
            if (this.currentState == State.UNPACKING)
            {
                if (!this.piston01.Enabled)
                {
                    this.piston01.Enabled = true;
                    foreach (var p in this.pistons)
                        p.Enabled = true;
                }

                bool finished = true;
                //bool roverLocked = false;

                if (this.magneticPlateArm.IsLocked)
                    this.magneticPlateArm.Unlock();

                this.rotor01.TargetVelocityRPM = 0.0f;

                if (this.lockPiston.CurrentPosition <= EPSILON)
                {
                    finished = false;
                    this.lockPiston.Velocity = 0.5f;
                } 
                else if (this.lockPiston.CurrentPosition >= this.lockPiston.MaxLimit - EPSILON)
                {
                    //roverLocked = true;
                    if (!this.lockPlate.IsLocked)
                        this.lockPlate.Lock();
                    this.lockPiston.Velocity = 0.0f;
                }

                if (this.hinge01.Angle <= -Math.PI / 8)
                {
                    this.hinge01.TargetVelocityRPM = 0.0f;
                }
                else // if (roverLocked)
                {
                    this.hinge01.TargetVelocityRPM = -1.5f;
                    finished = false;
                }

                if (this.hinge02.Angle <= -Math.PI / 3)
                {
                    this.hinge02.TargetVelocityRPM = 0.0f;
                }
                else // if (roverLocked)
                {
                    this.hinge02.TargetVelocityRPM = -1.5f;
                    finished = false;
                }

                if (this.piston01.CurrentPosition <= this.piston01.MaxLimit - EPSILON /*&& roverLocked*/)
                {
                    this.piston01.Velocity = 1.0f;
                    finished = false;
                }
                else
                {
                    this.piston01.Velocity = 0.0f;
                }

                if (finished)
                {
                    this.rotor01.RotorLock = false;
                    this.currentState = State.DEFAULT;
                    this.Print("Unpacking terminated");
                }
            }
            else if (this.currentState == State.PACKING)
            {
                bool finished = true;

                var rotor01InPlace = this.rotor01.Angle >= 2 * Math.PI - EPSILON_RAD || this.rotor01.Angle <= EPSILON_RAD;
                if (rotor01InPlace)
                {
                    this.rotor01.RotorLock = true;
                    this.rotor01.TargetVelocityRPM = 0.0f;
                }
                else if (this.rotor01.Angle >= Math.PI)
                {
                    this.rotor01.TargetVelocityRPM = 1.0f;
                    finished = false;
                }
                else // if (this.rotor01.Angle <= Math.PI)
                {
                    this.rotor01.TargetVelocityRPM = -1.0f;
                    finished = false;
                }

                //this.Print($"Hinge 01 angle: {this.hinge01.Angle}");
                if (this.hinge01.Angle <= Math.PI / 4.0 || rotor01InPlace && this.hinge01.Angle <= Math.PI / 2.0 - Math.PI / 32.0)
                {
                    this.hinge01.TargetVelocityRPM = 1.5f;
                    finished = false;
                }
                else
                {
                    this.hinge01.TargetVelocityRPM = 0f;
                }

                if (this.hinge02.Angle <= -Math.PI / 32.0)
                {
                    this.hinge02.TargetVelocityRPM = 1.5f;
                    finished = false;
                }
                else
                {
                    this.hinge02.TargetVelocityRPM = 0f;
                }

                if (this.piston01.CurrentPosition >= EPSILON)
                {
                    this.piston01.Velocity = -1.0f;
                    finished = false;
                }
                else
                {
                    this.piston01.Velocity = 0.0f;
                }

                //var rotor2TargetAngle = 2 * Math.PI - Math.PI / 4; // 315°.                

                if (finished)
                {
                    if (!this.magneticPlateArm.IsLocked)
                        this.magneticPlateArm.Lock();

                    if (this.lockPlate.IsLocked)
                    {
                        this.lockPlate.Unlock();
                        this.lockPiston.Velocity = -0.5f;
                    }

                    this.piston01.Enabled = false;
                    foreach (var p in this.pistons)
                        p.Enabled = false;

                    this.currentState = State.DEFAULT;
                    this.Print("Packing terminated");
                }
            }
        }

        /*
        enum RotateDirection
        {
            RIGHT,
            LEFT,
        }

        enum ElevationDirection
        {
            UP,
            DOWN,
        }

        const double SPEED_ROTATION = 1.0;
        const double SPEED_HINGE = 1.0;
        const double SPEED_EXTEND_1 = 1.0;
        const double SPEED_EXTEND_2 = 1.0;

        void Rotate(RotateDirection direction)
        {
            //if (this.rotor01.)
        }

        void ChangeElevation(ElevationDirection direction)
        {

        }

        void LockUnlock()
        {
            if (rotor01.Base)
        }*/

        public void Main(string argument, UpdateType updateSource)
        {
            if ((updateSource & UpdateType.Update100) != 0)
            {
                this.UpdateState();
            }
            else if ((updateSource & (UpdateType.Terminal | UpdateType.Trigger)) != 0)
            {
                switch (argument)
                {
                    case "UNPACK":
                        this.Print("Unpacking...");
                        this.currentState = State.UNPACKING;
                        break;

                    case "PACK":
                        this.Print("Packing...");
                        this.currentState = State.PACKING;
                        break;

                    case "STOP":
                        this.currentState = State.DEFAULT;
                        break;

                    /*
                    case "LOCK OR UNLOCK":
                        LockUnlock();
                        break;

                    case "DOWN":
                        ChangeElevation(ElevationDirection.DOWN);
                        break;

                    case "UP":
                        ChangeElevation(ElevationDirection.UP);
                        break;

                    case "LEFT":
                        Rotate(RotateDirection.LEFT);
                        break;

                    case "RIGHT":
                        Rotate(RotateDirection.RIGHT);
                        break;*/

                    default:
                        Echo($"Uknown command: {argument}");
                        break;
                }
            }
        }
    }
}

