#H-Series_gCode_PostProcessor.py
#Contributed by /retnel

import os   #For intergrating universal file I/O processing in Host OS (i.e. Windows, Linux, and Mac)
import sys  #For intergrating Error Handling
import datetime

begin_time = datetime.datetime.now()

#Variables for tracking Total Lines of
#   gCode, and Tool Changes
#   (debug only, not used for processing)
iLines=0
Tool_Count = [0, 0, 0, 0, 0]        #
Prime_Count  = [0, 0, 0, 0, 0]

iLayers=-1                           #Use to keep track of layers and is (used for processing and debug)
Tool_Iteration = [0, 0, 0, 0, 0]    #Variables for tracking when to add Tool Changeing Priming Cycle
Tool_Prime = [0, 0, 0, 0, 0, 0]
Error_ID = -1                       #Variables for error messages.
Tool_Layer = 1
Start_gCode = 2
Seek_Point = -1

Print_Settings = False
#tPrime_mark = False
#tActive_mark = False
#tStandby_mark = False
Delete_Original = False
Print_Report = False
Layer_Change = False
Tool_Change = False
Line_Copy = False
Strip_Settings = False
Reorder_XYZ = False
Reorder_XYZ_Flag = False
Tool_Reorder_XYZ = False

XY_Settings = "XY_Settings"
Z_Settings = "Z_Settings"

#Variables for tracking when settings are loaded

settings_found = [False, False, False, False]
#settings_found = [tPrime_mark,       tStandby_mark,     tActive_mark,      Print_Report]
#settings_found = [settings_found[0], settings_found[1], settings_found[2], settings_found[3]]

#for looking up pre-defined error messages
def fetch_errorMessage(ErrorID):
   switcher = {
      0: "Error Generated in Opening Files Block!"  + "\n",

      1: "Error Generated in Reading Input File Block!" + "\n" ,

      2: "Error Generated in tPrime_Settings Block!" + "\n" ,

      3: "Invalid PrimeCleaning Settings," + "\n" + "   Ensure 'ToolPrime={T1:T2:T3:T4:T5:Layer/Tool}' is located in your " + "\n" + "   Simplify3D 'Starting Script' for all or your 'Processes'."  + "\n",
      4: "Error Generated in StandbyTemp_Settings Block!"  + "\n" ,

      5: "Invalid StandbyTemps Settings," + "\n" + "   Ensure 'ToolStandbyTemperatures={T1:T2:T3:T4:T5}' is located in your " + "\n" + "   Simplify3D 'Starting Script' for all or your 'Processes'." + "\n",  

      6: "Error Generated in ActiveTemp_Settings Block!"  + "\n",  

      7: "Invalid ActiveTemps Settings," + "\n" + "   Ensure 'ToolActiveTemperatures={T1:T2:T3:T4:T5}' is located in your " + "\n" + "   Simplify3D 'Starting Script' for all or your 'Processes'." + "\n",   

      8: "\n" + "   StandbyTemperature, ActiveTemps, PrimeCleaning, " + "\n" + "   and Tool Changing Settings Not found." + "\n" + "   Please Ensure:"  + "\n" + "   ';PrimeCleaning={T1:T2:T3:T4:T5:Layer/Tool}'," + "\n" + "   ';ToolActiveTemperatures={T1:T2:T3:T4:T5}', and" + "\n" + "   ';ToolStandbyTemperatures={T1:T2:T3:T4:T5}' are located in your" + "\n" + "   Simplify3D 'Starting Script' for all or your 'Processes'." + "\n" + "   And Please Ensure:" + "\n" + "   ';ToolChanging={[new_tool]}' is located in your " + "\n" + "   Simplify3D 'Tool Changing Script' for all or your 'Processes'." + "\n",  

      9: "Error Generated in tool_change Block!", 

      10:   "Invalid Tool Number found," + "\n" + "   Ensure ';ToolChanging={[new_tool]}' is located in your " + "\n" + "   Simplify3D 'Tool Changing Script' for all or your 'Processes'."  + "\n",    

      11:   "Invalid Tool Priming setting found," + "\n" + "   Ensure Your Tool Priming setting:" + "\n" + "              Are In This Format:Using These Settings:';PrimeCleaning=T1:T2:T3:T4:T5:Layer/Tool'." + "\n" + "   Using These Settings:" + "\n" + "              Cleaning Iterations{-1=Always; 0=Never; 1=Once; 2 and Up = Integral}" + "\n" + "              Integrator{0=Layer; 1=Tool}" + "\n",        

      12:   "Error Generated in writing gCode line to .TEMP file Block!",       

      13:   "Error Generated in finalizing post-processing Block!",

      14:   "Error Generated in finalizing post-processing while inserting Print report Block!"
   }
   return(switcher.get(ErrorID, "Unknown Error"))


def get_PrimeSettingName(iSetting):
   strReturn = ""
   if (iSetting == -1):
       strReturn = "Alaways"
   elif (iSetting == 0):
      strReturn = "Never"

   elif (iSetting == 1):
      strReturn = "Once"
   else:
      strReturn = str(iSetting) + ": Cycles"
   return(strReturn)

def settings_Builder(strHeader, strSetting):
    sB_Header = strHeader 
    sB_Len = (len(strSetting) / 2)
    sB_dTwo = int((int(len(sB_Header)/2))-sB_Len)
    if ((sB_Len - int(sB_Len)) > 0):
       icnt = -1
    else:
       icnt = 0
    hd = (len(sB_Header)-(len(sB_Header)/2)+icnt-int(len(strSetting)))
    #print(str(hd))
    ft = (len(sB_Header)-(len(sB_Header)/2)-int(len(strSetting)))
    #print(str(ft))
    #print("|" + sB_Header + "|")
    strSpacing = sB_Header
    for el in strSpacing:
       strSpacing = strSpacing.replace(el, " ")
    
    msgg = strSpacing[:int(sB_dTwo)-icnt]
    msgg += strSetting
    msgg += strSpacing[:int(sB_dTwo)]
    return msgg


def process_outputpaths(strPath):           #Function for generating Output File Paths
    file_path = (os.path.dirname(strPath))  #Get original gCode file path.
    file_name = (os.path.split(strPath))    #get original gCode file name.
    
    output_file_name = file_name[1].replace(".gcode", "_(H-Series).gcode")  #Final output filename = ('original_file_name'_(H-Series).gcode)
    output_file_path = os.path.join(file_path, output_file_name)

    temp_file_name = file_name[1].replace(".gcode", "_(H-Series).TEMP")     #Temporary output filename = ('original_file_name'_(H-Series).TEMP)
    temp_file_path = os.path.join(file_path, temp_file_name)

    tFile_name = file_name[1].replace(".gcode", "_(H-Series).~temp")     #Temporary output filename = ('original_file_name'_(H-Series).~temp)
    tFile_path = os.path.join(file_path, tFile_name)

    error_file_name = file_name[1].replace(".gcode", "_(H-Series).ERROR")   #Error output filename = ('original_file_name'_(H-Series).ERROR)
    error_file_path = os.path.join(file_path, error_file_name)    

    all_paths = (output_file_path, temp_file_path, error_file_path, tFile_path)

    return(all_paths)

def isArray_or_Integers(intStrArray):   #Ensures Settings String Array contains only integers, Also Part of Error Handling.
    try:
        for item in intStrArray:
            i = int(item)
    except:  return False
    return True

def Build_Settings_Array(readfile_line, strSetting): #Build array of integers for settings
    integer_settings_array = []                      #Initialize return array for return
    line = readfile_line.replace(strSetting,"")      #First we remove the maker tag. i.e. ';PrimeCleaning={'
    array_line = line.split("}")                     #Split the remaining string by settings "}", closing braket.
    string_settings_array = array_line[0].split(":") #Load the settings left over that are in 'T1:T2:T3:T4:T5:Layer/Tool' format into an array.
    if (isArray_or_Integers(string_settings_array)): #All settings should be numbers, so we make sure as they all should be numbers.
        for item in string_settings_array:           #Itterate through the array items.
            integer_settings_array.append(int(item)) #Add setting element after converting it form a string to an integer.
        return (integer_settings_array)              #If string is successfully converted into array of integers, the function returns it.
        readfile_line = None                         #We are done with this variable so we now set it to the python equivalnt of 'Nothing'
        array_line = None                            #We are done with this variable so we now set it to the python equivalnt of 'Nothing'
    else:   
        return (["NaN", "NaN", "NaN", "NaN", "NaN"])    #If provided string does not contain an array of integers, return the error array.

def Process_Error(tempPath, errorPath):   #If an error is processd
    if os.path.exists(tempPath):
        os.rename(tempPath, errorPath) #Rename 'original_file_name'_(H-Series).TEMP to Error file name 'original_file_name'_(H-Series).ERROR

print("Post-Processing Started!")
try:
   #Error message if resulting error is not defined for opening files for I/O.
    Error_ID = -1  #Set Error ID

#Test path for use during debug
    #    If you use this method for testing, the 'r' before the "stringpath"
    #    is required to ensure python uses the string in raw mode,
    #    otherwise python corrupts it. (Not required with path passed from argument)
    #inPath = (os.path.join("Examples", "Circle.gcode"))
    #inPath = (os.path.join("Examples", "Large_Print_Test_NoReport.gcode"))
    #inPath = (os.path.join("Examples", "Large_Print_Test.gcode"))
    #inPath = (os.path.join("Examples", "All_Settings_Missing.gcode"))
    #inPath = (os.path.join("Examples", "Priming_Settings_Error.gcode"))
    #inPath = (os.path.join("Examples", "ActiveTemp_Settings_Error.gcode"))
    #inPath = (os.path.join("Examples", "StandbyTemp_Settings_Error.gcode"))    
    #inPath = (r"F:\user\Documents\3D_Printing\Diabase\TestPrint\TestPrint.gcode")
    #inPath = (r"F:\badon\Documents\3D_Printing\Factory_Prints\RFID_Scanner_Box\RFID_Badge.gcode")
    #inPath = (r"F:\badon\Documents\3D_Printing\Factory_Prints\Bw3\Silver_Metal_Cover_Logo\Silver_Metal_Cover_Logo.gcode")
    #inPath = (r"F:\badon\Documents\3D_Printing\Factory_Prints\Bw3\Silver_Metal_Cover_LogoHandel\Silver_Metal_Cover_LogoHandel.gcode")

#Obtains the gCode file path passed to the script 
    inPath = (sys.argv[1])
    scriptName = (os.path.split(sys.argv[0]))
    scriptDisplyName = str(scriptName[1]).replace("_", " ")
    scriptDisplyName = scriptDisplyName.replace(".py", "")
    output_paths = process_outputpaths(inPath)  #Generates all output file paths
    for op in output_paths:                     #iterate through path to see if they are
        if os.path.exists(op):                  #  present, and delete them to prevent
            os.remove(op)                       #  file permission errors.

    outPath = output_paths[0] #Creates variable for final output file path
    
    tempPath = output_paths[1]   #Creates variable for temporary file path for processing
    
    errorPath = output_paths[2]  #Creates variable for error file path if processing ends with an error.

    reportPath =  output_paths[3]  #Creates variable for temporary file path if insert report is requested.
    #print(reportPath)

    readfile = open(inPath, 'r') #Open the original gCode file for reading.

    outfile = open(tempPath, 'w') #Open the temporary gCode file for writing.

    outfile.write("; Post-Processed G-Code generated by " + scriptDisplyName + "\n") #Denotes that it was post-processed by script

    print("File Paths Generated...")
############## This is where the primary processing starts. ##############################
#Start reading gCode file to be processsd line by line

    tPrime_Settings_MARk = "toolprime={"
    tActive_Settings_MARk = "toolactivetemperatures={"
    tStandby_Settings_MARk = "toolstandbytemperatures={"
    pReport_Settings_MARk = "includereport={1}"
    Delete_Original_MARk = "deleteoriginal={1}"
    cStrip_Settings_MARk = "stripcommits={1}"
    oXYZ_Reorder_MARk = "reorderxyz={1}" #"ReorderXYZ={1}"
    sCommits = ";   "
    pCommits ="; "
    lChange = "; layer"
    tChange = "toolchanging={"
    bLayers = False
    cCopy = False

    print("gCode Processing Started...")  
    for readfile_line in readfile:
        
      
        #Error message if resulting error is not defined for reading gCode.
        Error_ID = 1  #Set Error ID

        #Test Debug Errors
        #test = int("a") #Produces <class 'ValueError'>
        #test = (0/0) #Produces <class 'ZeroDivisionError'>
        #test = open("NONE", 'r') #Produces <class 'FileNotFoundError'>
        
        readfile_line = readfile_line.rstrip() #remove line feed character at end of file line
        iLines += 1                            #Count the lines in the gCode file being processes

        if not (bLayers):
           bLayers = bool(iLayers)#((iLayers + 1) >= 1)
        
#<Main Logic Switch>
           #
           #
           #If not bLayer, then process directly
           #
           #
        if not (Print_Settings):   #If all settings have not been loaded error out.
                                   #All Start Scripting lines are read before first layer change maker.
   #<Tool Prime Settings>
           if (readfile_line.lower().startswith(tPrime_Settings_MARk)):
              print("Priming Settings Found...")
              Error_ID = 2  #Set Error ID

              #If Prime settings are found process them.
              Tool_Prime = Build_Settings_Array(readfile_line.lower(), tPrime_Settings_MARk)

              #Error Handling: Ensure PrimeCleaning Settings are Valid
              if (len(Tool_Prime) != 6 or isArray_or_Integers(Tool_Prime) == False):
                Error_ID = 3  #Set Error ID
                raise

              else: #if settings are as expected finish Processing Settings.
                 Tool_Layer = int(Tool_Prime[5])
               
               
                 if (Tool_Layer): #Create human readable output for Layer/Tool for Script Final Report.
                    intergrator_name = "Tool"
                 else:
                    intergrator_name = "Layer"
              
                 #settings_found= [Prime,    Standby,             Active,          Report]
                 settings_found = [True, settings_found[1], settings_found[2], settings_found[3]]
                 Print_Settings = all(settings_found)
                 #print("tPrime_Settings_MARk")
   #<Tool Prime Settings>


   #<Strip Settings>
           elif (readfile_line.lower().startswith(cStrip_Settings_MARk)):
              Strip_Settings = True
              print("Stripping Commits!")

   #<Active Temperature Settings>
           elif (readfile_line.lower().startswith(tActive_Settings_MARk)):
               print("Active Temperature Settings Found...")
               print(readfile_line.lower())
               #Error message if resulting error is not defined for ActiveTemp_Settings.
               Error_ID = 6  #Set Error ID

               #If Active Temperature settings are found process them.
               active_temperatures = Build_Settings_Array(readfile_line.lower(), tActive_Settings_MARk)  

               #Error Handling: Ensure ActiveTemps Settings are Valid
               if (len(active_temperatures) != 5 or isArray_or_Integers(active_temperatures) == False):
                  Error_ID = 7  #Set Error ID
                  raise
            
               else: #if settings are as expected finish Processing Settings.
                  #settings_found= [     Prime,             Standby,     Active,    Report]
                  settings_found = [settings_found[0], settings_found[1], True, settings_found[3]]
                  Print_Settings = all(settings_found)
                  #print("tActive_Settings_MARk")

   #<Standby Temperature Settings>              
           elif (readfile_line.lower().startswith(tStandby_Settings_MARk)):
              print("Standby Temperature Settings Found...")


              #Error message if resulting error is not defined for StandbyTemp_Settings.
              Error_ID = 4  #Set Error ID

              #If Standby Temperature settings are found process them.
              standby_temperatures = Build_Settings_Array(readfile_line.lower(), tStandby_Settings_MARk)  

              #Error Handling: Ensure StandbyTemps Settings are Valid
              if (len(standby_temperatures) != 5 or isArray_or_Integers(standby_temperatures) == False):
                Error_ID = 5  #Set Error ID
                raise

              else: #if settings are as expected finish Processing Settings.
                 #settings_found= [     Prime,       Standby,     Active,          Report]
                 settings_found = [settings_found[0], True, settings_found[2], settings_found[3]]
                 Print_Settings = all(settings_found)
                 #print("tStandby_Settings_MARk")
   #</Standby Temperature Settings> 


   #<Report Inclusion Setting>
           elif (readfile_line.lower().startswith(pReport_Settings_MARk)):
              print("Report Setting Found...")
              #settings_found= [     Prime,             Standby,           Active,         Report]
              Print_Report = True
              settings_found = [settings_found[0], settings_found[1], settings_found[2], Print_Report]
              Print_Settings = all(settings_found)
              Start_gCode = iLines
              #print("Print_Report")
   #</Report Inclusion Setting>

   #<Reorder_XYZ>
           elif (readfile_line.lower().startswith(oXYZ_Reorder_MARk)):
              print("Reorder_XYZ Setting Found...")
              Reorder_XYZ = True
   #</Reorder_XYZ>


           #elif (readfile_line.lower().startswith(cStrip_Settings_MARk)):


           #elif (readfile_line.lower().startswith(tChange)):
              #Tool_Change = True
              #print("tChange")

   #<Delete Original Setting>
           #elif (readfile_line.lower().startswith(Delete_Original_MARk)):
              #print("Delete Original Setting Found...")
              #Delete_Original = True
              #print("Delete_Original")
   #</Delete Original>


   #<If Layer Change is found before Settings>
           elif (readfile_line.lower().startswith(lChange)):
              settings_found = [settings_found[0], settings_found[1], settings_found[2], True]
              Print_Settings = all(settings_found)
              #Print_Settings = True
              print("lChange")

           elif (readfile_line.lower().startswith(sCommits) or readfile_line.lower().startswith(pCommits)):
              Line_Copy != Strip_Settings
              iLines = iLines - 1
              

   #<if not what we are looking for, then copy it over>
           else:
              Line_Copy != Strip_Settings #True
              iLines = iLines - 1
              #print("Copy Line Over")

        elif (readfile_line.lower().startswith(tChange)):
           Tool_Change = True
           Tool_Reorder_XYZ = True
           #print("P-tChange")


########<If Settings are found we can stop looking for them>           
        else:
           if (readfile_line.lower().startswith(lChange)):
              Layer_Change = True
              #print("lChange")

           elif (readfile_line.lower().startswith(tChange)):
              Tool_Change = True
              Tool_Reorder_XYZ = True
              print("Q-tChange")

   #<Reorder_XYZ>
           elif (Tool_Reorder_XYZ and Reorder_XYZ):
              
              print("(Tool_Reorder_XYZ and Reorder_XYZ)")
              if (readfile_line.startswith("G1 Z")):
                 if (Z_Settings == "Z_Settings"):
                    Z_Settings = readfile_line
                    Reorder_XYZ_Flag = True
                    print("Z Found")
                    print(Z_Settings)
                 
              elif(readfile_line.startswith("G1 X")):
                 XY_Settings = readfile_line
                 outfile.write("M118 S" '"' + "Resuming gCode!" + '"' +"\n")
                 outfile.write(XY_Settings + "\n")
                 outfile.write(Z_Settings + "\n")
                 print(XY_Settings)
                 Z_Settings = "Z_Settings"
                 XY_Settings = "XY_Settings"
                 print("XY Found")
                 print("Reorder_XYZ_Flag")
                 Line_Copy = False
                 Tool_Reorder_XYZ = False
                 Reorder_XYZ_Flag = False
                 print(Reorder_XYZ)
                 print(Tool_Reorder_XYZ)

              #else:
                 #Line_Copy = True
   #</Reorder_XYZ>
  
           else:
              Line_Copy = True
              #print("Copy Line Over")



#</Main Logic Switch>

#<Main Processing Switching>        
   #<Layer_Change>
                   
        if (Layer_Change): #Check to see if Layer change is happening.
           if not (Print_Settings):
              Error_ID = 8  #Set Error ID
              raise
              Layer_Change = True
              
           iLayers += 1                         #Keep track of layer changes.
           outfile.write(readfile_line + "\n")
           if (Tool_Layer == 0):              #If Tool Prime Setting is set to iterate by number 'Layer' changes.
              for i in range(0, 5):            #iterate through tool priming settings,
                 if (Tool_Prime[i] > 1):      #If tool relies on layer interval to integrate off of,
                    Tool_Iteration[i] += 1   #We keep track of the layers between priming cycles.
               
           Line_Copy != Strip_Settings #True
           Layer_Change = False
   #</Layer_Change>              

   #<Tool_Change>               
        elif (Tool_Change):

              #Error message if resulting error is not defined for tool_change.
              Error_ID = 9  #Set Error ID

              print_clean = False      #Initialize 'print_clean', Used to determine when to include tool priming gCode.
              readfile_line = readfile_line.lower().replace(tChange,"")
              array_line = readfile_line.split("}")
              tool_ID = "T" + array_line[0]
              if not (isArray_or_Integers(array_line[0])):#Error Handling: Ensure interger for tool number is valid.

                  Error_ID = 10 #Set Error ID
                  raise

              else:                             #If intTool is known to be an integer.
                  intTool = int(array_line[0])
                  
                  tan = (intTool - 1) #Tool Array Number

                  Tool_Count[tan] += 1 #Everytime the current tool is changed it is counted.
                      
                  if (Tool_Prime[tan] == -1):   #If Tool Prime Setting is set to 'Always' = -1
                      print_clean = True           #'Always' Prime = the tool primes everytime it changes                          
                              
                  elif (Tool_Prime[tan] == 0):  #If Tool Prime Setting is set to 'Never' = 0
                      print_clean = False           #'Never' Prime = the tool never primes.
                          

                  elif (Tool_Prime[tan] == 1):      #If Tool Prime Setting is set to 'Once' = 1
                      if (Tool_Iteration[tan] < 1): #Check to see if tool has been switched to yet.
                          Tool_Iteration[tan] = 1
                          print_clean = True           #'Once' Prime = the tool primes "Once".
                          
                  elif(Tool_Prime[tan] >= 2):
                      if (Tool_Iteration[tan] >= Tool_Prime[tan]):#If Tool Prime Setting is set to 'Interval' >= 2
                          Tool_Iteration[tan] = 1                 #Reset tool iteration count. 
                          print_clean = True                      #Interval achieved so we print tool.
                      elif (Tool_Count[tan] == 1):                #If first time tool changes Prime before printing.
                          Tool_Iteration[tan] = 1                 #Reset tool iteration count. 
                          print_clean = True                      #Interval achieved so we print tool.
                      else:
                          if (Tool_Layer == 1): #If Tool Prime Setting is set to iterate on number 'Tool' changes.
                              Tool_Iteration[tan] += 1   
                          print_clean = False   #Still counting so we do not prime yet.

                  else:            #This error should only be raised if a setting is < -1, You never know. 
                     Error_ID = 11 #Set Error ID
                     raise         #We can not trust the setting so we error out.

                  #Always ensure current tool is to Extrusion Temperature
                  logDuet = ("M118 S" + '"' + "Changing to Tool: " + tool_ID + '"' + "\n" )#Add Tool Change ID to Duet Log
                  wait_temp = ("M109 " + tool_ID + " S" + str(active_temperatures[tan]) + "; Set " + tool_ID \
                  + "  new active extruder to " + str(active_temperatures[tan]) \
                  + "c and wait for it to reach temperature before proceeding."  + "\n")

                  outfile.write(logDuet)
                  outfile.write(wait_temp)

                               
                  if (print_clean):
                     #If the current Tool needs to be Primed Then we write tool priming gCode.
                     Prime_Count[tan] += 1 #Keep track of Tool Priming cycles intergrated.
                     print_clean = False    #Reset print_clean to False.
                      
                      #Generated tool priming gCode.
                     logDuet = ("M118 S" + '"' + "Priming Tool: " + tool_ID + '"' + "\n" )#Add Tool Change ID to Duet Log
                     tprime = ("M98 P" + '"' + "tprime" + str(intTool) + ".g" + '"' + "; tool change" + "\n")
                     separator = ("; ------------------------------------------------------------" + "\n")
                     #tool_change_ID = (";Tool Primed " + tool_ID + ": " + str(intTool) + "\n")

                     #Write the gCode priming codes to the .TEMP file.
                     outfile.write(separator)
                     outfile.write(logDuet)
                     #.write(tool_change_ID)
                     outfile.write(tprime)
                     #outfile.write("G1 X-209.00 Y91.00 F30000" + "\n")
                     outfile.write("M83" + "\n")
                     outfile.write("G1 E18 F6000" + "\n")
                     outfile.write("G90" + "\n")
                     outfile.write(separator)

                  #Ensure you switch to Active Tool
                  switch_tool = (tool_ID + "; Switch to tool."  + "\n") #Call the Tool change = "T#"
                  #outfile.write("G1 X-209.00 Y91.00 F30000" + "\n")
                  outfile.write(switch_tool)

                  
                  outfile.write("; Ensure Standby Temperatures are set for each avaliable tool change." + "\n")
                  for i in range(0, 5):
                      if (active_temperatures[i] != -1):#Simplify3D reports tools not used in a process with an Active Temperature or -1, so we do not want to
                                                        #  Provide gCode settings for tools if the are not being used. This makes the script universal for
                                                        #  all of you needs.
                          outfile.write("G10 P" + str(i+1) \
                                        + " S" + str(active_temperatures[i]) \
                                        + " R" + str(standby_temperatures[i]) \
                                        + " ; set active temp to " + str(active_temperatures[i]) \
                                        + "c and standby temp to " + str(standby_temperatures[i]) + "c" + "\n")
            
                  Tool_Change = False
                  print("Tool_Change")
   #</Tool_Change>




   #<Copy Over gCode>
        elif (Line_Copy):
           #Error message if resulting error is not defined for writing gCode line over to .TEMP file.
           Error_ID = 12  #Set Error ID
           outfile.write(readfile_line + "\n")
           Line_Copy = False
   #</Copy Over gCode>  
#</Main Processing Switching> 
                  
#<Finalize Processing>
    #Add Total Post-Processing Time to 'Build Summary' at the end of the gCode file.
    now_time = datetime.datetime.now()
    script_time = (now_time - begin_time)
    time_tag = "hour:minute:second.microsecond"
    outfile.write(";   Script Post-Processing Time: " + str(script_time) + " " + time_tag + "\n")
 
                         
    readfile.close() #Close the original gCode file for reading.
    outfile.close() #Close the temporary gCode file for writing.
#</Finalize Processing>    

#<Include Print_Report>
    Error_ID = 14  #Set Error ID
    #print("End: " + str(Print_Report))
    if(Print_Report):
       os.rename(tempPath, reportPath) #Reanme .TEMP file to .gcode file 
       #print("End: Print_Report")
       if (Tool_Layer): #Create human readable output for Layer/Tool for Script Final Report.
             intergrator_name = "Tool"
       else:
             intergrator_name = "Layer"

       readfile = open(reportPath, 'r')
       n = 0
       outfile = open(tempPath, 'w')

       separator = "_|_"
       spaces = "____________________"
       for readfile_line in readfile:
             if n == Start_gCode:
                outfile.write("M118 S" + '"' + "Tool_|_#Changes_|_Active_c_|_Standby c_|_#Layers_|_#Primes_|_Inergrator_|_Prime Setting" + '"' + "\n")
                for i in range(0, 5):
                   psn = get_PrimeSettingName(Tool_Prime[i])
                   afw = ("M118 S" + '"' + (spaces[:(len("Tool_") - len("T" + str(i+1)))-1]) +  "T" + str(i+1) + separator)
                   afw += (spaces[:(len("_#Changes_") - len(str(Tool_Count[i])))-2]) +  (str(Tool_Count[i]) + separator)
                   afw += (spaces[:(len("_Active c_") - len(str(active_temperatures[i])))-2]) +  (str(active_temperatures[i]) + separator)
                   afw += (spaces[:(len("_Standby c_") - len(str(standby_temperatures[i])))-2]) +  (str(standby_temperatures[i]) + separator)
                   afw += (spaces[:(len("_#Layers_") - len(str(iLayers)))-2]) +  (str(iLayers) + separator)
                   afw += (spaces[:(len("_#Primes_") - len(str(Prime_Count[i])))-2]) +  (str(Prime_Count[i]) + separator)
                   afw += (spaces[:(len("_Inergrator_") - len(str(intergrator_name)))-2]) +  (intergrator_name + separator)   
                   afw += (spaces[:(len("_Prime Setting") - len(str(psn)))-1]) +  (psn + '"' + "\n")
                   outfile.write(afw)
             n += 1
             outfile.write(readfile_line)

       readfile.close()
       outfile.close()

       os.remove(reportPath)
#</Include Print_Report>
       
    os.rename(tempPath, outPath) #Reanme .TEMP file to .gcode file

#<Delete Readfile>
    if (Delete_Original == True):
       os.remove(inPath)                       #  file permission errors.

#</Delete Readfile>
    
    #Script final report printed to teminal

    print("")
    print("Total Lines of gCode Read: " + str(iLines))
    print("")
    print("Script Post-Processing Time: " + str(script_time) + " " + time_tag)
    print("")
    print("Script Process Successful :)")

#<Error Exception>
except:  #for all errors not predefined. Blocks of code sections have Error_ID = 1  #Set Error ID
      exc_type, exc_obj, exc_tb = sys.exc_info()
      #if you recieve an Error_ID of '5', The problem exsist in the code between Error_ID = 5 and Error_ID = 6.
      errorMessage = ("ID: " + str(Error_ID) + " = " + "ERROR Processing Halted:" + fetch_errorMessage(Error_ID)) 
      outfile.write("\n" + "--------------------------------------------------------------------------" + "\n" + "\n")
      outfile.write("The Error can be found between: Error_ID Marker - " + str(Error_ID) + " and " + str(Error_ID + 1) +"\n")
      outfile.write( "System Error Info: " + str(exc_type) + " on Line:" + str(exc_tb.tb_lineno) + "\n")
      outfile.write(errorMessage)
      outfile.write("\n" + "--------------------------------------------------------------------------" + "\n")
      outfile.close()
      readfile.close()
      #Generates all output file paths
      output_paths = process_outputpaths(inPath)
      Process_Error(tempPath, errorPath)
      print("The Error can be found between: Error_ID Marker - " + str(Error_ID) + " and " + str(Error_ID + 1))
      print("System Error Info: " + str(exc_type) + " on Line:" + str(exc_tb.tb_lineno) )
      print(errorMessage)
      sys.exit(errorMessage)
#</Error Exception>

