--[[
  Copyright (c) 2024 by Plexim GmbH
  All rights reserved.

  A free license is granted to anyone to use this software for any legal
  non safety-critical purpose, including commercial applications, provided
  that:
  1) IT IS NOT USED TO DIRECTLY OR INDIRECTLY COMPETE WITH PLEXIM, and
  2) THIS COPYRIGHT NOTICE IS PRESERVED in its entirety.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
--]]

local iL = {}
local R = 10                -- Radius for block and badge corners
local defaultCharWidth = 4  -- used for characters not defined in the alphabet below
local charSize = 7          -- font size

local alphabet = {
  A = 4,
  B = 4,
  C = 4,
  D = 4,
  E = 4,
  F = 4,
  G = 4,
  H = 4,
  I = 3.5,
  J = 4,
  K = 4,
  L = 4,
  M = 6,
  N = 4,
  O = 4,
  P = 4,
  Q = 4,
  R = 4,
  S = 4,
  T = 4,
  U = 4,
  V = 4,
  W = 6,
  X = 4,
  Y = 4,
  Z = 4,
  a = 3.5,
  b = 3.5,
  c = 3.5,
  d = 4,
  e = 3.5,
  f = 3.5,
  g = 4,
  h = 4,
  i = 2.5,
  j = 3.5,
  k = 4,
  l = 3.5,
  m = 5.5,
  n = 3.5,
  o = 3.5,
  p = 4,
  q = 4,
  r = 2.5,
  s = 3.5,
  t = 3.5,
  u = 3.5,
  v = 3.5,
  w = 5,
  x = 3.5,
  y = 4,
  z = 3.5
}

local color = {
  white = {255, 255, 255},
  black = {0, 0, 0},
  exp_grey_bg = {117, 117, 117},
  microchip_red = {212, 53, 54},
  ti_red = {187, 39, 26},  -- ti.com banner
  xmc_blue = {49, 99, 216},
}

local EXP_TSP = {
  -- add entries here to map experimental components to the corresponding settings
  dsPICX = 'dsPIC',
  SAMX = 'SAM',
  STX = 'ST',
  TIX = 'TI',
  XMCX = 'XMC',
}

local TSP = {
  -- define colors as {light = {R, G, B}} if it should remain the same for light and darkmode. Otherwise define {light = {R, G, B}, dark = {R, G, B}}
  default = {
    patchSize = 3,          -- horizontal padding
    color_bg = {light = color.white, dark = color.black},
    color_txt = 'normalFg'  -- uses the PLECS defaults
  },
  dsPIC = {
    patchSize = 3,
    color_bg = {light = color.microchip_red},
    color_txt = {light = color.white},
  },
  SAM = {
    patchSize = 4,
    color_bg = {light = color.microchip_red},
    color_txt = {light = color.white},
  },
  ST = {
    patchSize = 3,
    color_bg = {light = {4, 34, 73}, dark = {55, 110, 155}},
    color_txt = {light = color.white},
  },
  TI = {
    patchSize = 3,
    color_bg = {light = color.ti_red},
    color_txt = {light = color.white},
  },
  XMC = {
    patchSize = 4,
    color_bg = {light = color.xmc_blue},
    color_txt = {light = color.white}
  },
}

local function _fetchTspSettings(tsp_name)
  local isExperimental = false
  local tsp_display_name

  -- check if block is experimental
  if EXP_TSP[tsp_name] then
    isExperimental = true
    tsp_display_name = EXP_TSP[tsp_name]  -- remap display name (remove the 'X')
  else
    tsp_display_name = tsp_name
  end

  local settings = TSP[tsp_display_name]

  if settings == nil then  -- target settings not found
    settings = TSP['default']
  end

  if isExperimental then
    -- handle special case for experimental blocks (grey background)
    settings.color_bg = color.exp_grey_bg
    settings.color_txt = color.white
  end

  settings.display_name = tsp_display_name

  return settings
end

-- set the text for a block
-- optionally provide x and y shifts or use default shift for multiple lines.
function iL.setBlockName(block_name, x, y)
  local x_shift = 0  -- center text by default
  -- vertically shift text that has multiple lines to give space for the tag
  local y_shift = string.find(block_name, '\n') and 3 or 0

  if type(x) == 'number' then
    x_shift = x
  end

  if type(y) == 'number' then
    y_shift = y + y_shift -- add the default here for more logical UI
  end
  local direction = Block:get('Direction')
  if direction == 0.25 or direction == 0.75 then
    Icon:text(x_shift, y_shift, block_name)
  else
    Icon:text(y_shift, x_shift, block_name)
  end
end

-- helper function for iL.drawDecoration()
local function _drawEmptyPatch(x1, y1, x2, y2, pos)
  local ptx = {}
  local pty = {}
  local radius = R - 0.1
  if pos then
    for i = 0, 90 do
      local angle = i * math.pi / 180
      table.insert(ptx, x1 + radius * math.cos(angle))
      table.insert(pty, y1 + radius * math.sin(angle))
    end
    for i = 180, 270 do
      local angle = i * math.pi / 180
      table.insert(ptx, x2 + radius * math.cos(angle))
      table.insert(pty, y2 + radius * math.sin(angle))
    end
  else
    for i = 90, 180 do
      local angle = i * math.pi / 180
      table.insert(ptx, x1 + radius * math.cos(angle))
      table.insert(pty, y1 + radius * math.sin(angle))
    end
    for i = 270, 360 do
      local angle = i * math.pi / 180
      table.insert(ptx, x2 + radius * math.cos(angle))
      table.insert(pty, y2 + radius * math.sin(angle))
    end
  end

  Icon:patch(ptx, pty)
end

-- helper function for iL.drawDecoration(), draws patch borders
local function _drawPatchBorders(x1, y1, x2, y2, patchSize, pos)
  local dir = nil
  if pos then
    dir = -90
  else
    dir = 90
  end

  Icon:arc(x1, y1, R, R, 180, dir)
  Icon:arc(x2, y2, R, R, 0, dir)
  Icon:line({x1 - R + 0.5, x1 - R + patchSize - 0.5}, {y1, y1})
  Icon:line({x2 + R - patchSize + 0.5, x2 + R - 0.5}, {y2, y2})
end

--[[
  Draw a patch to highlight TSP -blocks from regular PLECS blocks and/or other TSP -blocks.
  The TSP's supported by PLECS are pre -defined (color and text) and cannot be modified from the mask.
--]]
function iL.drawPatch(raw_tsp_name)
  local tar = _fetchTspSettings(raw_tsp_name)

  local tsp_name = tar.display_name
  local color_bg = tar.color_bg
  local color_txt = tar.color_txt

  Icon:color(color_bg)

  local flipped = Block:get('Flipped')
  local direction = Block:get('Direction')
  local frame = Block:get('Frame')
  local x_start = frame[1]
  local y_start = frame[2]
  local x_total = frame[3]
  local y_total = frame[4]

  local l = string.len(tsp_name)
  for i = 1, l do
    local thisCharWidth = alphabet[string.sub(tsp_name, i, i)]
    if thisCharWidth == nil then
      tar.patchSize = tar.patchSize + defaultCharWidth
    else
      tar.patchSize = tar.patchSize + thisCharWidth
    end
  end
  local delta = (tar.patchSize + R) / 2

  local x, y, g, position
  local xT = nil
  local yT = nil

  if direction == 0.0 then
    x = -x_start + y_start
    y = x_start - y_start
    g = -1
    if flipped then
      xT = x_start + R / 2
      yT = y_start + delta
      position = 'upLeft'
    else
      xT = x_start + R / 2
      yT = -y_start - delta
      position = 'upRight'
    end
  end

  if direction == 0.25 then
    x = 0
    y = 0
    g = 1
    if flipped then
      xT = -x_start - delta
      yT = y_start + R / 2
      position = 'upRight'
    else
      xT = -x_start - delta
      yT = -y_start - R / 2
      position = 'lowRight'
    end
  end

  if direction == 0.5 then
    x = -x_start + y_start
    y = x_start - y_start
    g = -1
    if flipped then
      xT = -x_start - R / 2
      yT = -y_start - delta
      position = 'lowRight'
    else
      xT = -x_start - R / 2
      yT = y_start + delta
      position = 'lowLeft'
    end
  end

  if direction == 0.75 then
    x = 0
    y = 0
    g = 1
    if flipped then
      xT = x_start + delta
      yT = -y_start - R / 2
      position = 'lowLeft'
    else
      xT = x_start + delta
      yT = y_start + R / 2
      position = 'upLeft'
    end
  end

  if position == 'upLeft' then
    _drawEmptyPatch((x_start + tar.patchSize + x), (y_start + y), (x_start + R + x), (y_start + R + y), true)
    Icon:color('normalFg')
    _drawPatchBorders((x_start + R + x), (y_start + R + y),
                      (x_start + tar.patchSize + x), (y_start + y), tar.patchSize, true)
    Icon:text(xT, yT, [[<b><i>]]..tsp_name, 'FontSize', charSize, 'TextFormat', 'RichText', 'Color', color_txt)
  elseif position == 'upRight' then
    _drawEmptyPatch((-x_start - tar.patchSize - x), (y_start + y), (-x_start - R - x), (y_start + R + y), false)
    Icon:color('normalFg')
    _drawPatchBorders((-x_start - tar.patchSize - x), (y_start + y),
                      (-x_start - R - x), (y_start + R + y), tar.patchSize, false)
    Icon:text(g * -xT, g * yT, [[<b><i>]]..tsp_name, 'FontSize', charSize, 'TextFormat', 'RichText', 'Color', color_txt)
  elseif position == 'lowRight' then
    _drawEmptyPatch((-x_start - R - x), (-y_start - R - y), (-x_start - tar.patchSize - x), (-y_start - y), true)
    Icon:color('normalFg')
    _drawPatchBorders((-x_start - tar.patchSize - x), (-y_start - y),
                      (-x_start - R - x), (-y_start - R - y), tar.patchSize, true)
    Icon:text(-xT, -yT, [[<b><i>]]..tsp_name, 'FontSize', charSize, 'TextFormat', 'RichText', 'Color', color_txt)
  elseif position == 'lowLeft' then
    _drawEmptyPatch((x_start + R + x), (-y_start - R - y), (x_start + tar.patchSize + x), (-y_start - y), false)
    Icon:color('normalFg')
    _drawPatchBorders((x_start + R + x), (-y_start - R - y), 
                      (x_start + tar.patchSize + x), (-y_start - y), tar.patchSize, false)
    Icon:text(g * xT, g * -yT, [[<b><i>]]..tsp_name, 'FontSize', charSize, 'TextFormat', 'RichText', 'Color', color_txt)
  end
end

-- helper function for iL.drawTrigger()
local function _drawTriggerLines(x, y, trigger_type)
  if trigger_type == 'rising' then
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2.5}, {y - 3, y - 3})
    Icon:line({x, x - 2.5}, {y + 3, y + 3})
    Icon:line({x - 2, x}, {y + 1, y - 1})
    Icon:line({x + 2, x}, {y + 1, y - 1})
  elseif trigger_type == 'falling' then
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2.5}, {y + 3, y + 3})
    Icon:line({x, x - 2.5}, {y - 3, y - 3})
    Icon:line({x - 2, x}, {y - 1, y + 1})
    Icon:line({x + 2, x}, {y - 1, y + 1})
  elseif trigger_type == 'either' then
    x = x - 2.5
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2.5}, {y - 3, y - 3})
    Icon:line({x, x - 2.5}, {y + 3, y + 3})
    Icon:line({x - 2, x}, {y + 1, y - 1})
    Icon:line({x + 2, x}, {y + 1, y - 1})
    x = x + 5
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2.5}, {y + 3, y + 3})
    Icon:line({x, x - 2.5}, {y - 3, y - 3})
    Icon:line({x - 2, x}, {y - 1, y + 1})
    Icon:line({x + 2, x}, {y - 1, y + 1})
  elseif trigger_type == 'levelHigh' then
    x = x - 1.5
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2.5}, {y - 3, y - 3})
    Icon:line({x, x - 2}, {y + 3, y + 3})
    x = x + 3
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2}, {y + 3, y + 3})
    Icon:line({x, x - 2.5}, {y - 3, y - 3})
  elseif trigger_type == 'levelLow' then
    x = x - 1.5
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2.5}, {y + 3, y + 3})
    Icon:line({x, x - 2}, {y - 3, y - 3})
    x = x + 3
    Icon:line({x, x}, {y - 3, y + 3})
    Icon:line({x, x + 2}, {y - 3, y - 3})
    Icon:line({x, x - 2.5}, {y + 3, y + 3})

  end
end

-- draw trigger symbol for blocks that can be triggered by other blocks (i.e. ADC) 
-- and specify the location of the symbol, optionally add triggerType
function iL.drawTrigger(x, y, triggerType)
  triggerType = tonumber(triggerType) or 1 -- default to rising
  
  -- TriggerTypeName list must match block dialog 
  -- this is weirdly brittle, all dialogs must define these as 1, 2, 3
  -- Note that XMC and STM implement the 'none' option as 1, so these should be
  -- zero indexed (with a minus 1) before being passed in.
  local TriggerTypeName = {[0] = 'none', 'rising', 'falling', 'either', 'levelHigh', 'levelLow'}
  local t = TriggerTypeName[triggerType]

  if t == 'none' then
    return -- skip drawing the trigger
  end

  local flipped = Block:get('Flipped')
  local direction = Block:get('Direction')

  Icon:color('normalFg')

  if direction == 0.0 then
    if flipped then
      _drawTriggerLines(-y, x, t)
    else
      _drawTriggerLines(y, x, t)
    end
  end
  if direction == 0.25 then
    if flipped then
      _drawTriggerLines(-x, -y, t)
    else
      _drawTriggerLines(-x, y, t)
    end
  end
  if direction == 0.5 then
    if flipped then
      _drawTriggerLines(y, -x, t)
    else
      _drawTriggerLines(-y, -x, t)
    end
  end
  if direction == 0.75 then
    if flipped then
      _drawTriggerLines(x, y, t)
    else
      _drawTriggerLines(x, -y, t)
    end
  end
end

local function _drawSyncIconLines(x, y)
  -- helper function for iL.drawSyncIcon()
  Icon:line({x - 3.5, x + 3.5}, {y + 2.5, y + 2.5})
  Icon:line({x, x}, {y + 2.5, y - 2.5})
  Icon:line({x - 2.5, x - 2.5}, {y + 2.5, y - 2.5})
  Icon:line({x + 2.5, x + 2.5}, {y + 2.5, y - 2.5})
  Icon:circle(x, y - 2.5, 0.5)
  Icon:circle(x - 2.5, y - 2.5, 0.5)
  Icon:circle(x + 2.5, y - 2.5, 0.5)
end

-- draw synchronize symbol for blocks that can be synchronized with other blocks and specify the location of the symbol
function iL.drawSyncIcon(x, y)
  local flipped = Block:get('Flipped')
  local direction = Block:get('Direction')
  Icon:color('event')
  if direction == 0.0 then
    if flipped then
      _drawSyncIconLines(-y, x)
    else
      _drawSyncIconLines(y, x)
    end
  end
  if direction == 0.25 then
    if flipped then
      _drawSyncIconLines(-x, -y)
    else
      _drawSyncIconLines(-x, y)
    end
  end
  if direction == 0.5 then
    if flipped then
      _drawSyncIconLines(y, -x)
    else
      _drawSyncIconLines(-y, -x)
    end
  end
  if direction == 0.75 then
    if flipped then
      _drawSyncIconLines(x, y)
    else
      _drawSyncIconLines(x, -y)
    end
  end
end

local function _drawCompInPin(x, y)
  local direction = Block:get('Direction')

  if direction == 0.0 then
    Icon:line({x, x}, {y, y - 5})
    Icon:circle(x, y - 6, 1.0)
  elseif direction == 0.25 then
    Icon:line({x, x + 5}, {y, y})
    Icon:circle(x + 6, y, 1.0)
  elseif direction == 0.5 then
    Icon:line({x, x}, {y, y + 5})
    Icon:circle(x, y + 6, 1.0)
  elseif direction == 0.75 then
    Icon:line({x, x - 5}, {y, y})
    Icon:circle(x - 6, y, 1.0)
  end
end

function iL.drawComparator(isInverted, showHyst, showOutExt, negativeIn)
  -- optional inputs default to false
  showHyst = showHyst or false
  showOutExt = showOutExt or false
  negativeIn = negativeIn or false
  -- check datatype on all provided inputs
  if type(isInverted) ~= 'boolean' 
  or type(showHyst) ~= 'boolean'
  or type(showOutExt) ~= 'boolean'
  or type(negativeIn) ~= 'boolean' then
    error('These inputs must be boolean')
  end

  Icon:color('event')
  local direction = Block:get('Direction')
  local flipped = Block:get('Flipped')

  local gain = 1
  if flipped then
    gain = -1
  end
  if direction == 0.0 then
    Icon:line({-13, 0, 13, -13}, {-13, 13, -13, -13})
    Icon:line({0, 0}, {16, 25})
    if not isInverted then
      Icon:line({0, 0}, {13, 16})
    else
      Icon:circle(0.0, 14.5, 1.5)
    end
    Icon:line({-5.0, -5.0}, {-10, -8})
    if showHyst then
      Icon:line({-2, -2, 2, 2}, {-2.5, -1, -1, 2.5})
      Icon:line({-2, -2, 2}, {-1, 1, 1})
    end

    if negativeIn then
      Icon:line({-5 * gain, -5 * gain}, {-13, -25})
    else
      _drawCompInPin(-5 * gain, -13)
    end
    _drawCompInPin(5 * gain, -13)
    if showOutExt then
      Icon:line({0, -5 * gain, -5 * gain}, {18, 18, 20})
      Icon:circle(-5.0 * gain, 21, 1.0)
    end
    Icon:line({5.0, 5.0}, {-10, -8})
    Icon:line({4 * gain, 6 * gain}, {-9, -9})

  elseif direction == 0.25 then
    Icon:line({13, -13, 13, 13}, {-13, 0, 13, -13})
    Icon:line({-16, -25}, {0, 0})
    if not isInverted then
      Icon:line({-13, -16}, {0, 0})
    else
      Icon:circle(-14.5, 0.0, 1.5)
    end
    Icon:line({10, 8}, {-5.0, -5.0})
    if showHyst then
      Icon:line({2.5, 1, 1, -2.5}, {-2, -2, 2, 2})
      Icon:line({1, -1, -1}, {-2, -2, 2})
    end
    if negativeIn then
      Icon:line({13, 25}, {-5 * gain, -5 * gain})
    else
      _drawCompInPin(13, -5 * gain)
    end
    _drawCompInPin(13, 5 * gain)
    if showOutExt then
      Icon:line({-18, -18, -20}, {0, -5 * gain, -5 * gain})
      Icon:circle(-21, -5.0 * gain, 1.0)
    end
    Icon:line({10, 8}, {5.0, 5.0})
    Icon:line({9, 9}, {4 * gain, 6.0 * gain})

  elseif direction == 0.5 then
    Icon:line({13, 0, -13, 13}, {13, -13, 13, 13})
    Icon:line({0, 0}, {-16, -25})
    if not isInverted then
      Icon:line({0, 0}, {-13, -16})
    else
      Icon:circle(0.0, -14.5, 1.5)
    end
    Icon:line({5.0, 5.0}, {10, 8})
    if showHyst then
      Icon:line({2, 2, -2, -2}, {2.5, 1, 1, -2.5})
      Icon:line({2, 2, -2}, {1, -1, -1})
    end
    if negativeIn then
      Icon:line({5 * gain, 5 * gain}, {13, 25})
    else
      _drawCompInPin(5 * gain, 13)
    end
    _drawCompInPin(-5 * gain, 13)
    if showOutExt then
      Icon:line({0, 5 * gain, 5 * gain}, {-18, -18, -20})
      Icon:circle(5.0 * gain, -21, 1.0)
    end
    Icon:line({-5.0, -5.0}, {10, 8})
    Icon:line({-4 * gain, -6.0 * gain}, {9, 9})

  elseif direction == 0.75 then
    Icon:line({-13, 13, -13, -13}, {13, 0, -13, 13})
    Icon:line({16, 25}, {0, 0})
    if not isInverted then
      Icon:line({13, 16}, {0, 0})
    else
      Icon:circle(14.5, 0.0, 1.5)
    end
    Icon:line({-10, -8}, {5.0, 5.0})
    if showHyst then
      Icon:line({-2.5, -1, -1, 2.5}, {2, 2, -2, -2})
      Icon:line({-1, 1, 1}, {2, 2, -2})
    end
    if negativeIn then
      Icon:line({-13, -25}, {5 * gain, 5 * gain})
    else
      _drawCompInPin(-13, 5 * gain)
    end
    _drawCompInPin(-13, -5 * gain)
    if showOutExt then
      Icon:line({18, 18, 20}, {0, 5 * gain, 5 * gain})
      Icon:circle(21, 5.0 * gain, 1.0)
    end
    Icon:line({-10, -8}, {-5.0, -5.0})
    Icon:line({-9, -9}, {-4 * gain, -6.0 * gain})
  end
end

-- Used for blocks that will still compile, but we strongly encourage
-- not using any more.
function iL.drawDeprecate(c)
  local color_d = c or {128, 128, 128}  -- default to gray

  local frame = Block:get('Frame')
  local xLeft = frame[1]
  local xTot = frame[3]
  local yUp = frame[2]
  local yTot = frame[4]

  Icon:color(color_d)
  Icon:line({xLeft + 10, xLeft + xTot - 10}, {yUp + yTot - 10, yUp + 10})
end

function iL.drawRemove(c)
  local color_d = c or {128, 128, 128}  -- default to gray

  local frame = Block:get('Frame')
  local xLeft = frame[1]
  local xTot = frame[3]
  local yUp = frame[2]
  local yTot = frame[4]

  Icon:color(color_d)
  Icon:line({xLeft + 10, xLeft + xTot - 10}, {yUp + yTot - 10, yUp + 10})
  Icon:line({xLeft + xTot - 10, xLeft + 10}, {yUp + yTot - 10, yUp + 10})
end

function iL.drawOpamp(mode, posIn, out)
  local direction = Block:get('Direction')
  local flipped = Block:get('Flipped')
  local gain = 1

  local opampMode = {'standalone', 'follower', 'pga'}  -- must match block dialog -- this is weirdly brittle, all dialogs must define these as 1, 2, 3
  local m = opampMode[mode]

  if flipped then
    gain = -1
  end
  Icon:color('event')
  if direction == 0.0 then
    Icon:line({-13, 0, 13, -13}, {-13, 13, -13, -13})
    Icon:line({-5, -5}, {-10, -8})
    Icon:line({5, 5}, {-10, -8})
    Icon:line({4 * gain, 6 * gain}, {-9, -9})
    if posIn == 2 then
      Icon:line({5 * gain, 5 * gain}, {-13, -40})
    else
      Icon:line({5 * gain, 5 * gain}, {-13, -30})
      Icon:circle(5 * gain, -31, 1)
    end
    Icon:line({0, 0}, {13, 30})
    if out == 1 then
      Icon:circle(0, 31, 1)
    else
      Icon:line({2, 0, -2}, {27, 30, 27})
    end
  elseif direction == 0.25 then
    Icon:line({13, -13, 13, 13}, {-13, 0, 13, -13})
    Icon:line({10, 8}, {-5, -5})
    Icon:line({10, 8}, {5, 5})
    Icon:line({9, 9}, {4 * gain, 6 * gain})
    if posIn == 2 then
      Icon:line({13, 40}, {5 * gain, 5 * gain})
    else
      Icon:line({13, 30}, {5 * gain, 5 * gain})
      Icon:circle(31, 5 * gain, 1)
    end
    Icon:line({-13, -30}, {0, 0})
    if out == 1 then
      Icon:circle(-31, 0, 1)
    else
      Icon:line({-27, -30, -27}, {2, 0, -2})
    end
  elseif direction == 0.5 then
    Icon:line({13, 0, -13, 13}, {13, -13, 13, 13})
    Icon:line({-5, -5}, {10, 8})
    Icon:line({5, 5}, {10, 8})
    Icon:line({-4 * gain, -6 * gain}, {9, 9})
    if posIn == 2 then
      Icon:line({-5 * gain, -5 * gain}, {13, 40})
    else
      Icon:line({-5 * gain, -5 * gain}, {13, 30})
      Icon:circle(-5 * gain, 31, 1)
    end
    Icon:line({0, 0}, {-13, -30})
    if out == 1 then
      Icon:circle(0, -31, 1)
    else
      Icon:line({2, 0, -2}, {-27, -30, -27})
    end
  elseif direction == 0.75 then
    Icon:line({-13, 13, -13, -13}, {13, 0, -13, 13})
    Icon:line({-10, -8}, {5, 5})
    Icon:line({-10, -8}, {-5, -5})
    Icon:line({-9, -9}, {-4 * gain, -6 * gain})
    if posIn == 2 then
      Icon:line({-13, -40}, {-5 * gain, -5 * gain})
    else
      Icon:line({-13, -30}, {-5 * gain, -5 * gain})
      Icon:circle(-31, -5 * gain, 1)
    end
    Icon:line({13, 30}, {0, 0})
    if out == 1 then
      Icon:circle(31, 0, 1)
    else
      Icon:line({27, 30, 27}, {2, 0, -2})
    end
  end

  if m == 'follower' then
    if direction == 0.0 then
      Icon:line({0, 0, -20 * gain, -20 * gain, -5 * gain, -5 * gain}, {13, 20, 20, -20, -20, -13})
    elseif direction == 0.25 then
      Icon:line({-13, -20, -20, 20, 20, 13}, {0, 0, -20 * gain, -20 * gain, -5 * gain, -5 * gain})
    elseif direction == 0.5 then
      Icon:line({0, 0, 20 * gain, 20 * gain, 5 * gain, 5 * gain}, {-13, -20, -20, 20, 20, 13})
    elseif direction == 0.75 then
      Icon:line({13, 20, 20, -20, -20, -13}, {0, 0, 20 * gain, 20 * gain, 5 * gain, 5 * gain})
    end
  elseif m == 'standalone' then
    if direction == 0.0 then
      Icon:line({-5 * gain, -5 * gain}, {-13, -30})
      Icon:circle(-5 * gain, -31, 1)
    elseif direction == 0.25 then
      Icon:line({13, 30}, {-5 * gain, -5 * gain})
      Icon:circle(31, -5 * gain, 1)
    elseif direction == 0.5 then
      Icon:line({5 * gain, 5 * gain}, {13, 30})
      Icon:circle(5 * gain, 31, 1)
    elseif direction == 0.75 then
      Icon:line({-13, -30}, {5 * gain, 5 * gain})
      Icon:circle(-31, 5 * gain, 1)
    end
  elseif m == 'pga' then
    if direction == 0.0 then
      Icon:line({0, -8 * gain}, {20, 20})
      Icon:line({-8 * gain, -8 * gain}, {18, 22})
      Icon:line({-8 * gain, -14 * gain}, {18, 18})
      Icon:line({-8 * gain, -14 * gain}, {22, 22})
      Icon:line({-14 * gain, -14 * gain}, {18, 22})
      Icon:line({-14 * gain, -18 * gain, -18 * gain, -5 * gain, -5 * gain}, {20, 20, -20, -20, -13})
      Icon:line({-18 * gain, -22 * gain}, {20, 20})
      Icon:line({-22 * gain, -22 * gain}, {18, 22})
      Icon:line({-22 * gain, -28 * gain}, {18, 18})
      Icon:line({-22 * gain, -28 * gain}, {22, 22})
      Icon:line({-28 * gain, -28 * gain}, {18, 22})
      Icon:line({-28 * gain, -32 * gain}, {20, 20})
      Icon:line({-32 * gain, -32 * gain}, {18, 22})
      Icon:line({-34 * gain, -34 * gain}, {19, 21})
      Icon:circle(-18 * gain, 20, 1)
      Icon:circle(0, 20, 1)
    elseif direction == 0.25 then
      Icon:line({-20, -20}, {0, -8 * gain})
      Icon:line({-18, -22}, {-8 * gain, -8 * gain})
      Icon:line({-18, -18}, {-8 * gain, -14 * gain})
      Icon:line({-22, -22}, {-8 * gain, -14 * gain})
      Icon:line({-18, -22}, {-14 * gain, -14 * gain})
      Icon:line({-20, -20, 20, 20, 13}, {-14 * gain, -18 * gain, -18 * gain, -5 * gain, -5 * gain})
      Icon:line({-20, -20}, {-18 * gain, -22 * gain})
      Icon:line({-18, -22}, {-22 * gain, -22 * gain})
      Icon:line({-18, -18}, {-22 * gain, -28 * gain})
      Icon:line({-22, -22}, {-22 * gain, -28 * gain})
      Icon:line({-18, -22}, {-28 * gain, -28 * gain})
      Icon:line({-20, -20}, {-28 * gain, -32 * gain})
      Icon:line({-18, -22}, {-32 * gain, -32 * gain})
      Icon:line({-19, -21}, {-34 * gain, -34 * gain})
      Icon:circle(-20, -18 * gain, 1)
      Icon:circle(-20, 0, 1)
    elseif direction == 0.5 then
      Icon:line({0, 8 * gain}, {-20, -20})
      Icon:line({8 * gain, 8 * gain}, {-18, -22})
      Icon:line({8 * gain, 14 * gain}, {-18, -18})
      Icon:line({8 * gain, 14 * gain}, {-22, -22})
      Icon:line({14 * gain, 14 * gain}, {-18, -22})
      Icon:line({14 * gain, 18 * gain, 18 * gain, 5 * gain, 5 * gain}, {-20, -20, 20, 20, 13})
      Icon:line({18 * gain, 22 * gain}, {-20, -20})
      Icon:line({22 * gain, 22 * gain}, {-18, -22})
      Icon:line({22 * gain, 28 * gain}, {-18, -18})
      Icon:line({22 * gain, 28 * gain}, {-22, -22})
      Icon:line({28 * gain, 28 * gain}, {-18, -22})
      Icon:line({28 * gain, 32 * gain}, {-20, -20})
      Icon:line({32 * gain, 32 * gain}, {-18, -22})
      Icon:line({34 * gain, 34 * gain}, {-19, -21})
      Icon:circle(18 * gain, 20, 1)
      Icon:circle(0, -20, 1)
    elseif direction == 0.75 then
      Icon:line({20, 20}, {0, 8 * gain})
      Icon:line({18, 22}, {8 * gain, 8 * gain})
      Icon:line({18, 18}, {8 * gain, 14 * gain})
      Icon:line({22, 22}, {8 * gain, 14 * gain})
      Icon:line({18, 22}, {14 * gain, 14 * gain})
      Icon:line({20, 20, -20, -20, -13}, {14 * gain, 18 * gain, 18 * gain, 5 * gain, 5 * gain})
      Icon:line({20, 20}, {18 * gain, 22 * gain})
      Icon:line({18, 22}, {22 * gain, 22 * gain})
      Icon:line({18, 18}, {22 * gain, 28 * gain})
      Icon:line({22, 22}, {22 * gain, 28 * gain})
      Icon:line({18, 22}, {28 * gain, 28 * gain})
      Icon:line({20, 20}, {28 * gain, 32 * gain})
      Icon:line({18, 22}, {32 * gain, 32 * gain})
      Icon:line({19, 21}, {34 * gain, 34 * gain})
      Icon:circle(20, 18 * gain, 1)
      Icon:circle(20, 0, 1)
    end
  end
end

return iL
