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
    {
        private MyCommandLine _cli;
        private MyIni _ini;
        private Console _console;
        private List<IEnumerator<bool>> _jobs;
        private Dictionary<string, Sequencer> _doors;
        private int _tickCount;

        public Program()
        {
            _tickCount = 0;
            _cli = new MyCommandLine();
            _ini = new MyIni();
            _console = new Console(this, _ini);
            _jobs = new List<IEnumerator<bool>>();
            _doors = new Dictionary<string, Sequencer>();

            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(doorName, new PrefixedConsole(_console, doorName));
                }

                // Add the part; the Door object handles typing and sequencing.
                ISequenceable wrapped = SequenceableFactory.MakeSequenceable(block, _ini, "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.");
        }

        public void Main(string argument, UpdateType updateSource)
        {
            _console.PrintLower($"Total Ticks: {_tickCount++}");

            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.Update1;
                }
            }

            // 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;
            }
        }
    }
}