using Sandbox.ModAPI.Ingame;
using System.Collections.Generic;
using VRage.Game.ModAPI.Ingame.Utilities;

namespace IngameScript
{
    public partial class Program : MyGridProgram, IConsoleProgram
    {
        public IConsole Console { get; private set; }
        public MyIni Ini { get; } = new MyIni();

        private IEnumerator<bool> _job = null;
        private MyCommandLine _cli = new MyCommandLine();

        private List<IMyShipConnector> _dockingPorts = new List<IMyShipConnector>();
        private ActionGroup _actionGroup;

        public Program()
        {
            Console = new MainConsole(this, "Docking Controller");
            _actionGroup = new ActionGroup(Console, "docking");

            // We store a list of the dockingPorts because we will only dock if one is ready to lock.
            GridTerminalSystem.GetBlocksOfType(_dockingPorts, blockFilter);
            foreach (IMyShipConnector dockingPort in _dockingPorts)
            {
                _actionGroup.AddActionBlock("dock", 0, new BlockActionConnector(dockingPort, ConnectorAction.Connect));
                _actionGroup.AddActionBlock("undock", 1, new BlockActionConnector(dockingPort, ConnectorAction.Disconnect));
            }

            List<IMyThrust> thrusters = new List<IMyThrust>();
            GridTerminalSystem.GetBlocksOfType(thrusters, blockFilter);
            foreach (IMyThrust thruster in thrusters)
            {
                _actionGroup.AddActionBlock("dock", 1, new BlockActionThruster(thruster, ThrusterAction.Disable));
                _actionGroup.AddActionBlock("undock", 0, new BlockActionThruster(thruster, ThrusterAction.Enable));
            }

            List<IMyGasTank> gasTanks = new List<IMyGasTank>();
            GridTerminalSystem.GetBlocksOfType(gasTanks, blockFilter);
            foreach (IMyGasTank gasTank in gasTanks)
            {
                _actionGroup.AddActionBlock("dock", 1, new BlockActionGasTank(gasTank, GasTankAction.Stockpile));
                _actionGroup.AddActionBlock("undock", 0, new BlockActionGasTank(gasTank, GasTankAction.Dispense));
            }

            Console.Print($"Found {_dockingPorts.Count} docking ports.");
            Console.Print($"Found {thrusters.Count} thrusters.");
            Console.Print($"Found {gasTanks.Count} gas tanks.");

            Console.UpdateTickCount();
        }

        public void Main(string argument, UpdateType updateSource)
        {
            Console.UpdateTickCount();
            if (argument != "")
            {
                if (_job != null)
                {
                    Console.Print("Already performing a docking action.");
                }
                else
                {
                    _cli.TryParse(argument);
                    if (_cli.ArgumentCount == 0)
                    {
                        Console.Print("Error: Must pass an argument, 'dock' or 'undock'.");
                        return;
                    }
                    string action = _cli.Argument(0).ToLower();

                    if (action == "dock" && !readyToDock())
                    {
                        Console.Print("No docking port in a dockable state.");
                        return;
                    }

                    _job = _actionGroup.RunAction(_cli.Argument(0).ToLower());
                    Runtime.UpdateFrequency = UpdateFrequency.Update10;
                }
            }

            if (_job != null && !_job.MoveNext())
            {
                _job.Dispose();
                _job = null;
                Console.Print("Docking action complete.");
                Runtime.UpdateFrequency = UpdateFrequency.None;
            }
        }

        private bool blockFilter(IMyTerminalBlock block)
        {
            if (!block.IsSameConstructAs(this.Me)) return false;
            if (MyIni.HasSection(block.CustomData, "docking"))
            {
                Ini.TryParse(block.CustomData);
                return !Ini.Get("docking", "ignore").ToBoolean(false);
            }
            return true;
        }

        private bool readyToDock()
        {
            foreach (IMyShipConnector dockingPort in _dockingPorts)
            {
                if (dockingPort.Status == MyShipConnectorStatus.Connectable) return true;
            }
            return false;
        }
    }
}
