private (CNCState.CNCState, ProgramBuilderCommand command, int programid, int amount) Process(Arguments args,
                                                                                               ActionProgram.ActionProgram program,
                                                                                               CNCState.CNCState state,
                                                                                               int curprogram,
                                                                                               int curline,
                                                                                               Dictionary <IAction, (int, int)> starts)
        ProcessBlock(Arguments block,
                     ActionProgram.ActionProgram program,
                     CNCState.CNCState state)
        {
            var cmd = block.Options.FirstOrDefault((arg) => (arg.codeType == Arguments.Option.CodeType.Code && (arg.letter == 'G' || arg.letter == 'M')));
            ProgramBuilderCommand command = ProgramBuilderCommand.Continue;
            int pid = -1, amount = -1;

            state = state.BuildCopy();
            state = ProcessParameters(block, program, state);

            foreach (var opt in block.Options)
            {
                if (opt.codeType == Arguments.Option.CodeType.Variable)
                {
                    var    val   = GetValue(opt, state);
                    string varid = opt.varid;
                    state = state.BuildCopy();
                    state.VarsState.Vars[varid] = val.Value;
                }
            }

            if (cmd == null)
            {
                state = ProcessMove(block, program, state);
            }
            else if (cmd.letter == 'G')
            {
                switch (cmd.ivalue1)
                {
                case 0:
                case 1:
                case 2:
                case 3:
                    state = ProcessMove(block, program, state);
                    break;

                case 4:
                    try
                    {
                        if (block.SingleOptions.ContainsKey('P'))
                        {
                            var P  = block.SingleOptions['P'];
                            var dt = GetValue(P, state).Value;
                            state = builder.AddPause(dt, program, state);
                        }
                    }
                    catch
                    {
                        ;
                    }
                    break;

                case 17:
                    state.AxisState.Params.CurrentPlane = AxisState.Plane.XY;
                    break;

                case 18:
                    state.AxisState.Params.CurrentPlane = AxisState.Plane.YZ;
                    break;

                case 19:
                    state.AxisState.Params.CurrentPlane = AxisState.Plane.ZX;
                    break;

                case 20:
                    state.AxisState.Params.SizeUnits = AxisState.Units.Inches;
                    break;

                case 21:
                    state.AxisState.Params.SizeUnits = AxisState.Units.Millimeters;
                    break;

                case 28:
                    state = builder.AddHoming(program, state);
                    break;

                case 30:
                    state = builder.AddZProbe(program, state);
                    break;

                case 53:
                case 54:
                case 55:
                case 56:
                case 57:
                case 58:
                case 59:
                    state = ProcessCoordinatesSystemSet(block, program, state);
                    break;

                case 80:
                    state.DrillingState.Drilling = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 81:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Rapid;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = false;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = false;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 82:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Rapid;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = true;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = false;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 83:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = true;
                    state.DrillingState.Retract        = DrillingState.RetractType.Rapid;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = false;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = false;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 84:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Feed;
                    state.DrillingState.RetractReverse = true;
                    state.DrillingState.Dwell          = false;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = false;
                    state.DrillingState.Tapping        = true;
                    state = ProcessMove(block, program, state);
                    break;

                case 85:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Feed;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = false;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = false;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 86:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Feed;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = false;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = true;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 87:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Manual;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = false;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = true;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 88:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Manual;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = true;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = true;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 89:
                    state.DrillingState.Drilling       = true;
                    state.DrillingState.Peck           = false;
                    state.DrillingState.Retract        = DrillingState.RetractType.Feed;
                    state.DrillingState.RetractReverse = false;
                    state.DrillingState.Dwell          = true;
                    state.DrillingState.LeftHand       = false;
                    state.DrillingState.StopSpindle    = false;
                    state.DrillingState.Tapping        = false;
                    state = ProcessMove(block, program, state);
                    break;

                case 90:
                    state.AxisState.Absolute = true;
                    break;

                case 91:
                    state.AxisState.Absolute = false;
                    break;

                case 92:
                    state = ProcessCoordinatesSet(block, program, state);
                    break;

                case 98:
                    state.DrillingState.RetractDepth = DrillingState.RetractDepthType.InitialHeight;
                    break;

                case 99:
                    state.DrillingState.RetractDepth = DrillingState.RetractDepthType.RHeight;
                    break;
                }
            }
            else if (cmd.letter == 'M')
            {
                switch (cmd.ivalue1)
                {
                case 0:
                    program.AddBreak(state);
                    command = ProgramBuilderCommand.Pause;
                    break;

                case 2:
                    program.AddStop(state);
                    command = ProgramBuilderCommand.Finish;
                    break;

                case 3:
                case 4:
                case 5:
                    state = ProcessToolCommand(block, program, state);
                    break;

                case 6:
                    if (block.SingleOptions.ContainsKey('T'))
                    {
                        // TODO: stop spindle
                        int tool = block.SingleOptions['T'].ivalue1;
                        state = builder.ProcessToolChange(tool, program, state);
                        if (toolManager.ToolChangeInterrupts)
                        {
                            command = ProgramBuilderCommand.Pause;
                        }
                    }
                    break;

                case 97:
                    command       = ProgramBuilderCommand.Call;
                    (pid, amount) = CallSubprogram(block, state);
                    break;

                case 99:
                    command = ProgramBuilderCommand.Return;
                    break;

                case 120:
                    builder.PushState(state.AxisState);
                    break;

                case 121:
                    builder.PopState(state.AxisState);
                    break;

                case 703:
                case 705:
                    state = ProcessSyncToolCommand(block, program, state);
                    break;
                }
            }

            return(state, command, pid, amount);
        }