from init_input import *

############ CORES.
colors = [
      color.rgb(255 / 255.0, 0   / 255.0, 102 / 255.0),
      color.rgb(0   / 255.0, 102 / 255.0, 255 / 255.0),
      color.rgb(51  / 255.0, 204 / 255.0, 0   / 255.0),
      color.rgb(179 / 255.0, 0   / 255.0, 179 / 255.0),
      color.rgb(255 / 255.0, 100 / 255.0, 0   / 255.0), 
      color.rgb(84  / 255.0, 113 / 255.0, 152 / 255.0), 
      color.rgb(228 / 255.0, 0   / 255.0, 0   / 255.0), 
      color.rgb(117 / 255.0, 178 / 255.0, 112 / 255.0), 
      color.rgb(197 / 255.0, 61  / 255.0, 61  / 255.0),
      color.rgb(0   / 255.0, 194 / 255.0, 186 / 255.0), 
      color.rgb(248 / 255.0, 194 / 255.0, 96  / 255.0), 
      color.rgb(0   / 255.0, 135 / 255.0, 68  / 255.0), 
      color.rgb(248 / 255.0, 84  / 255.0, 121 / 255.0),
      color.rgb(0   / 255.0, 140 / 255.0, 166 / 255.0), 
      color.rgb(244 / 255.0, 127 / 255.0, 123 / 255.0), 
      color.rgb(157 / 255.0, 214 / 255.0, 78  / 255.0), 
      color.rgb(150 / 255.0, 95  / 255.0, 173 / 255.0),
      color.rgb(54  / 255.0, 171 / 255.0, 181 / 255.0), 
      color.rgb(254 / 255.0, 94  / 255.0, 81  / 255.0), 
      color.rgb(92  / 255.0, 184 / 255.0, 92  / 255.0), 
      color.rgb(158 / 255.0, 61  / 255.0, 100 / 255.0),
      color.rgb(0   / 255.0, 174 / 255.0, 219 / 255.0), 
      color.rgb(190 / 255.0, 41  / 255.0, 236 / 255.0), 
      color.rgb(0   / 255.0, 177 / 255.0, 89  / 255.0)
   ]

colorsTEX = [
      'magenta',
      'cyan',
      'orange',
      'green',
      'teal',
      'purple',
      'pink',
      'gray',
      'lime'
   ]

#################################### CRIAÇÃO DE ARQUIVOS.
############ PNG.
def create_png(c, filename):
   c.writeEPSfile(filename + '.eps')
   im = Image.open(filename + '.eps')
   fig = im.convert('RGBA')
   fig.save(filename + '.png', lossless = True)
   im.close()
   os.remove(filename + '.eps')

############ TEX.
def create_tex (R, listRaster, listRasterLink, listPoints, parameters):
   folderName = parameters['folder'] + '/TEX'
   fileTEX = folderName + '/' + parameters['filename'] + '_heuristic' + str(parameters['heuristic']) 
   
   if not parameters['heuristic'] == 1:
      fileTEX = fileTEX + '_delta' + str(parameters['delta']) 
   
   fileTEX = fileTEX + '.tex'

   if not os.path.exists(folderName):
      os.makedirs(folderName)

   with open (fileTEX, 'w') as f:
      sys.stdout = f 
      
      text.set(mode = "latex")
      text.preamble(r"\usepackage{times}")

      print (r"\begin{tikzpicture}[scale = 0.1]")

      qEnd = None      
      lEnd = None

      for l in listRaster:
         r = R[l[0]]
         p = r['p'][r['rbit']]
         q = r['p'][1-r['rbit']]
         
         if lEnd != None:
            if lEnd[1] != l[1]:
               print (r'\draw [color=black!60, line width=0.1pt, line cap=dashed] ', qEnd, '--', p, ';')
            else:
               for i in listRasterLink:
                  if (R[lEnd[0]]['sid'] == i[4] and R[l[0]]['sid'] == i[5]) or (R[lEnd[0]]['sid'] == i[5] and R[l[0]]['sid'] == i[4]):
                     x = i[0]
                     y = i[1]
                     print (r'\draw [color=' + colorsTEX[l[1] % len(colorsTEX)] + '!60, line width=0.05pt, line cap=round] ', x, '--', y, ';')
         
         print (r'\draw [color=' + colorsTEX[l[1] % len(colorsTEX)] + '!60, line width=0.05pt, line cap=round] ', p, '--', q, ';')
         
         qEnd = q
         lEnd = l

      p = listPoints[0]
      print(r'\filldraw[black] ', p[0], ' circle (5pt) node;')

      print(r'\end{tikzpicture}')

############ LOG.
def create_log (R, listRaster, parameters, execution_time, tE, tA, Tend, Cmax, smax, groupNumber):
   folderName = parameters['folder'] + '/LOG'
   fileLog = folderName + '/' + parameters['filename'] + '_heuristic' + str(parameters['heuristic']) 

   if not parameters['heuristic'] == 1:
      fileLog = fileLog + '_delta' + str(parameters['delta'])
   
   fileLog = fileLog + '_log.txt'

   if not os.path.exists(folderName):
      os.makedirs(folderName)

   with open(fileLog, 'w') as f:
      sys.stdout = f 
      print ('Execution time: %7.3f ' % execution_time)
      print ('Number of Lines: ', len(R))
      print ('Number of Blocks: ', groupNumber)
      print ('Total extrusion time: %7.3f' % tE)
      print ('Total air-time: %7.3f' % tA)
      print ('Total time: %7.3f' % Tend)
      print ('Maximum cooling time: %7.3f ' % Cmax, end = '')
      print ('from stroke R%03d output [%d] ' % (smax[3]['sid'], smax[4]), end = '')
      print ('to stroke R%03d output [%d]' % (smax[5]['sid'], smax[6]))
      print ('Sequence: ')

      for indexRaster in range(len(listRaster)):
         Rk = R[listRaster[indexRaster][0]]
         print("STROKE R%03d | " % (Rk['sid']), end = '')
         print("RBit: %d | " % (Rk['rbit']), end = '')
         print("Group: %d | " % (listRaster[indexRaster][1]), end = '')
         print("pI: (%6.2f %6.2f) | " % (Rk['p'][Rk['rbit']][0], Rk['p'][Rk['rbit']][1]), end = '')
         print("pE: (%6.2f %6.2f) | " % (Rk['p'][1-Rk['rbit']][0], Rk['p'][1-Rk['rbit']][1]), end = '')
         if Rk['Tini'] != None: print("Tini = %7.3f | " % (Rk['Tini']), end = '')
         if Rk['Tend'] != None: print("Tend = %7.3f | " % (Rk['Tend']), end = '')
         print("e = %6.2f" % Rk['e'])

############ GCODE.
def create_gcode (R, listRaster, listRasterLink, parameters):
   folderName = parameters['folder'] + '/GCODE'
   fileGcode = folderName + '/' + parameters['filename'] + '_heuristic' + str(parameters['heuristic']) 
   
   if not parameters['heuristic'] == 1:
      fileGcode = fileGcode + '_delta' + str(parameters['delta']) 
   
   fileGcode = fileGcode + '.gcode'

   if not os.path.exists(folderName):
      os.makedirs(folderName)

   with open(fileGcode, 'w') as f:
      sys.stdout = f 
      print("G21 ;set units to millimeters")
      print("M107 ;fan off")
      print("G28 ;home all axes")
      print("G90 ;absolute coordinates")
      print("G28 E0 ;home all axes")
      print("G1 E20.0 F100.0 ;extrude 20mm material with 100 mm/min speed")
      print("G28 E0 ;home all axes")
      print("G90 ;use absolute coordinates")
      print("M82 ;use absolute distances for extrusion")
      print("G92 E0 ;home of axes")
      print("M104 S" + str(parameters['temperature']))
      print('\nG1  X0.0 Y0.0 F2400') ### FORÇA QUE O BICO ESTEJA NO PONTO (0,0,0) DA PLATAFORMA NO INÍCIO.
      print("G1 E-2.00000 F24000 ;retracts 2mm of material")

      indexSlice = parameters['filename'].split("_")
      print("\n(Layer " + str(indexSlice[-1])  + ")")
      thickness = parameters['thickness'] * (int(indexSlice[-1]) + 1)
      print("G1  Z" + str(thickness) + " F" + str(int(parameters['travel_speed']*60)))

      print_filling(R, listRaster, listRasterLink, parameters)

      print("G92 E0")
      print("M107 ;fan off")
      print("M104 S0 ;turn off temperature")
      print("G28 X Y")

def print_filling (R, listRaster, listRasterLink, parameters):
   filling_speed = int(parameters['filling_speed'] * 60)
   travel_speed = int(parameters['travel_speed'] * 60)

   eNumber = 0
   group = 0

   lEnd = None
   qEnd = None

   for l in listRaster:
      r = R[l[0]]
      p = r['p'][r['rbit']]
      q = r['p'][1-r['rbit']]

      if (lEnd == None) or (lEnd[1] != l[1]):
         print("\n(Raster " + str(group)  + ")")
         print("G92 E0")

         print("G1  X%.6f Y%.6f F%d" % (p[0], p[1], travel_speed))
         
         print("G1  E2.00000 F2400")
         eNumber = 2
         eNumber = eNumber + calcule_e(p, q, parameters)
         print("G1  X%.6f Y%.6f E%.3f F%d" % (q[0], q[1], eNumber, filling_speed))

         group = group + 1

      else:
         rasterLink = list()
         for i in listRasterLink:
            if (R[lEnd[0]]['sid'] == i[4] and R[l[0]]['sid'] == i[5]) or (R[lEnd[0]]['sid'] == i[5] and R[l[0]]['sid'] == i[4]):
               rasterLink.append(i[0])
               rasterLink.append(i[1])
         
         rasterLink = list(dict.fromkeys(rasterLink))

         if rasterLink[0] == qEnd:
            indexBegin = 0
            indexEnd = len(rasterLink)
            i = 1
         else:
            indexBegin = len(rasterLink) - 1
            indexEnd = 0
            i = -1

         xPrev = qEnd

         for n in range(indexBegin, indexEnd, i):
            x = rasterLink[n]
            if not (x == qEnd) and not (x == p):
               eNumber = eNumber + calcule_e(xPrev, x, parameters)
               print("G1  X%.6f Y%.6f E%.3f F%d" % (x[0], x[1], eNumber, filling_speed))

         eNumber = eNumber + calcule_e(qEnd, p, parameters)
         print("G1  X%.6f Y%.6f E%.3f F%d" % (p[0], p[1], eNumber, filling_speed))

         eNumber = eNumber + calcule_e(p, q, parameters)
         print("G1  X%.6f Y%.6f E%.3f F%d" % (q[0], q[1], eNumber, filling_speed))
      
      qEnd = q
      lEnd = l

   print("G1  E%.6f F2400" % (eNumber-2))

def calcule_e (p, q, parameters):
   e = calculate_distance(p, q)

   volume = e * ((parameters['width'] - parameters['thickness']) * parameters['thickness'] + m.pi * m.pow(parameters['thickness'] / 2,2))
   filament = volume/(m.pi*m.pow(parameters['filamentDiamenter']/2,2))

   return 1.27 * filament

############ CSV.
def create_csv (summary, filename):
    COLUMNS_NAMES = [
        'NOME',
        'NUMERO DE BLOCOS',
        'TEMPO DE EXTRUSAO',
        'TEMPO DE REPOSICIONAMENTO',
        'TEMPO TOTAL',
        'MAIOR RESFRIAMENTO',
        'TEMPO EXECUCAO',
        'CHECK DELTA'
    ]

    df = pd.DataFrame(summary, columns = COLUMNS_NAMES)
    df.to_csv (filename + '.csv', index = False, header = True, sep = ';')

#################################### FUNÇÕES AUXILIARES.
def check_link(R1, R2):
   '''
      Retorna o raster link que liga as duas linhas de raster (considerando o sentido que elas seguem).
      (Caso não exista um raster link para realizar a ligação, retorna nulo.)
   '''
   # Verifica se as duas linhas de raster estão em sentidos opostos.
   if R1['rbit'] == R2['rbit']:
      return None

   for edge in range(2):
      for side in R1['links'][edge]:   
         if (side[0][3]['sid'] == R1['sid'] and side[0][5]['sid'] == R2['sid']) or (side[0][3]['sid'] == R2['sid'] and side[0][5]['sid'] == R1['sid']):
            if side[2 + R2['rbit']] != None:
               return side[2 + R2['rbit']][1]
   return None

def create_list_result(R, parameters):
   '''
      Cria as listas com informações usadas para criar os arquivos de resultados.
   '''
   rEnd = None

   if parameters['heuristic'] == 1:
      groupEnd = -1
   else:
      groupEnd = 0

   listRaster = list() 
   listAirJump = list()
   listRasterLink = list()
   listPoints = list()

   for rasterIndex in range(len(R)):
      r = R[rasterIndex]

      if parameters['heuristic'] == 1 or (parameters['heuristic'] == 2 and not parameters['rasterLink']):
         groupEnd = groupEnd + 1
         listRaster.append((rasterIndex, groupEnd))
         
         if rEnd == None: 
            '''
               Apenas adiciona na lista de pontos iniciais o ponto da primeira 
               linha de raster da solução encontrada, indicando seu início.
            '''
            listPoints.append((r['p'][r['rbit']], 0.2, color.rgb.black))
         
         else:
            p = rEnd['p'][1 - rEnd['rbit']]
            q = r['p'][r['rbit']]
            listAirJump.append((p, q, groupEnd))

      else:
         if rEnd != None:
            p = rEnd['p'][1 - rEnd['rbit']]
            q = r['p'][r['rbit']]

            # Verifica se existe um raster link ligando as duas linhas de raster analisadas.
            rasterLink = check_link(rEnd, r) 
            
            if rasterLink != None:
               for i in range(len(rasterLink) - 1):
                  p = rasterLink[i]
                  q = rasterLink[i + 1]
                  listRasterLink.append((p, q, colors[groupEnd % len(colors)], groupEnd, rEnd['sid'], r['sid']))

            else:
               groupEnd = groupEnd + 1
               listPoints.append((q, 0.2, colors[(groupEnd-1) % len(colors)], groupEnd, r['rbit']))
               listAirJump.append((p, q, groupEnd))

            listRaster.append((rasterIndex, groupEnd))

         else:
            listPoints.append((r['p'][r['rbit']], 0.2, color.rgb.black, groupEnd, r['rbit']))
            listRaster.append((rasterIndex, groupEnd))

      rEnd = r

   return groupEnd + 1, listRaster, listAirJump, listRasterLink, listPoints

#################################### PLOTS.
def plot_slice(R, listRaster, listAirJump, listRasterLink, listPoints, parameters):
   c = canvas.canvas()
   
   style.linewidth(0.2 * unit.w_cm)
   line_width = 0.2 * parameters['width'] * unit.w_cm

   plot_raster_lines(c, listRaster, R, line_width)
   plot_links(c, listRasterLink, line_width)
   
   if parameters['sides']:
      for r in R:
         links = r['links'][0]
         plot_sides(c, links, line_width)
         links = r['links'][1]
         plot_sides(c, links, line_width)

   if parameters['airJump']:
      plot_air(c, listAirJump, line_width)

   if parameters['beginPoint']:
      plot_points(c, listPoints)

   if parameters['printBlock'] and not parameters['heuristic'] == 1 and not (parameters['heuristic'] == 2 and not parameters['rasterLink']):
      plot_blocks(c, listPoints, R)

   elif parameters['printRaster'] and not parameters['printBlock']:
      plot_raster_id(c, listRaster, R)
   
   fileImage = parameters['folder'] + '/PNG/' + parameters['filename'] + '_heuristic' + str(parameters['heuristic'])

   if not parameters['heuristic'] == 1:
      fileImage = fileImage + '_delta' + str(parameters['delta'])

   create_png (c, fileImage)

def plot_raster_lines(c, listRaster, R, line_width):
   for l in listRaster:
      r = R[l[0]]
      p = r['p'][r['rbit']]
      q = r['p'][1-r['rbit']]
      c.stroke(path.line(p[0], p[1], q[0], q[1]), [style.linewidth(2 * line_width), style.linecap.round, colors[l[1] % len(colors)]])
   
def plot_links(c, listRasterLink, line_width):
   for r in listRasterLink:
      p = r[0]
      q = r[1]
      c.stroke(path.line(p[0], p[1], q[0], q[1]), [style.linewidth(2 * line_width), style.linecap.round, r[2]])

def plot_air(c, listAirJump, line_width):
   for j in listAirJump:
      p = j[0]
      q = j[1]
      c.stroke(path.line(p[0], p[1], q[0], q[1]), [style.linestyle.dashed, color.rgb.black])

def plot_sides(c, links, line_width):
      for s in links:
         side = s[0]
         xm = side[0]
         ym = side[1]
         xl = side[2]
         x0 = xm - xl/2
         x1 = xm + xl/2
         c.stroke(path.line(x0, ym, x1, ym), [style.linewidth(0.1 * unit.w_cm), color.rgb.black])

def plot_points(c, listPoints):   
   for p in listPoints:
      c.fill(path.circle(p[0][0], p[0][1], p[1]), [p[2]])

def plot_blocks(c, listPoints, R):
   for p in listPoints:
      x = p[0][0]
      y = p[0][1]
      
      groupLabel = p[3]
      
      rbit = p[4]
      
      if rbit == 0:
         x = x - 2.4
      else:
         x = x - 0.5

      c.text(x, y, str(groupLabel), [text.parbox(1.2), text.valign.middle, trafo.scale(2), colors[groupLabel % len(colors)]])

def plot_raster_id(c, listRaster, R):
   for index in range(len(listRaster)):
      r = R[listRaster[index][0]]
      sid = r['sid']
      group = listRaster[index][1]
      point = r['p'][r['rbit']]
      x = point[0]
      y = point[1]

      if r['p'][r['rbit']][0] < r['p'][1 - r['rbit']][0]:
         x = x - 2.4
      else:
         x = x - 0.5

      c.text(x, y, str(sid), [text.parbox(1.2), text.valign.middle, trafo.scale(2), colors[group % len(colors)]])

def plot_debug(R, listRaster, listAirJump, listRasterLink, listPoints, groupEnd, parameters):
   if not os.path.exists(parameters['folder'] + '/DEBUG'):
      os.makedirs(parameters['folder'] + '/DEBUG')

   if not os.path.exists(parameters['folder'] + '/DEBUG/DEBUG - ' + parameters['filename']):
      os.makedirs(parameters['folder'] + '/DEBUG/DEBUG - ' + parameters['filename'])
   
   fileOutput = parameters['folder'] + '/DEBUG/DEBUG - ' + parameters['filename'] + '/' + parameters['filename']+ '_heuristic' + str(parameters['heuristic']) + '_delta' + str(parameters['delta'])

   c = canvas.canvas()
   style.linewidth(0.2 * unit.w_cm)
   line_width = 0.2 * parameters['width'] * unit.w_cm

   for group in range(0, groupEnd+1):
      for l in listRaster:
         if l[1] == group:
            r = R[l[0]]
            p = r['p'][r['rbit']]
            q = r['p'][1-r['rbit']]
            c.stroke(path.line(p[0], p[1], q[0], q[1]), [style.linewidth(2 * line_width), style.linecap.round, colors[l[1] % len(colors)]])

      if parameters['rasterLink']:
         for r in listRasterLink:
            if r[3] == group:
               p = r[0]
               q = r[1]
               c.stroke(path.line(p[0], p[1], q[0], q[1]), [style.linewidth(0.5 * line_width), style.linecap.round, r[2]])

      if parameters['airJump']:
         for j in listAirJump:
            if j[2] == group:
               p = j[0]
               q = j[1]
               c.stroke(path.line(p[0], p[1], q[0], q[1]), [style.linestyle.dashed, color.rgb.black])
      
      if parameters['beginPoint']:
         for p in listPoints:
            if p[2] == group:
               c.fill(path.circle(p[0][0], p[0][1], p[1]), [color.rgb.black])

      create_png (c, fileOutput + str(group))
      
#################################### PRINTS.
def print_strokes (R):
    for Rk in R:
        print_stroke (Rk, 0, None, 0)
        sys.stderr.write("\n")

def print_stroke (Rk, rbitk, label, ind):
   '''
     Prints the stroke {Rk} inverted if {rbitk} = 1. If the stroke
     already has 'rbit' equal to 1, they cancel each other. {ind}
     is the number of blank to indent.
   '''
   indx = ' ' * ind
   rbit_both = (Rk['rbit'] + rbitk) % 2
   sys.stderr.write("%s Stroke R%03d:%d " % (indx, Rk['sid'],rbit_both))
   if label != None:
       sys.stderr.write(" [%s] " % label)
   sys.stderr.write(" (%6.2f %6.2f) " % (Rk['p'][rbit_both][0], Rk['p'][rbit_both][1]))
   sys.stderr.write(" (%6.2f %6.2f) " % (Rk['p'][1-rbit_both][0], Rk['p'][1-rbit_both][1]))
   if Rk['Tini'] != None: sys.stderr.write(" t = %7.3f " % (Rk['Tini']))
   if Rk['Tend'] != None: sys.stderr.write(" t = %7.3f " % (Rk['Tend']))
   sys.stderr.write(" e = %6.2f " % Rk['e'])
   sys.stderr.write(" w = %4.2f\n" % Rk['w'])
   sys.stderr.write("%s  Inputs: \n" % indx)
   print_links (0, Rk, rbitk, ind + 2)
   sys.stderr.write("%s  Outputs: \n" % indx)
   print_links (1, Rk, rbitk, ind + 2)

def print_cands (Cands, ind):
    indx = ' ' * ind
    for k in range(len(Cands)):
        Ck = Cands[k]
        print_stroke (Ck[0], Ck[1], ('C%03d' % k), ind)
        sys.stderr.write("%s  Score: %.6f \n" % (indx, Ck[2]))
        sys.stderr.write("\n")

def print_links (edge, Rk, rbitk, ind):
    indx = ' ' * ind
    rbit_both = (Rk['rbit'] + rbitk) % 2
    L = Rk['links'][edge]
    for i in range(len(L)):
        linki = L[i]
        sys.stderr.write("%s  %3d" % (indx, i))
        side = linki[0]
        sys.stderr.write(" (%6.2f %6.2f) " % (side[0], side[1]))
        sys.stderr.write(" len = %6.2f" % (side[2]))
        if edge == 0:
            Rl = side[3]
            jl = side[4]
            sys.stderr.write("  R%03d out [%d] " % (Rl['sid'],jl))
            assert (side[5] == Rk)
            assert (side[6] == i)
        else:     
            Rl = side[5]
            jl = side[6]
            sys.stderr.write("  R%03d inp [%d] " % (Rl['sid'],jl))
            assert (side[3] == Rk)
            assert (side[4] == i)
        if rbit_both == 0:   
           trel = linki[1]
        else:   
           trel = Rk['e'] - linki[1]  
        sys.stderr.write("  trel = %7.3f\n" % (trel))

#################################### PLOT INPUT.
def plot_input(R, parameters):
   if not os.path.exists(parameters['folder'] + '/INPUT - IMAGE'):
      os.makedirs(parameters['folder'] + '/INPUT - IMAGE')

   filename = parameters['folder'] + '/INPUT - IMAGE' + '/' + parameters['filename'] + '_heuristic' + str(parameters['heuristic']) 

   c = canvas.canvas()
   style.linewidth(0.1 * unit.w_cm)
   line_width = 0.08 * unit.w_cm

   for r in R:
      p = r['p'][r['rbit']]
      q = r['p'][1-r['rbit']]
      
      c.stroke(path.line(p[0], p[1], q[0], q[1]), [style.linewidth(2 * line_width), style.linecap.round, colors[r['group'] % len(colors)]])
      
      if parameters['heuristic'] == 3:
         if r['first']:
            c.fill(path.circle(p[0], p[1], 0.2), [color.rgb.black])
         if r['last']:
            c.fill(path.circle(q[0], q[1], 0.1), [color.rgb.black])
      else:
         c.fill(path.circle(p[0], p[1], 0.1), [color.rgb.black])

      for edge in range(2):
         for i in r['links'][edge]:
               if i[2] != None:
                  pPrev = None
                  for p in i[2][1]:
                     if pPrev != None:
                           c.stroke(path.line(pPrev[0], pPrev[1], p[0], p[1]), [style.linewidth(2 * line_width), style.linecap.round, colors[r['group'] % len(colors)]])
                     pPrev = p
               
               if i[3] != None:
                  pPrev = None
                  for p in i[3][1]:
                     if pPrev != None:
                           c.stroke(path.line(pPrev[0], pPrev[1], p[0], p[1]), [style.linewidth(2 * line_width), style.linecap.round, colors[r['group'] % len(colors)]])
                     pPrev = p
   
   create_png(c, filename)