using System; using System.Collections.Generic; namespace Quik.CommandMachine { public class CommandEngine { private int _zIndex = 0; private readonly Stack _zStack = new Stack(); public int ZIndex => _zIndex; private QuikRectangle _viewport; private readonly Stack _viewportStack = new Stack(); private readonly Stack _matrixStack = new Stack(); private Command _customCommandBase = Command.CustomCommandBase; private readonly List _customCommands = new List(); public QuikRectangle Viewport { get; } // TODO: Make a real matrix class. public float[] ActiveTransforms { get; } public StyleStack Style { get; } protected CommandEngine() { Reset(); } public Command RegisterCustomCommand(QuikCommandHandler handler) { Command id = _customCommandBase++; _customCommands.Insert(id - Command.CustomCommandBase, handler); return id; } public void ProcessCommands(QuikRectangle bounds, CommandQueue queue) { if (!queue.Peek().IsCommand) throw new ArgumentException("The first element in the queue must be a command frame."); Reset(); _viewport = bounds; _viewportStack.Push(_viewport); Frame frame; while (queue.TryDequeue(out frame)) { Command cmd = (Command)frame; switch (cmd) { default: if (cmd > Command.CustomCommandBase) { _customCommands[cmd - Command.CustomCommandBase].Invoke(this, queue); } else { ChildProcessCommand(cmd, queue); } break; case Command.ConditionalBegin: ConditionalHandler(queue); break; case Command.ConditionalEnd: /* nop */ break; case Command.Invoke: queue.Dequeue().As().Invoke(this, queue); break; case Command.PushViewport: _viewportStack.Push(_viewport); break; case Command.IntersectViewport: _viewport = QuikRectangle.Intersect((QuikRectangle)queue.Dequeue(), _viewport); break; case Command.StoreViewport: _viewport = (QuikRectangle)queue.Dequeue(); break; case Command.PopViewport: _viewport = _viewportStack.TryPop(out QuikRectangle viewport) ? viewport : bounds; break; case Command.PushZ: _zStack.Push(_zIndex); break; case Command.IncrementZ: _zIndex++; break; case Command.AddZ: _zIndex += (int)queue.Dequeue(); break; case Command.StoreZ: _zIndex = (int)queue.Dequeue(); break; case Command.DecrementZ: _zIndex--; break; case Command.PopZ: _zIndex = _zStack.TryPop(out int zindex) ? zindex : 0; break; } } } protected virtual void ChildProcessCommand(Command name, CommandQueue queue) { } private void Reset() { _zIndex = 0; _zStack.Clear(); _viewport = new QuikRectangle(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue); _viewportStack.Clear(); _matrixStack.Clear(); _matrixStack.Push(null); // TODO: replace with identity matrix. } private void ConditionalHandler(CommandQueue queue) { Frame frame = queue.Dequeue(); if ( frame.IsInteger && (int)frame != 0 || frame.As>().Invoke()) { // Take this branch. return; } // Skip this branch. int depth = 1; while (queue.TryPeek(out frame)) { if (!frame.IsCommand) { queue.Dequeue(); continue; } switch ((Command)frame) { case Command.ConditionalBegin: // Increment conditional depth. depth++; break; case Command.ConditionalEnd: // Decrement condional depth, exit if zero. if (--depth == 0) { queue.Dequeue(); return; } break; } queue.Dequeue(); } } } }