using EmptyKeys.UserInterface.Generated.DataTemplatesContracts_Bindings;

using Sandbox.Game.Entities;
using Sandbox.Game.Entities.Cube;
using Sandbox.Game.EntityComponents;
//using Sandbox.ModAPI;
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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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 string MISSILE_GRID_PREFIX = "[PM]";

        const float EPSILON = 0.05f;
        const double DELAY_BEFORE_TRAVELLING_MODE = 5000; // [ms] (5 s).
        const double AUTO_DESTRUCTION_AFTER = 120000; // [ms] (2 min). Or if the hydrogen tank is empty.

        enum State
        {
            NORMAL,
            LAUNCHING,
            TRAVELLING,
        }

        State currentState = State.NORMAL;

        readonly Output output;
        readonly IMyCubeGrid grid;

        int tickFromStart;
        IMyThrust forwardThruster;
        IEnumerable<IMyThrust> thrusters;
        IMyFlightMovementBlock aiMove;
        IMyOffensiveCombatBlock aiCombat;
        IEnumerable<IMyWarhead> warheads;
        IMyGasTank gasTank;
        IMyLightingBlock light;

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

            this.output.Print("Missile controller system starting...");

            this.grid = this.Me.CubeGrid;
            this.Runtime.UpdateFrequency = UpdateFrequency.Update10;

            this.output.Print("Missile controller system started");
        }

        bool FindElements()
        {
            if (this.forwardThruster == null)
                this.forwardThruster = this.GridTerminalSystem.GetBlock<IMyThrust>("[PM] Hydrogen Thruster 01", this.grid);
            if (this.forwardThruster == null)
            {
                this.output.Print("Error: Cannot find forward thruster");
                return false;
            }

            if (this.thrusters == null)
            {
                this.thrusters = this.GridTerminalSystem.GetBlocksFromGroup<IMyThrust>("[PM] Thrusters", this.grid);
                if (this.thrusters.Count() == 0)
                {
                    this.output.Print("Error: Cannot find thrusters");
                    return false;
                }
            }

            if (this.aiMove == null)
                this.aiMove = this.GridTerminalSystem.GetBlock<IMyFlightMovementBlock>("[PM] AI Flight (Move)", this.grid);
            if (this.aiMove == null)
            {
                this.output.Print("Error: Cannot find AI move");
                return false;
            }

            if (this.aiCombat == null)
                this.aiCombat = this.GridTerminalSystem.GetBlock<IMyOffensiveCombatBlock>("[PM] AI Offensive (Combat)", this.grid);
            if (this.aiCombat == null)
            {
                this.output.Print("Error: Cannot find AI combat");
                return false;
            }

            if (this.warheads == null)
                this.warheads = this.GridTerminalSystem.GetBlocksFromGroup<IMyWarhead>("[PM] Warheads", this.grid);
            if (this.warheads.Count() == 0)
            {
                this.output.Print("Error: Cannot find any warhead");
                return false;
            }
            // Debug.
            //else
            //{
            //    this.output.Print(String.Format("Number of warhead: {0}", this.warheads.Count()));
            //}

            if (this.gasTank == null)
                this.gasTank = this.GridTerminalSystem.GetBlock<IMyGasTank>("[PM] Hydrogen Tank", this.grid);
            if (this.gasTank == null)
            {
                this.output.Print("Error: Cannot find gas tank");
                return false;
            }

            if (this.light == null)
                this.light = this.GridTerminalSystem.GetBlock<IMyLightingBlock>("[PM] Light", this.grid);
            if (this.light == null)
            {
                this.output.Print("Error: Cannot find light");
                return false;
            }

            return true;
        }

        bool BlinkBeforeBeingAutodestructed()
        {
            return this.MsSinceLaunch > AUTO_DESTRUCTION_AFTER - 3000;
        }

        bool MustBeAutodestructed()
        {
            return this.MsSinceLaunch > AUTO_DESTRUCTION_AFTER;
        }

        bool EnemyAtRange()
        {
            return this.aiCombat.SearchEnemyComponent.FoundEnemyId.HasValue;
        }

        void UpdateState10()
        {
            this.tickFromStart += 10;

            switch (this.currentState)
            {
                case State.LAUNCHING:
                    if (!this.FindElements())
                    {
                        this.output.Print("Cannot find all missile elements, launching aborted");
                        this.currentState = State.NORMAL;
                        break;
                    }

                    this.forwardThruster.Enabled = true; // Only one thruster is enabled when launching.
                    this.forwardThruster.ThrustOverridePercentage = 1;
                    if (this.MsSinceLaunch > DELAY_BEFORE_TRAVELLING_MODE && (this.EnemyAtRange() || this.BlinkBeforeBeingAutodestructed()))
                    {
                        foreach (var thruster in this.thrusters)
                        {
                            if (thruster != this.forwardThruster)
                                thruster.Enabled = true;
                        }

                        this.forwardThruster.ThrustOverridePercentage = 0;
                        this.aiMove.Enabled = true;

                        foreach (var warhead in this.warheads)
                            warhead.IsArmed = true;

                        this.output.Print($"Travelling mode");
                        this.currentState = State.TRAVELLING;
                    }
                    break;

                case State.TRAVELLING:
                    if (this.BlinkBeforeBeingAutodestructed())
                        this.light.BlinkIntervalSeconds = 0.5f;

                    if (this.gasTank.FilledRatio <= EPSILON || this.MustBeAutodestructed())
                    { 
                        Detonate();
                    }
                    break;

                case State.NORMAL:
                    break; // Nothing;
            }
        }

        void Detonate()
        {
            foreach (var warhead in this.warheads)
                warhead.Detonate(); 
        }

        public void Save()
        {
        }

        double MsSinceLaunch
        {
            get { return (double)this.tickFromStart / 60 * 1000; }
        }

        public void Main(string argument, UpdateType updateSource)
        {
            if ((updateSource & UpdateType.Update10) != 0)
            {
                this.UpdateState10();
            }
            else if ((updateSource & (UpdateType.Script | UpdateType.Terminal | UpdateType.Trigger)) != 0)
            {
                switch (argument)
                {
                    case "START":
                        this.output.Print("Launching mode");
                        this.tickFromStart = 0;
                        this.currentState = State.LAUNCHING;
                        break;

                    case "STOP":
                        this.output.Print("Stop mode");
                        this.currentState = State.NORMAL;
                        break;

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