require 'cairo'

netuphist = {}
netdownhist = {}
ramhist = {}
swaphist = {}
cpuhist = {}
cur = 1

-- SETTINGS
hostname = conky_parse("$nodename")

if hostname == "tablet" then
   screenWidth = 1920
   screenHeight = 1080
   nbCPU = 8
   FSs = {"/", "/var", "/home"}
   ladapter = "eth0"
   wadapter = "wlp58s0"
   ntop = 10
   hwmon = 7
   sensor = 1
elseif hostname == "hnv-laptop" then
   screenWidth = 1920
   screenHeight = 1080
   nbCPU = 8
   FSs = {"/", "/var", "/home"}
   ladapter = "eth0"
   wadapter = "wlp58s0"
   ntop = 10
   hwmon = 7
   sensor = 1
elseif hostname == "home" then
   screenWidth = 1920
   screenHeight = 1080
   nbCPU = 12
   FSs = {"/", "/home", "/data", "/archives"}
   ladapter = "eno1"
   wadapter = "wlan0"
   ntop = 5
   hwmon = 2
   sensor = 2
elseif hostname == "hegp-laptop" then
   screenWidth = 1920
   screenHeight = 1080
   nbCPU = 8
   FSs = {"/", "/var", "/home"}
   ladapter = "eth0"
   wadapter = "wlo1"
   ntop = 10
   hwmon = 7
   sensor = 2
elseif hostname == "hegp" then
   screenWidth = 3840
   screenHeight = 1600
   nbCPU = 48
   FSs = {"/", "/var", "/home", "/data"}
   ladapter = "enp0s31f6"
   wadapter = "wlan0"
   ntop = 10
   hwmon = 2
   sensor = 2
end
-- SETTINGS

function conky_init()
  local cr, cs = nil

  if conky_window == nil then return end
  if cs == nil then -- or cairo_xlib_surface_get_width(cs) ~= conky_window.width or cairo_xlib_surface_get_height(cs) ~= conky_window.height then
    if cs then cairo_surface_destroy(cs) end
    cs = cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, screenWidth, screenHeight)
  end
  if cr then cairo_destroy(cr) end
  cr = cairo_create(cs)

  local items = 30 + nbCPU + #FSs + ntop
  if hostname == "hegp" then
     defaultSize = screenHeight / items - 1
  else
     defaultSize = screenHeight / items
  end
  height = defaultSize + 1
  small = defaultSize * .8
  blue = {.4,.6,.8,.5}
  cur = cur % 100 + 1

  local ypos = 60
  local xpos = 25
  local margin = 50

  ypos = general(cr, xpos, ypos)       + margin
  ypos = fs     (cr, xpos, ypos)       + margin
  ypos = ram    (cr, xpos, ypos)       + margin
  ypos = cpu    (cr, xpos, ypos)       + margin
  ypos = top    (cr, xpos, ypos, ntop) + margin
  ypos = network(cr, xpos, ypos)       + margin

if hostname == "home" then
  clock(cr, screenWidth / 2, screenHeight / 2 - 170)
  weather(cr, screenWidth / 2 - 11.5*.6*15, screenHeight / 2 - 60)
else
   clock(cr, screenWidth - 260, 170)
   weather(cr, screenWidth - 420, 290)
   calendar(cr, screenWidth - 475, 440)
   agenda(cr, screenWidth - 480, 620)
end

  cairo_destroy(cr)
  cairo_surface_destroy(cs)
end

function clock(cr, x, y)
  emboss(cr, x, y - 100, conky_parse("${time %H}"), 1, 200)
  emboss(cr, x, y - 50, conky_parse(":${time %M}"), 0, 150)
end

function calendar(cr, x, y)
   size = 18
   yt = 0
   for line in io.lines("/tmp/cal.txt") do
      yt = yt + size
      xt = 0
      for substr in string.gmatch(line, "[^]+") do
         if substr:match("%[7m") then
            color = {.45, .65, .9, 1}
            xt = xt + 3
         else
            color = nil
         end
         substr = substr:gsub("%[.?7m", "")
         substr = substr:gsub("%[.?0m", "")
         emboss(cr, x + xt, y + yt, substr, 0, size, nil, color, "Fira Code")
         xt = xt + .6*size * utf8.len(substr)
      end
   end
end

function agenda(cr, x, y)
  size = 14
  yt = 0
  for line in io.lines("/tmp/agenda_export.txt") do
    yt = yt + size
    xt = 0
    for substr in string.gmatch(line, "[^]+") do
      color = tonumber(substr:match("%[38;5;(%d+)m"))
      if color ~= nil then
        if color <= 231 then
          color = color - 16
          b = color % 36 % 6
          color = color - b
          g = color % 36 / 6
          color = color - g * 6
          r = color / 36
          color = {r * .2, g * .2, b * .2, .5}
        else
          a = (color - 232) / 24
          color = {a, a, a, .5}
        end
      end
      substr = substr:gsub("%[[^m]+m", "")
      emboss(cr, x + xt, y + yt, substr, 0, size, nil, color, "Fira Code")
      xt = xt + .6*size * utf8.len(substr)
    end
  end
end

function weather(cr, x, y)
  size = 20
  yt = 0
  for line in io.lines("/tmp/meteo") do
    yt = yt + size
    xt = 0
    for substr in string.gmatch(line, "[^]+") do
      color = tonumber(substr:match("%[38;5;(%d+)m"))
      if color ~= nil then
        if color <= 231 then
          color = color - 16
          b = color % 36 % 6
          color = color - b
          g = color % 36 / 6
          color = color - g * 6
          r = color / 36
          color = {r * .2, g * .2, b * .2, .5}
        else
          a = (color - 232) / 24
          color = {a, a, a, .5}
        end
      end
      substr = substr:gsub("%[[^m]+m", "")
      emboss(cr, x + xt, y + yt, substr, 0, size, nil, color, "Fira Code")
      xt = xt + .6*size * utf8.len(substr)
    end
  end
end

function general(cr, x, y)
  cadre(cr, x, y, 450, 3 * height, 10)

  emboss(cr    , x      , y, "Kernel")
  y = emboss(cr, x + 450, y, conky_parse("$kernel") , 1)
  emboss(cr    , x      , y, "Uptime")
  y = emboss(cr, x + 450, y, conky_parse("$uptime") , 1)
  emboss(cr    , x      , y, "Load")
  y = emboss(cr, x + 450, y, conky_parse("$loadavg"), 1)

  return y
end

function ram(cr, x, y)
   if hostname == "home" or hostname == "hegp" then
      cadre(cr, x, y, 450, 2 * height, 10)
   else
      cadre(cr, x, y, 450, 10 + 4 * height, 10)

      if #swaphist == 0 then
         local i
         for i = 1,100 do
            swaphist[i] =.001
         end
      end

      swaphist[cur] = tonumber(conky_parse("$swapperc"))
   end

  if #ramhist == 0 then
    local i
    for i = 1,100 do
      ramhist[i] =.001
    end
  end

  ramhist[cur] = tonumber(conky_parse("$memperc"))

  emboss(cr, x      , y, "Ram")
  emboss(cr, x + 180, y, conky_parse("$mem/")  , 1)
  y = emboss(cr, x + 260, y, conky_parse("$memmax"), 1)
  bar(cr, x + 5, y, 255, conky_parse("$memperc"), blue)
  graph(cr, x + 280, y, 170, ramhist, blue, 100)

  if hostname ~= "home" and hostname ~= "hegp" then
     y = y + height + 10

     emboss(cr, x      , y, "Swap")
     emboss(cr, x + 180, y, conky_parse("$swap/")  , 1)
     y = emboss(cr, x + 260, y, conky_parse("$swapmax"), 1)
     bar(cr, x + 5, y, 255, conky_parse("$swapperc"), blue)
     graph(cr, x + 280, y, 170, swaphist, blue, 100)
  end

  y = y + height

  return y
end

function cpu(cr, x, y)
  cadre(cr, x, y, 450, 10 + (1 + nbCPU) * height, 10)

  if #cpuhist == 0 then
    local i, j
    for i = 1,nbCPU do
      cpuhist[i] = {}
      for j = 1,100 do
        cpuhist[i][j] = .001
      end
    end
  end

  emboss(cr    , x      , y, "Cpu")
  emboss(cr    , x + 260, y, conky_parse("$freq_g GHz")             , 1)
  y = emboss(cr, x + 450, y, conky_parse("Temp ${hwmon "..hwmon.." temp "..sensor.."}°C"), 1) + 10

  local cpu

  for cpu=1,nbCPU do
    bar(cr, x + 5, y, 255, conky_parse("${cpu cpu" .. cpu .. "}"), blue)
    cpuhist[cpu][cur] = tonumber(conky_parse("${cpu cpu" .. cpu .. "}"))
    graph(cr, x + 280, y, 170, cpuhist[cpu], blue, 100)
    y = y + height
  end

  return y
end

function network(cr, x, y)
  cadre(cr, x, y, 450, 4 * height + 10, 10)

  local upspd = conky_parse("${upspeedf " .. wadapter .. "}") + conky_parse("${upspeedf " .. ladapter .. "}")
  local downspd = conky_parse("${downspeedf " .. wadapter .. "}") + conky_parse("${downspeedf " .. ladapter .. "}")

  local upspdunit = "KiB/s"
  local downspdunit = "KiB/s"
  if upspd > 1024 then
    upspd = upspd / 1024
    upspdunit = "MiB/s"
  end
  if downspd > 1024 then
    downspd = downspd / 1024
    downspdunit = "MiB/s"
  end

  local ethup = string.match(conky_parse("${totalup " .. ladapter .. "}"),"[%d.]+")
  local ethupunit = string.match(conky_parse("${totalup " .. ladapter .. "}"),"%a")
  if ethupunit == "K" then
    ethup = ethup * 1024
  elseif ethupunit == "M" then
    ethup = ethup * 1024 * 1024
  elseif ethupunit == "G" then
    ethup = ethup * 1024 * 1024 * 1024
  end

  local ethdown = string.match(conky_parse("${totaldown " .. ladapter .. "}"),"[%d.]+")
  local ethdownunit = string.match(conky_parse("${totaldown " .. ladapter .. "}"),"%a")
  if ethdownunit == "K" then
    ethdown = ethdown * 1024
  elseif ethdownunit == "M" then
    ethdown = ethdown * 1024 * 1024
  elseif ethdownunit == "G" then
    ethdown = ethdown * 1024 * 1024 * 1024
  end

  local wlanup = string.match(conky_parse("${totalup " .. wadapter .. "}"),"[%d.]+")
  local wlanupunit = string.match(conky_parse("${totalup " .. wadapter .. "}"),"%a")
  if wlanupunit == "K" then
    wlanup = wlanup * 1024
  elseif wlanupunit == "M" then
    wlanup = wlanup * 1024 * 1024
  elseif wlanupunit == "G" then
    wlanup = wlanup * 1024 * 1024 * 1024
  end

  local wlandown = string.match(conky_parse("${totaldown " .. wadapter .. "}"),"[%d.]+")
  local wlandownunit = string.match(conky_parse("${totaldown " .. wadapter .. "}"),"%a")
  if wlandownunit == "K" then
    wlandown = wlandown * 1024
  elseif wlandownunit == "M" then
    wlandown = wlandown * 1024 * 1024
  elseif wlandownunit == "G" then
    wlandown = wlandown * 1024 * 1024 * 1024
  end

  local totalup = ethup + wlanup
  local upunit = "B"
  if totalup > (1024 * 1024 * 1024) then
    totalup = totalup / (1024 * 1024 * 1024)
    upunit = "GiB"
  elseif totalup > (1024 * 1024) then
    totalup = totalup / (1024 * 1024)
    upunit = "MiB"
  elseif totalup > 1024 then
    totalup = totalup / 1024
    upunit = "KiB"
  end

  local totaldown = ethdown + wlandown
  local downunit = "B"
  if totaldown > (1024 * 1024 * 1024) then
    totaldown = totaldown / (1024 * 1024 * 1024)
    downunit = "GiB"
  elseif totaldown > (1024 * 1024) then
    totaldown = totaldown / (1024 * 1024)
    downunit = "MiB"
  elseif totaldown > 1024 then
    totaldown = totaldown / 1024
    downunit = "KiB"
  end

  if #netuphist == 0 then
    local i
    for i = 1,100 do
      netuphist[i] = .001
    end
  end

  if #netdownhist == 0 then
    local i
    for i = 1,100 do
      netdownhist[i] = .001
    end
  end

  netuphist[cur] = tonumber(conky_parse("${upspeedf " .. wadapter .. "}") + conky_parse("${upspeedf " .. ladapter .. "}"))
  netdownhist[cur] = tonumber(conky_parse("${downspeedf " .. wadapter .. "}") + conky_parse("${downspeedf " .. ladapter .. "}"))

  maxspeed = max({max(netuphist), max(netdownhist)})
  if maxspeed == 0 then
    maxspeed = 1
  end

  emboss(cr, x      , y, "Up")
  emboss(cr, x + 260, y, string.format("%.1f",upspd) .. upspdunit    , 1)
  graph(cr,  x + 280, y, 170, netuphist, blue, maxspeed)
  y = y + height
  emboss(cr, x      , y, "Down")
  emboss(cr, x + 260, y, string.format("%.1f",downspd) .. downspdunit, 1)
  graph(cr,  x + 280, y, 170, netdownhist, blue, maxspeed, 1)
  y = y + height + 10
  emboss(cr    , x    , y, "Total up")
  y = emboss(cr, x+260, y, string.format("%.2f",totalup) .. upunit     , 1)
  emboss(cr    , x    , y, "Total down")
  y = emboss(cr, x+260, y, string.format("%.2f",totaldown) .. downunit , 1)

  return y
end

function fs(cr, x, y)
  cadre(cr, x, y, 450, height * #FSs, 10)

  for row=1,#FSs do
    emboss(cr, x      , y     , FSs[row])
    emboss(cr, x + 180, y     , conky_parse("${fs_used " .. FSs[row] .. "}/"), 1)
    emboss(cr, x + 260, y     , conky_parse("${fs_size " .. FSs[row] .. "}") , 1)
    bar(cr, x + 280, y, 170, conky_parse("${fs_used_perc " .. FSs[row] .. "}"), blue)
    y = y + height
  end

  return y
end

function top(cr, x, y, nrows)
  cadre(cr, x, y, 450, height + 10 + nrows * small, 10)

  emboss(cr    , x      , y, "Name")
  emboss(cr    , x + 220, y, "Cpu", 1)
  emboss(cr    , x + 230, y, "Name")
  y = emboss(cr, x + 450, y, "Ram", 1) + 10

  local row

  for row=1,nrows do
    emboss(cr    , x      , y, conky_parse("${top name " .. row .. "}")    , 0, small)
    emboss(cr    , x + 220, y, conky_parse("${top cpu " .. row .. "}")     , 1, small)
    emboss(cr    , x + 230, y, conky_parse("${top_mem name " .. row .. "}"), 0, small)
    y = emboss(cr, x + 450, y, conky_parse("${top_mem mem " .. row .. "}") , 1, small)
  end

  return y
end

function cadre(cr, x, y, w, h, r, pop, col)
  if pop == nil then pop = 1 end

  local pi = 3.141592

  if pop < 0 then y = y + pop end
  cairo_set_operator(cr, CAIRO_OPERATOR_XOR)
    cairo_move_to(cr, x, y - r)
    cairo_arc(cr, x + w, y    , r, 1.5 * pi, 0   * pi)
    cairo_arc(cr, x + w, y + h, r, 0   * pi, .5  * pi)
    cairo_arc(cr, x    , y + h, r, .5  * pi, 1   * pi)
    cairo_arc(cr, x    , y    , r, 1   * pi, 1.5 * pi)

    cairo_set_source_rgb(cr, 1, 1, 1)
    cairo_fill(cr)

    y = y - pop

    cairo_move_to(cr, x, y - r)
    cairo_arc(cr, x + w, y    , r,1.5 * pi, 0   * pi)
    cairo_arc(cr, x + w, y + h, r,0   * pi, .5  * pi)
    cairo_arc(cr, x    , y + h, r,.5  * pi, 1   * pi)
    cairo_arc(cr, x    , y    , r,1   * pi, 1.5 * pi)

    cairo_set_source_rgb(cr, 0, 0, 0)
    cairo_fill(cr)
  cairo_set_operator(cr, CAIRO_OPERATOR_OVER)

  cairo_move_to(cr, x, y - r)
  cairo_arc(cr, x + w, y    , r, 1.5 * pi, 0   * pi)
  cairo_arc(cr, x + w, y + h, r, 0   * pi, .5  * pi)
  cairo_arc(cr, x    , y + h, r, .5  * pi, 1   * pi)
  cairo_arc(cr, x    , y    , r, 1   * pi, 1.5 * pi)

  if pop > 0 then cairo_set_source_rgba(cr, 0, 0, 0, .1) else cairo_set_source_rgba(cr, 1, 1, 1, .1) end
  cairo_fill_preserve(cr)
  if col ~= nil then cairo_set_source_rgba(cr, col[1], col[2], col[3], col[4]) else cairo_set_source_rgba(cr, 0, 0, 0, 0)end
  cairo_fill(cr)
end

function bar(cr, x, y, width, percent, color)
  local barh = height / 3
  emboss(cr, x + width, y + 3, percent .. "%", 1, small)
  width = width - 45
  cadre(cr, x, y + barh + 4, width                , barh - 4, 4)
  cadre(cr, x, y + barh + 4, width * percent / 100, barh - 4, 2, -1, color)
end

function emboss(cr, x, y, text, right, size, pop, col, font)
  if pop == nil then pop = 1 end
  if size == nil then size = defaultSize end
  if right == nil then right = 0 end
  if font == nil then font = "Impact" end

  y = y + size

  cairo_select_font_face (cr, font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size (cr, size);

  if right == 1 then
    extent = cairo_text_extents_t:create()
    tolua.takeownership(extent)
    cairo_text_extents(cr, text, extent)
    x = x - extent.x_advance
  -- cairo_text_extents_t:destroy(extent)
  end

  if pop < 0 then y = y + pop end
  cairo_set_operator(cr, CAIRO_OPERATOR_XOR)
    cairo_move_to(cr, x, y)
    cairo_text_path(cr, text)
    cairo_set_source_rgb(cr, 1, 1, 1)
    cairo_fill(cr)

    cairo_move_to(cr,x,y - pop)
    cairo_text_path(cr, text)
    cairo_set_source_rgb(cr, 0, 0, 0)
    cairo_fill(cr)
  cairo_set_operator(cr, CAIRO_OPERATOR_OVER)

  cairo_move_to(cr, x, y)
  cairo_text_path(cr, text)
  if pop > 0 then cairo_set_source_rgba(cr, 0, 0, 0, .1) else cairo_set_source_rgba(cr, 1, 1, 1, .1) end
  cairo_fill_preserve(cr)
  if col ~= nil then cairo_set_source_rgba(cr, col[1], col[2], col[3], col[4]) else cairo_set_source_rgba(cr, 0, 0, 0, 0) end
  cairo_fill(cr)

  return y
end

function graph(cr, x, y, width, hist, color, maximum, reverse)

  if maximum == nil then maximum = max(hist) end
  if reverse == nil then reverse = 0 end

  x = x - 2
  width = width + 2

  local scalev = (height - 2) / maximum
  if reverse == 1 then
    scalev = -scalev
    y = y + 1
  else
    y = y + height - 1
  end
  local scaleh = width / 100

  cairo_move_to(cr, x + scaleh, y)

  local i
  for i = cur+1,100 do
    cairo_line_to(cr, x + (i - cur) * scaleh, y - hist[i] * scalev)
  end
  for i = 1,cur do
    cairo_line_to(cr, x + ((100 - cur) + i) * scaleh, y - hist[i] * scalev)
  end

  cairo_line_to(cr, x + 100 * scaleh, y)
  cairo_close_path(cr)
  cairo_set_source_rgb(cr, 1, 1, 1)
  cairo_fill(cr)

  y = y - 2

  cairo_move_to(cr, x + scaleh, y)

  for i = cur+1,100 do
    cairo_line_to(cr, x + (i - cur) * scaleh, y - hist[i] * scalev)
  end
  for i = 1,cur do
    cairo_line_to(cr, x + ((100 - cur) + i) * scaleh, y - hist[i] * scalev)
  end

  cairo_line_to(cr, x + 100 * scaleh, y)
  cairo_close_path(cr)
  cairo_set_source_rgb(cr, 0, 0, 0)
  cairo_fill(cr)

  y = y + 1

  cairo_move_to(cr, x + scaleh, y)

  for i = cur+1,100 do
    cairo_line_to(cr, x + (i - cur) * scaleh, y - hist[i] * scalev)
  end
  for i = 1,cur do
    cairo_line_to(cr, x + ((100 - cur) + i) * scaleh, y - hist[i] * scalev)
  end

  cairo_line_to(cr, x + 100 * scaleh, y)
  cairo_close_path(cr)
  cairo_set_source_rgba(cr, color[1], color[2], color[3], 1)
  cairo_fill(cr)
end

function max(hist)
  local i
  local max = 0

  for i=1,#hist do
    if hist[i] > max then max = hist[i] end
  end

  return max
end
