private bool QueueBeforeIfNeedToSwitchExtruders(string lineIn, string lineNoComment)
        {
            // check if there is a travel
            if (sendState == SendStates.WaitingForMove &&
                activeTool != RequestedTool &&                 // is different than the last extruder set
                (lineNoComment.StartsWith("G0 ") || lineNoComment.StartsWith("G1 ")) &&                 // is a G1 or G0
                (lineNoComment.Contains("X") || lineNoComment.Contains("Y") || lineNoComment.Contains("Z")))                    // has a move axis in it
            {
                postSwitchLine = lineIn;

                string beforeGcodeToQueue = "";
                switch (RequestedTool)
                {
                case 0:
                    beforeGcodeToQueue = printer.Settings.GetValue(SettingsKey.before_toolchange_gcode).Replace("\\n", "\n");
                    break;

                case 1:
                    beforeGcodeToQueue = printer.Settings.GetValue(SettingsKey.before_toolchange_gcode_1).Replace("\\n", "\n");
                    break;
                }

                preSwitchPosition = lastDestination.position;
                if (lastDestination.feedRate != double.PositiveInfinity)
                {
                    preSwitchFeedRate = lastDestination.feedRate;
                }
                else
                {
                    preSwitchFeedRate = lastSeenFeedRate;
                }

                // put together the output we want to send
                var gcode = new StringBuilder();
                if (beforeGcodeToQueue.Trim().Length > 0)
                {
                    gcode.Append(printer.Settings.ReplaceMacroValues(beforeGcodeToQueue));
                }

                gcode.Append("\n");

                ManageCoolDownAndOffTemps(gcode);

                // send the actual tool change
                gcode.AppendLine($"T{RequestedTool}");

                // send the marker to let us know we have sent the before gcode
                gcode.AppendLine(completedBeforeGCodeString);

                queuedCommandsStream.Add(gcode.ToString());

                sendState = SendStates.SendingBefore;

                return(true);
            }

            return(false);
        }
        public override string ReadLine()
        {
            if (queuedCommands.Count > 0)
            {
                return(queuedCommands.Dequeue());
            }

            string lineToSend = base.ReadLine();

            if (lineToSend == null)
            {
                return(null);
            }

            if (lineToSend.EndsWith("; NO_PROCESSING"))
            {
                return(lineToSend);
            }

            GCodeFile.GetFirstNumberAfter("F", lineToSend, ref lastSeenFeedRate);

            var requestedToolForTempChange = -1;

            // if we see a temp command remember what heat we are setting
            if (lineToSend.StartsWith("M109") || lineToSend.StartsWith("M104"))
            {
                int toolTemp = 0;
                // get the temp we are setting
                GCodeFile.GetFirstNumberAfter("S", lineToSend, ref toolTemp);
                // set it to the tool we will be changing to
                requestedToolForTempChange = RequestedTool;
                // check if this command contains a tool specification
                GCodeFile.GetFirstNumberAfter("T", lineToSend, ref requestedToolForTempChange);

                if (!lineToSend.Contains("; INACTIVE_COOL_DOWN"))
                {
                    if (targetTemps[requestedToolForTempChange] != toolTemp)
                    {
                        targetTemps[requestedToolForTempChange] = toolTemp;
                    }
                }
            }

            // check if any of the heaters we will be switching to need to start heating
            ManageReHeating(lineToSend);

            if (lineToSend == completedBeforeGCodeString &&
                sendState != SendStates.Normal)
            {
                activeTool = RequestedTool;
                sendState  = SendStates.Normal;
                QueueAfterGCode();
            }

            var lineNoComment = lineToSend.Split(';')[0];

            if (lineNoComment == "G28" ||
                lineNoComment == "G28 Z0")
            {
                sendState     = SendStates.Normal;
                RequestedTool = activeTool = 0;
            }

            // if this command is a temperature change request
            if (requestedToolForTempChange != -1)
            {
                if (requestedToolForTempChange != activeTool)
                {
                    if (DoSmoothieCorrections)
                    {
                        // For smoothie, switch back to the extrude we were using before the temp change (smoothie switches to the specified extruder, marlin repetier do not)
                        queuedCommands.Enqueue($"T{activeTool}");
                    }

                    var temp = GetNextToolTemp(requestedToolForTempChange);
                    if (temp > 0)
                    {
                        return($"{lineToSend.Substring(0, 4)} T{requestedToolForTempChange} S{temp}");
                    }
                    else                     // send the temp as requested
                    {
                        return(lineToSend);
                    }
                }

                // if we are waiting to switch to the next tool
                else if (activeTool != RequestedTool)
                {
                    // if this command does not include the extruder to switch to, than we need to switch before sending it
                    if (!lineNoComment.Contains("T"))
                    {
                        queuedCommands.Enqueue($"T{RequestedTool}");
                    }

                    if (DoSmoothieCorrections)
                    {
                        // For smoothie, switch back to the extrude we were using before the temp change (smoothie switches to the specified extruder, marlin repetier do not)
                        queuedCommands.Enqueue($"T{activeTool}");
                    }

                    // then send the heat command
                    return(lineToSend);
                }
            }

            // if this is a tool change request
            else if (lineToSend.StartsWith("T"))
            {
                int changeCommandTool = -1;
                if (GCodeFile.GetFirstNumberAfter("T", lineToSend, ref changeCommandTool))
                {
                    if (changeCommandTool == activeTool)
                    {
                        if (sendState == SendStates.WaitingForMove)
                        {
                            // we have to switch back to our starting tool without a move
                            // change back to normal processing and don't change tools
                            sendState = SendStates.Normal;
                            var lastRequestedTool = RequestedTool;
                            // set the requested tool
                            RequestedTool = changeCommandTool;
                            // don't send the change we are on the right tool now
                            return($"; switch back without move from T{lastRequestedTool} to T{activeTool}");
                        }
                    }
                    else                     // we are switching tools
                    {
                        if (sendState == SendStates.Normal)
                        {
                            sendState = SendStates.WaitingForMove;
                            // set the requested tool
                            RequestedTool = changeCommandTool;
                            // don't queue the tool change until after the before gcode has been sent
                            return($"; waiting for move on T{RequestedTool}");
                        }
                    }
                }
            }

            // if it is only an extrusion move
            if (sendState == SendStates.WaitingForMove &&
                activeTool != RequestedTool &&                 // is different than the last extruder set
                (lineNoComment.StartsWith("G0 ") || lineNoComment.StartsWith("G1 ")) &&                 // is a G1 or G0
                lineNoComment.Contains("E")                    // it is an extrusion move
                // and have no other position information
                && !lineNoComment.Contains("X") &&
                !lineNoComment.Contains("Y") &&
                !lineNoComment.Contains("Z"))
            {
                double ePosition = 0;

                if (GCodeFile.GetFirstNumberAfter("E", lineNoComment, ref ePosition))
                {
                    // switch extruders
                    queuedCommands.Enqueue($"T{RequestedTool}");

                    if (DoSmoothieCorrections)
                    {
                        // if we know the current E position before the switch
                        // set the E value to the previous E value.
                        if (lastDestination.extrusion != double.PositiveInfinity)
                        {
                            // On Marlin E position is shared between extruders and this code has no utility
                            // On Smoothie E is stored per extruder and this makes it behave the same as Marlin
                            queuedCommands.Enqueue($"G92 E{lastDestination.extrusion}");
                        }
                    }

                    // send the extrusion
                    queuedCommands.Enqueue(lineNoComment + " ; NO_PROCESSING");

                    lastDestination.extrusion = ePosition;

                    if (DoSmoothieCorrections)
                    {
                        // switch back
                        queuedCommands.Enqueue($"T{activeTool}");
                        queuedCommands.Enqueue($"G92 E{lastDestination.extrusion}");
                    }

                    return("");
                }
            }

            if (QueueBeforeIfNeedToSwitchExtruders(lineToSend, lineNoComment))
            {
                return("");
            }

            if (LineIsMovement(lineToSend))
            {
                lastDestination = GetPosition(lineToSend, lastDestination);
            }

            return(lineToSend);
        }
        public override string ReadLine()
        {
            string lineToSend = base.ReadLine();

            if (lineToSend == null)
            {
                return(null);
            }

            if (lineToSend.EndsWith("; NO_PROCESSING"))
            {
                return(lineToSend);
            }

            // check if any of the heaters we will be switching to need to start heating
            ManageReHeating(lineToSend);

            if (lineToSend == compleatedBeforeGCodeString)
            {
                activeTool = requestedTool;
                SendState  = SendStates.Normal;
                QueueAfterGCode();
            }

            // track the tool state
            if (lineToSend.StartsWith("T"))
            {
                int changeCommandTool = -1;
                if (GCodeFile.GetFirstNumberAfter("T", lineToSend, ref changeCommandTool) &&
                    changeCommandTool != activeTool)
                {
                    requestedTool = changeCommandTool;
                    if (SendState == SendStates.Normal)
                    {
                        SendState = SendStates.WaitingForMove;
                        // don't queue the tool change until after the before gcode has been sent
                        return($"; waiting for move on T{requestedTool}");
                    }
                }
            }
            // check if there is a temperature change request
            else if (lineToSend.StartsWith("M104") || lineToSend.StartsWith("M109"))
            {
                double toolBeingSet = -1;
                // if there is a tool specification
                if (GCodeFile.GetFirstNumberAfter("T", lineToSend, ref toolBeingSet))
                {
                    if (toolBeingSet != activeTool)
                    {
                        // For smoothie, switch back to the extrude we were using before the temp change (smoothie switches to the specified extruder, marlin repetier do not)
                        queuedCommandsStream.Add("T{0} ; NO_PROCESSING".FormatWith(activeTool));
                    }
                }
            }

            if (QueueBeforeIfNeedToSwitchExtruders(lineToSend))
            {
                return("");
            }

            if (LineIsMovement(lineToSend))
            {
                lastDestination = GetPosition(lineToSend, lastDestination);
            }

            return(lineToSend);
        }