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
{
    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> _sequences = new Dictionary<string, Sequencer>();

        public Program()
        {
            Console = new MainConsole(this, "Action Sequencer");

            // initialize all the sequencers
            List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
            GridTerminalSystem.GetBlocksOfType(blocks, block => MyIni.HasSection(block.CustomData, "sequence"));
            foreach (IMyTerminalBlock block in blocks)
            {
                Ini.TryParse(block.CustomData);
                string id = Ini.Get("sequence", "id").ToString();

                if (id == "")
                {
                    Console.Print($"No id found for '{block.CustomName}'. Skipping.");
                    continue;
                }
                if (id == "all")
                {
                    Console.Print($"'All' is a reserved keyword. Skipping '{block.CustomName}'.");
                    continue;
                }

                if (!_sequences.ContainsKey(id)) _sequences[id] = new Sequencer(this, id);

                ISequenceable wrapped = SequenceableFactory.MakeSequenceable(this, block, "sequence");
                if (wrapped == null)
                {
                    Console.Print($"Skipping incompatible block '{block.CustomName}'.");
                    continue;
                }

                _sequences[id].AddBlock(wrapped);
            }

            Console.Print($"Found {_sequences.Count} sequences.");
            Console.UpdateTickCount();
        }

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

            if (argument != "")
            {
                _cli.TryParse(argument);

                List<Sequencer> sequencesToRun = new List<Sequencer>();
                bool deploy = !_cli.Switch("stow");

                if (_cli.ArgumentCount > 0 && _cli.Argument(0) == "all")
                {
                    sequencesToRun = _sequences.Values.ToList();
                }
                for (int i = 0; i < _cli.ArgumentCount; i++)
                {
                    string id = _cli.Argument(i);
                    if (!_sequences.ContainsKey(id))
                    {
                        Console.Print($"Ignoring non-existent sequence '{id}'");
                        continue;
                    }
                    sequencesToRun.Add(_sequences[id]);
                }

                foreach (Sequencer sequence in sequencesToRun)
                {
                    Console.Print($"Activating sequence '{sequence.Name}'");
                    _jobs.Add(sequence.RunSequence(deploy));
                }
                if (_jobs.Count > 0) 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;
            }
        }
    }
}
