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
    {
        const int CONSOLE_NB_LINES = 7;
        const string GRID_PREFIX = "[Base]";

        const float PISTON_SPEED = 0.05f;
        enum State
        {
            NORMAL,
            MINING,
            STOPPING_MINING,
        }

        const float EPSILON = 0.05f;

        State currentState = State.NORMAL;

        readonly Output output;

        List<IMyExtendedPistonBase> pistons = new List<IMyExtendedPistonBase>();

        IMyLandingGear magneticPlate;
        List<IMyShipDrill> drills = new List<IMyShipDrill>();

        IMySoundBlock soundAlert;
        List<IMyLightingBlock> rotatingLights = new List<IMyLightingBlock>();

        IMyShipConnector minerConnector;

        public Program()
        {
            var output = this.Me.GetSurface(0);
            this.output = new Output(output, CONSOLE_NB_LINES);

            this.output.Print("Base system starting...");

            this.InitMiningSystem();

            this.minerConnector = this.GridTerminalSystem.GetBlockWithName("[Base] Connector Miner") as IMyShipConnector;
            if (this.minerConnector == null)
                this.output.Print($"Error: miner connector not found");

            this.Runtime.UpdateFrequency = UpdateFrequency.Update100;

            this.output.Print("Base system has started");
        }

        void InitMiningSystem()
        {
            this.output.Print("Mining system initializing...");

            this.GridTerminalSystem.GetBlocksOfType(
                this.drills,
                (IMyShipDrill drill) => drill.CustomName.StartsWith(GRID_PREFIX)
            );

            this.output.Print($"Nb of drills: {this.drills.Count}");

            this.GridTerminalSystem.GetBlocksOfType(
                this.pistons,
                (IMyExtendedPistonBase piston) => piston.CustomName.StartsWith(GRID_PREFIX) && piston.CustomName.Contains("Miner")
            );

            this.output.Print($"Nb of pistons: {this.pistons.Count}");

            this.magneticPlate = this.GridTerminalSystem.GetBlockWithName("[Base] Magnetic Plate Miner") as IMyLandingGear;

            this.soundAlert = this.GridTerminalSystem.GetBlockWithName("[Base] Sound Block Miner") as IMySoundBlock;
            if (this.soundAlert == null)
                this.output.Print($"Error: sound alert system not found");

            this.GridTerminalSystem.GetBlocksOfType(
                this.rotatingLights,
                (IMyLightingBlock light) => light.CustomName.StartsWith(GRID_PREFIX) && light.CustomName.Contains("Miner")
            );

            this.output.Print($"Nb of rotating lights: {this.rotatingLights.Count}");

            this.output.Print("Mining system initialized");
        }

        public void Save()
        {
        }

        void UpdateState()
        {
            if (this.currentState == State.STOPPING_MINING)
            {
                bool finished = true;

                foreach (var d in this.drills)
                {
                    d.Enabled = false;
                }

                foreach (var p in this.pistons)
                {
                    var distanceDocked = 0.0;

                    if (p.CustomName == "[Base] Piston Miner 02")
                        distanceDocked = 0.1;

                    if (p.CurrentPosition > distanceDocked + EPSILON)
                    {
                        finished = false;
                        p.Velocity = 5 * -PISTON_SPEED;
                    }
                    else
                    {
                        p.Velocity = 0.0f;
                    }
                }

                if (finished)
                {
                    foreach (var p in this.pistons)
                        p.Enabled = false;

                    foreach (var l in this.rotatingLights)
                        l.Enabled = false;

                    this.soundAlert.Stop();

                    this.output.Print("Drills parked, operation terminated");
                    this.currentState = State.NORMAL;
                }
            }
            else if (this.currentState == State.MINING)
            {
                this.magneticPlate.Unlock();

                foreach (var p in this.pistons)
                {
                    p.Enabled = true;
                    p.Velocity = PISTON_SPEED;
                }

                foreach (var d in this.drills)
                {
                    d.Enabled = true;
                }
            }

            // Send miner connector position.
            this.IGC.SendBroadcastMessage("POSITION_CONNECTOR_MINER", this.minerConnector.WorldMatrix);
        }

        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 "STOP_MINING":
                        this.output.Print("Stopping mining...");
                        this.currentState = State.STOPPING_MINING;
                        break;

                    case "START_MINING":
                        this.soundAlert.Play();
                        foreach (var l in this.rotatingLights)
                            l.Enabled = true;

                        this.output.Print("Mining in progress...");
                        this.currentState = State.MINING;
                        break;

                    default:
                        this.output.Print($"Uknown command: {argument}");
                        break;
                }
            }
        }
    }
}

