-- Voron V0 Profile
-- Hugron Pierre-Alexandre 29/06/2021
-- Updated by Bedell Pierre 05/10/2022

extruder_e = 0
extruder_e_restart = 0

current_extruder = 0
current_frate = 0

current_fan_speed = -1

processing = false

path_type = 2 -- 1:default, 2:Craftware, 3:Prusa/Super Slicer, 4:Cura

path_tag = {
  --{ 'default',  'Craftware',              'Prusa/Super Slicer',       'Cura'            }
  { ';travel',    ';segType:Travel',        '',                         ''                },
  { ';perimeter', ';segType:Perimeter',     ';TYPE:External perimeter', ';TYPE:WALL-OUTER'},
  { ';shell',     ';segType:HShell',        ';TYPE:Internal perimeter', ';TYPE:WALL-INNER'},
  { ';cover',     ';segType:Infill',        ';TYPE:Solid infill',       ';TYPE:FILL'      },
  { ';infill',    ';segType:Infill',        ';TYPE:Internal infill',    ';TYPE:FILL'      },
  { ';gapfill',   ';segType:Infill',        ';TYPE:Gap fill',           ';TYPE:FILL'      },
  { ';bridge',    ';segType:SupportTouch',  ';TYPE:Overhang perimeter', ';TYPE:WALL-OUTER'},
  { ';support',   ';segType:Support',       ';TYPE:Support material',   ';TYPE:SUPPORT'   },
  { ';brim',      ';segType:Skirt',         ';TYPE:Skirt',              ';TYPE:SKIRT'     },
  { ';raft',      ';segType:Raft',          ';TYPE:Skirt',              ';TYPE:SKIRT'     },
  { ';shield',    ';segType:Pillar',        ';TYPE:Skirt',              ';TYPE:SKIRT'     },
  { ';tower',     ';segType:Pillar',        ';TYPE:Skirt',              ';TYPE:SKIRT'     },
}

--##################################################

function comment(text)
  output('; ' .. text)
end

function round(number, decimals)
  local power = 10^decimals
  return math.floor(number * power) / power
end

function vol_to_mass(volume, density)
  return density * volume
end

function e_to_mm_cube(filament_diameter, e)
  local r = filament_diameter / 2
  return (math.pi * r^2 ) * e
end

-- get the E value (for G1 move) from a specified deposition move
function e_from_dep(dep_length, dep_width, dep_height, extruder)
  local r1 = dep_width / 2
  local r2 = filament_diameter_mm[extruder] / 2
  local extruded_vol = dep_length * math.pi * r1 * dep_height
  return extruded_vol / (math.pi * r2^2)
end

function tag_path()
  if     path_is_travel          then output(path_tag[1][path_type])
  elseif path_is_perimeter       then output(path_tag[2][path_type])
  elseif path_is_outer_perimeter then output(path_tag[2][path_type])
  elseif path_is_shell           then output(path_tag[3][path_type])
  elseif path_is_cover           then output(path_tag[4][path_type])
  elseif path_is_infill          then output(path_tag[5][path_type])
  elseif path_is_gapfill         then output(path_tag[6][path_type])
  elseif path_is_bridge          then output(path_tag[7][path_type])
  elseif path_is_support         then output(path_tag[8][path_type])
  elseif path_is_brim            then output(path_tag[9][path_type])
  elseif path_is_raft            then output(path_tag[10][path_type])
  elseif path_is_shield          then output(path_tag[11][path_type])
  elseif path_is_tower           then output(path_tag[12][path_type])
  end
end

function set_accel()
  if layer_id < 1 then -- fisrt layer specific acceleration
    output('M204 S' .. first_layer_acc)
  else
    if      path_is_travel    then output('M204 S' .. default_acc)
    elseif  path_is_perimeter then output('M204 S' .. perimeter_acc)
    elseif  path_is_shell     then output('M204 S' .. perimeter_acc)
    elseif  path_is_infill    then output('M204 S' .. infill_acc)
    elseif  path_is_raft      then output('M204 S' .. default_acc)
    elseif  path_is_brim      then output('M204 S' .. default_acc)
    elseif  path_is_shield    then output('M204 S' .. default_acc)
    elseif  path_is_support   then output('M204 S' .. default_acc)
    elseif  path_is_tower     then output('M204 S' .. default_acc)
    else output('M204 S' .. default_acc)
    end
  end
end

--##################################################

function header()
  if use_klipper_start_stop_macros then
    output("; start print using Klipper's macro")
    output(macro_start .. '=' .. extruder_temp_degree_c[extruders[0]] .. ' BED=' .. bed_temp_degree_c )
  else
    h = file('header.gcode')
    h = h:gsub( '<TOOLTEMP>', extruder_temp_degree_c[extruders[0]] )
    h = h:gsub( '<HBPTEMP>', bed_temp_degree_c )
    output(h)
  end

  -- additionnal informations for Klipper web API (Moonraker)
  -- if feedback from Moonraker is implemented in the choosen web UI (Mainsail, Fluidd, Octoprint), this info will be used for gcode previewing
  output("")
  output("; Additionnal informations for Mooraker API")
  output("; Generated by <" .. slicer_name .. " " .. slicer_version .. ">")
  output("; print_height_mm :\t" .. f(extent_z))
  output("; layer_count :\t" .. f(extent_z/z_layer_height_mm))
  output("; filament_type : \t" .. name_en)
  output("; filament_name : \t" .. name_en)
  output("; filament_used_mm : \t" .. f(filament_tot_length_mm[0]) )
  -- caution! density is in g/cm3, convertion to g/mm3 needed!
  output("; filament_used_g : \t" .. f(vol_to_mass(e_to_mm_cube(filament_diameter_mm[0], filament_tot_length_mm[0]), filament_density/1000)) )
  output("; estimated_print_time_s : \t" .. time_sec)
  output("")
end

function footer()
  if use_klipper_start_stop_macros then
    output("")
    output("; end print using Klipper's macro")
    output(macro_stop)
  else
    output(file('footer.gcode'))
  end
end

function layer_start(zheight)
  comment('<layer ' .. layer_id .. ' >')
  if not layer_spiralized then
    output('G1 Z' .. f(zheight))
  end
end

function layer_stop()
  extruder_e_restart = extruder_e
  output('G92 E0')
  comment('</layer ' .. layer_id .. ' >')
end

function retract(extruder,e)
  local len   = filament_priming_mm[extruder]
  local speed = retract_mm_per_sec[extruder] * 60
  output('G1 F' .. speed .. ' E' .. ff(e - len - extruder_e_restart))
  extruder_e = e - len
  return e - len
end

function prime(extruder,e)
  local len   = filament_priming_mm[extruder]
  local speed = priming_mm_per_sec[extruder] * 60
  output('G1 F' .. speed .. ' E' .. ff(e + len - extruder_e_restart))
  extruder_e = e + len
  return e + len
end

-- this is called once for each used extruder at startup
-- it is used here to generate purge with proper nozzle diameter
function select_extruder(extruder)
  if not use_klipper_start_stop_macros then
    local n = nozzle_diameter_mm_0

    -- purge position
    local x_pos = 10.0
    local y_pos = 1.0
    local z_pos = 0.3 -- used as deposition height

    local l1 = 40 -- length of the purge start
    local l2 = 40 -- length of the purge end

    local w1 = n * 1.5 -- width of the purge start
    local w2 = n * 3.0 -- width of the purge end

    local speed = 25 -- mm/s

    local e_value = 0.0

    output('\n; purging extruder')
    output('G0 F6000 X' .. x_pos .. ' Y' .. y_pos ..' Z' .. z_pos)
    output('G92 E0')

    x_pos = x_pos + l1
    e_value = round(e_from_dep(l1, w1, z_pos, extruder),2)
    output('G1 F' .. speed * 60 .. ' X' .. x_pos .. ' E' .. e_value .. '   ; purge line start') -- purge start

    x_pos = x_pos + l2
    e_value = e_value + round(e_from_dep(l2, w2, z_pos, extruder),2)
    output('G1 F' .. speed * 60 .. ' X' .. x_pos .. ' E' .. e_value .. '  ; purge line end') -- purge end
    output('G92 E0')
    output('; done purging extruder\n')

    current_extruder = extruder
    current_frate = travel_speed_mm_per_sec * 60
    changed_frate = true
  end
end

function swap_extruder(from,to,x,y,z)
end

function move_xyz(x,y,z)
  if processing == true then 
    tag_path() 
    processing = false

    -- acceleration management
    if use_per_path_accel then
      set_accel()
    end
  end
  output('G0 X' .. f(x) .. ' Y' .. f(y) .. ' Z' .. ff(z))
end

function move_xyze(x,y,z,e)
  if processing == false then 
    tag_path() 
    processing = true

    -- acceleration management
    if use_per_path_accel then
      set_accel()
    end
  end

  local e_value = e - extruder_e_restart
  extruder_e = e
  output('G1 X' .. f(x) .. ' Y' .. f(y) .. ' Z' .. ff(z) .. ' F' .. current_frate .. ' E' .. ff(e_value))
end

function move_e(e)
  local e_value = e - extruder_e_restart
  extruder_e = e
  output('G1 E' .. ff(e_value))
end

function set_feedrate(feedrate)
  output('G1 F' .. feedrate)
  current_frate = feedrate
end

function extruder_start()
end

function extruder_stop()
end

function progress(percent)
end

function set_extruder_temperature(extruder,temperature)
  output('M104 S' .. temperature .. ' T' .. extruder)
end

function set_and_wait_extruder_temperature(extruder,temperature)
  output('M109 S' .. temperature .. ' T' .. extruder)
end

function set_fan_speed(speed)
  if speed ~= current_fan_speed then
    output('M106 S'.. math.floor(255 * speed/100))
    current_fan_speed = speed
  end
end

-- The contents of this function is a placeholder
-- you can replace it by what you see fit to exented "layer time"
function wait(sec,x,y,z)
  local pos_x = 10 -- "parking" coordinates
  local pos_y = 10

  output("\n; Waiting for minimum layer time -- " .. f(sec) .. "s remaining")
  output("G0 F" .. travel_speed_mm_per_sec * 60 .. " X" .. pos_x .. " Y" .. pos_y .. " ; go to the parking position")
  -- G4 uses milliseconds on Klipper !
  output("G4 P" .. f(sec) * 1000 .. " ; wait for " .. f(sec) .. "s")
  output("G0 F" .. travel_speed_mm_per_sec * 60 .. " X" .. f(x) .." Y" .. f(y) .. " Z" .. ff(z) .. "; going back to  the previous location\n")
end
