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

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

        private MyCommandLine _cli = new MyCommandLine();
        private List<IEnumerator<bool>> _jobs = new List<IEnumerator<bool>>();
        private Dictionary<string, Sequencer> _doors = new Dictionary<string, Sequencer>();

        public Program()
        {
            Console = new MainConsole(this, "Door Controller");

            List<IMyTerminalBlock> doorBlocks = new List<IMyTerminalBlock>();
            GridTerminalSystem.GetBlocksOfType(doorBlocks, block => MyIni.HasSection(block.CustomData, "mechDoor"));
            foreach (IMyTerminalBlock block in doorBlocks)
            {
                Ini.TryParse(block.CustomData);
                string doorName = Ini.Get("mechDoor", "id").ToString();
                // Create the door if this is a new id
                if (!_doors.ContainsKey(doorName))
                {
                    _doors[doorName] = new Sequencer(this, doorName);
                }

                // Add the part; the Door object handles typing and sequencing.
                ISequenceable wrapped = SequenceableFactory.MakeSequenceable(this, block, "mechDoor");
                if (!(block is IMyShipMergeBlock)) wrapped.Step = 1; // TODO: actually support merge blocks here
                if (wrapped == null) { Console.Print($"Tried to add incompatible block '{block.CustomName}'"); continue; }
                _doors[doorName].AddBlock(wrapped);
            }

            Console.Print($"Found {_doors.Keys.Count} doors.");
            Console.UpdateTickCount();
        }

        public void Main(string argument, UpdateType updateSource)
        {
            Console.UpdateTickCount();

            if (updateSource == UpdateType.Trigger || updateSource == UpdateType.Terminal)
            {
                // Create a new job
                _cli.TryParse(argument);
                List<Sequencer> doorsToControl = new List<Sequencer>();

                if (_cli.ArgumentCount == 0)
                {
                    Console.Print("No arguments passed. Controlling all doors.");
                    foreach (Sequencer door in _doors.Values)
                    {
                        if (door.Running) continue;
                        doorsToControl.Add(door);
                    }
                }

                for (int i = 0; i < _cli.ArgumentCount; i++)
                {
                    string key = _cli.Argument(i);
                    if (!_doors.ContainsKey(key))
                    {
                        Console.Print($"Door '{key}' not found. Skipping.");
                        continue;
                    }
                    if (_doors[key].Running)
                    {
                        Console.Print($"Door '{key}' already moving. Skipping.");
                        continue;
                    }
                    doorsToControl.Add(_doors[key]);
                }

                if (doorsToControl.Count == 0)
                {
                    Console.Print("No doors found. Not creating new job.");
                }
                else
                {
                    Console.Print("Creating new job(s).");
                    bool deploy = _cli.Switch("deploy") || _cli.Switch("open");
                    foreach (Sequencer door in doorsToControl)
                    {
                        _jobs.Add(door.RunSequence(deploy));
                    }
                    Runtime.UpdateFrequency |= UpdateFrequency.Update10;
                }
            }

            // Process running jobs
            for (int i = 0; i < _jobs.Count; i++)
            {
                if (_jobs[i].MoveNext()) continue;

                _jobs[i].Dispose();
                _jobs.Remove(_jobs[i]);
                i--;
                Console.Print("Operation Complete.");
            }

            if (_jobs.Count == 0)
            {
                Runtime.UpdateFrequency = UpdateFrequency.None;
            }
        }
    }
}
