--------------------------------------
-- This is the application launcher --
--------------------------------------

-- Awesome Libs
local awful = require("awful")
local dpi = require("beautiful").xresources.apply_dpi
local gears = require("gears")
local wibox = require("wibox")
local ical_parser = require("src.tools.ical_parser")

local capi = {
  awesome = awesome,
  mouse = mouse,
}

local icondir = gears.filesystem.get_configuration_dir() .. "src/assets/icons/calendar/"

--- Month name lookup table
local months_table = {
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December",
  "January",
  "February",
}

--- Table to easily shift back every month by two months
local month_convert = {
  11,
  12,
  1,
  2,
  3,
  4,
  5,
  6,
  7,
  8,
  9,
  10,
}

--- Weekdays name lookup table
local weekdays_table = {
  "Mon",
  "Tue",
  "Wed",
  "Thu",
  "Fri",
  "Sat",
  "Sun",
}

--- Create a date object from the current date
local os_date = os.date("%d%m%Y")
local date = {
  day = math.floor(os_date:sub(1, 2)),
  month = month_convert[math.floor(os_date:sub(3, 4))],
  year = math.floor(os_date:sub(5, 8))
}

---Calculates the weekday of a given date
---@param day number|string? as number, usually from date.da
---@param month number|string? as number, usually from date.month
---@param year number|string? as number, usually from date.year
---@return number|nil weekday as number
local function get_date_weekday(day, month, year)
  if not (day and month and year) then return end

  if (month == 11) or (month == 12) then
    year = year - 1
  end

  -- No idea how the algorithm works, but since it works -> don't touch it!
  local w = ((day + math.floor(2.6 * month - 0.2) - 2 * tonumber(tostring(year):match("([0-9]+)[0-9][0-9]")) +
      tonumber(tostring(year):match("[0-9][0-9]([0-9]+)")) +
      math.floor(tonumber(tostring(year):match("[0-9][0-9]([0-9]+)")) / 4) +
      math.floor(tonumber(tostring(year):match("([0-9]+)[0-9][0-9]")) / 4)) % 7)
  --TODO: Add user variable to choose between Sunday and Monday weekstart
  if w == 0 then w = 7 end
  return w
end

---Returns the length of the month from a lookup table and also check for leap years
---@param month number? as number, usually from date.month, can also be a string
---@param year number? as number, usually from date.year, can also be a string
---@return integer|nil month_length as integer
local function get_last_day_of_month(month, year)
  if not (month and year) then return end

  month = tonumber(month)
  local last_day = {
    31,
    30,
    31,
    30,
    31,
    31,
    30,
    31,
    30,
    31,
    31,
    28,
  }
  --In this calculcation February is the 12th of last year
  if (month == 12) and (math.floor(year % 4) == 0) then
    return 29
  else
    return last_day[month]
  end
end

---Simple function to calculate how many weeks will need to be displayed in a calendar
---@param month number? as number, usually from date.month, can also be a string
---@param year number? as number, usually from date.year, can also be a string
---@return number|nil weeks ammount of weeks between 4-6
local function get_weeks_in_month(month, year)
  if not (month and year) then return end
  return math.ceil((get_last_day_of_month(month, year) + get_date_weekday(1, month, year) - 1) / 7)
end

---Gets the last month and accounts for year changes
---@param d table date object
---@return table|nil date returns a date object
local function get_last_month(d)
  if not (d) then return end
  if d.month == 1 then
    return { month = 12, year = d.year - 1 }
  else
    return { month = d.month - 1, year = d.year }
  end
end

---Simple function to create a widget 7x for each day of the week
---@return table weeks_widget All weekdays names as a widget
local function create_weekdays()
  local weekdays = { layout = wibox.layout.flex.horizontal }
  for i = 1, 7 do
    table.insert(weekdays, wibox.widget {
      {
        text = weekdays_table[i],
        align = "center",
        valign = "center",
        widget = wibox.widget.textbox,
      },
      bg = Theme_config.calendar.weekdays.bg,
      fg = Theme_config.calendar.weekdays.fg,
      widget = wibox.container.background,
    })
  end
  return weekdays
end

---Create tasks from ical object
---@return table|nil tasks All tasks as a table
local function create_tasks()
  if not ical_parser or not ical_parser.VCALENDAR then return end
  local tasks = {}
  --TODO: Initialize the timezone from ical.VCALENDAR.VTIMEZONE and make sure the time is correct

  -- Sort every VEVENT in ical by date into the tasks table.
  for _, calendar in ipairs(ical_parser.VCALENDAR) do
    for _, event in ipairs(calendar.VEVENT) do
      local start_time = event.DTSTART.DTSTART
      start_time.month = month_convert[start_time.month]
      local end_time
      if event.DTEND then
        end_time = event.DTEND.DTEND
        end_time.month = month_convert[end_time.month]
      end

      if event.RRULE then
        if event.RRULE.FREQ == "DAILY" then
        elseif event.RRULE.FREQ == "WEEKLY" then
          -- An event will always start on the day it first occurs.
          local event_start = start_time
          local event_end = event.RRULE.UNTIL

          local year_counter = event_start.year
          local month_counter = event_start.month
          if month_counter == 11 then
            year_counter = year_counter - 1
          elseif month_counter == 12 then
            year_counter = year_counter - 1
          end
          local day_counter = event_start.day
          local task = {}
          while (year_counter <= event_end.year) or (month_counter <= event_end.month) or (day_counter <= event_end.day) do
            -- First event will always be right since we start on its starting day
            task = {
              date_start = {
                year = year_counter,
                month = month_counter,
                day = day_counter,
                hour = event_start.hour or 0,
                minute = event_start.minute or 0,
                second = event_start.second or 0
              },
              date_end = {
                year = year_counter,
                month = month_counter,
                day = day_counter,
                hour = end_time.hour or 0,
                minute = end_time.minute or 0,
                second = end_time.second or 0
              },
              summary = event.SUMMARY,
              location = event.LOCATION,
            }

            if event.VALARM then
              task.alarm = {
                time = event.VALARM.TRIGGER.TRIGGER
              }
              local alarm_time = task.alarm.time:match("([-]?%d+)")
              local alarm_unit = task.alarm.time:match("(%a)")
              if alarm_unit == "W" then
                alarm_time = alarm_time * 604800
              elseif alarm_unit == "D" then
                alarm_time = alarm_time * 86400
              elseif alarm_unit == "H" then
                alarm_time = alarm_time * 3600
              elseif alarm_unit == "M" then
                alarm_time = alarm_time * 60
              end

              --[[ gears.timer {
              autostart = true,
              callback = function()
                if alarm_time then
                  require("naughty").notification {
                    app_name = "Task Alarm",
                    title = task.summary,
                    message = task.description,
                    urgency = "normal",
                    timeout = 5,
                    icon = icondir .. "/alarm.png",
                  }
                  gears.timer:stop()
                end
              end
            } ]]
            end

            table.insert(tasks, task)

            day_counter = day_counter + 7
            local month_length = get_last_day_of_month(month_counter, year_counter)
            if day_counter > month_length then
              day_counter = day_counter - month_length
              month_counter = month_counter + 1
            end
            if month_counter == 11 then
              year_counter = year_counter + 1
            end
            if month_counter > 13 then
              month_counter = 1
            end
          end
        elseif event.RRULE.FREQ == "MONTHLY" then
        elseif event.RRULE.FREQ == "YEARLY" then
          if not end_time then
            end_time = {
              year = start_time.year + 1000,
            }
          end

          local task = {}
          for i = start_time.year, end_time.year, 1 do
            task = {
              date_start = {
                year = i,
                month = start_time.month,
                day = start_time.day,
                hour = start_time.hour or 0,
                minute = start_time.minute or 0,
                second = start_time.second or 0
              },
              date_end = {
                year = i,
                month = start_time.month,
                day = start_time.day,
                hour = end_time.hour or 0,
                minute = end_time.minute or 0,
                second = end_time.second or 0
              },
              summary = event.SUMMARY,
              location = event.LOCATION,
              description = event.DESCRIPTION,
              url = event.URL,
            }

            if event.VALARM then
              task.alarm = {
                time = event.VALARM.TRIGGER.TRIGGER
              }
              local alarm_time = task.alarm.time:match("([-]?%d+)")
              local alarm_unit = task.alarm.time:match("(%a)")
              if alarm_unit == "W" then
                alarm_time = alarm_time * 604800
              elseif alarm_unit == "D" then
                alarm_time = alarm_time * 86400
              elseif alarm_unit == "H" then
                alarm_time = alarm_time * 3600
              elseif alarm_unit == "M" then
                alarm_time = alarm_time * 60
              end

              --[[ gears.timer {
              timeout = os.time(task.date_start) - os.time() + alarm_time,
              autostart = true,
              callback = function()
                require("naughty").notification {
                  app_name = "Task Alarm",
                  title = task.summary,
                  message = task.description,
                  urgency = "normal",
                  timeout = 5,
                  icon = icondir .. "/alarm.png",
                }
              end
            } ]]
            end

            table.insert(tasks, task)
          end
        end
      else
        local task = {
          date_start = {
            year = start_time.year,
            month = start_time.month,
            day = start_time.day,
            hour = start_time.hour or 0,
            minute = start_time.minute or 0,
            second = start_time.second or 0
          },
          date_end = {
            year = start_time.year,
            month = start_time.month,
            day = start_time.day,
            hour = start_time.hour or 0,
            minute = start_time.minute or 0,
            second = start_time.second or 0
          },
          summary = event.SUMMARY,
          location = event.LOCATION,
          description = event.DESCRIPTION,
          url = event.URL,
        }

        if event.VALARM then
          task.alarm = {
            time = event.VALARM.TRIGGER.TRIGGER
          }
          local alarm_time = task.alarm.time:match("([-]?%d+)")
          local alarm_unit = task.alarm.time:match("(%a)")
          if alarm_unit == "W" then
            alarm_time = alarm_time * 604800
          elseif alarm_unit == "D" then
            alarm_time = alarm_time * 86400
          elseif alarm_unit == "H" then
            alarm_time = alarm_time * 3600
          elseif alarm_unit == "M" then
            alarm_time = alarm_time * 60
          end

          --[[ gears.timer {
          timeout = os.time(task.date_start) - os.time() + alarm_time,
          autostart = true,
          callback = function()
            require("naughty").notification {
              app_name = "Task Alarm",
              title = task.summary,
              message = task.description,
              urgency = "normal",
              timeout = 5,
              icon = icondir .. "/alarm.png",
            }
          end
        } ]]
        end
        table.insert(tasks, task)
      end
    end
  end

  return tasks
end

local tasks = create_tasks()

local selected_day = {
  year = date.year,
  month = date.month,
  day = date.day,
  col = 1,
  row = 1,
}

return function(s)
  -- The calendar grid
  local calendar_matrix = wibox.widget { layout = wibox.layout.grid, spacing = dpi(2) }

  local weeks = wibox.widget { layout = wibox.layout.fixed.vertical }

  ---Main function to create the calendar widget
  ---Probably needs some refractor at some point since it's a bit messy
  ---@return wibox.widget calendar_widget
  local function create_calendar()

    calendar_matrix:reset()

    --- Months table holds every month with their starting week day, length(30/31 or 28/29), the last week day and the name
    local months = {}
    for m_num, month in ipairs(months_table) do
      months[m_num] = {
        name = month,
        first_day = get_date_weekday("01", m_num, date.year),
        length = get_last_day_of_month(m_num, date.year),
        last_day = get_date_weekday(get_last_day_of_month(m_num, date.year), m_num, date.year),
        weeks = get_weeks_in_month(m_num, date.year)
      }
    end

    local function get_tasks_for_day(day, month, year)
      if not tasks or #tasks == 0 then return end
      local tasks_layout = {
        layout = require("src.lib.overflow_widget.overflow").vertical,
        scrollbar_width = 0,
        step = dpi(50),
        spacing = dpi(2)
      }
      for _, task in ipairs(tasks) do
        if (task.date_start.year == year) and (task.date_start.month == month) and (task.date_start.day == day) then
          table.insert(tasks_layout, wibox.widget {
            {
              {
                text = task.summary,
                align = "left",
                halign = "center",
                font = "JetBrainsMono Nerd Font, bold 10",
                widget = wibox.widget.textbox
              },
              margins = dpi(2),
              widget = wibox.container.margin
            },
            fg = Theme_config.calendar.task.fg,
            bg = Theme_config.calendar.task.bg,
            shape = Theme_config.calendar.task.shape,
            forced_height = dpi(20),
            widget = wibox.container.background
          })
        end
      end
      return tasks_layout
    end

    if months[date.month].first_day ~= 1 then
      -- Fill previous month days, i doubles as the day
      local column = 1
      local last_month = get_last_month(date)
      local prev_month = date.month
      local prev_year = date.year
      if date.month == 1 then
        prev_month = 12
        last_month = months[12].length
      else
        last_month = months[date.month - 1].length
      end
      if date.month == 11 then
        prev_year = date.year - 1
      end
      prev_month = prev_month - 1
      for i = last_month - months[date.month].first_day + 2, last_month, 1 do
        local border = Theme_config.calendar.day.border_color
        local bg = Theme_config.calendar.day.bg_unfocus
        local fg = Theme_config.calendar.day.fg_unfocus
        if column == selected_day.col and 1 == selected_day.row then
          border = Theme_config.calendar.day.today_border_color
          bg = Theme_config.calendar.day.today_bg_focus
          fg = Theme_config.calendar.day.today_fg_focus
        end
        local y = tonumber(os.date("%Y"))
        local m = month_convert[tonumber(os.date("%m"))]
        if m == 1 then
          m = 12
        end
        if (i == date.day) and (m == prev_month) and (date.year == y) then
          bg = Theme_config.calendar.day.bg_focus
          fg = Theme_config.calendar.day.fg_focus
        end
        local day = wibox.widget {
          {
            {
              {
                {
                  {
                    {
                      { -- Day
                        widget = wibox.widget.textbox,
                        align = "center",
                        valign = "center",
                        text = math.floor(i),
                        id = "day_text",
                      },
                      widget = wibox.container.margin,
                      margins = dpi(2),
                    },
                    id = "day_bg",
                    widget = wibox.container.background,
                    bg = bg,
                    shape = Theme_config.calendar.day.shape,
                    fg = fg,
                  },
                  widget = wibox.container.place,
                  valign = "center",
                  halign = "center",
                },
                {
                  get_tasks_for_day(math.floor(i), prev_month, prev_year),
                  widget = wibox.container.margin,
                  margins = dpi(4)
                },
                id = "tasks",
                spacing = dpi(4),
                layout = wibox.layout.fixed.vertical
              },
              widget = wibox.container.margin,
              top = dpi(4)
            },
            id = "background",
            widget = wibox.container.background,
            bg = Theme_config.calendar.day.bg_unfocus,
            fg = Theme_config.calendar.day.fg_unfocus,
            border_color = border,
            border_width = Theme_config.calendar.day.border_width,
            shape = function(cr, width, height)
              gears.shape.rounded_rect(cr, width, height, dpi(8))
            end
          },
          widget = wibox.container.constraint,
          width = dpi(100),
          height = dpi(120),
          strategy = "exact"
        }

        -- update selected_day if the day is clicked
        day:buttons(
          gears.table.join(
            awful.button({}, 1, function()
              selected_day.col = column
              selected_day.row = 1
              day:emit_signal("day::update_selected")
            end)
          )
        )

        day:connect_signal("day::update_selected", function()
          if column == selected_day.col and 1 == selected_day.row then
            capi.awesome.emit_signal("day::reset_border")
            day.background.border_color = Theme_config.calendar.day.today_border_color
          end
        end)

        capi.awesome.connect_signal("day::reset_border", function()
          day.background.border_color = Theme_config.calendar.day.border_color
        end)

        calendar_matrix:add_widget_at(day, 1, column)
        column = column + 1
      end
    end

    --Actual month days
    local row = 1
    local col = months[date.month].first_day
    for i = 1, months[date.month].length, 1 do

      local border = Theme_config.calendar.day.border_color
      local fg = Theme_config.calendar.day.fg
      local bg = Theme_config.calendar.day.bg
      if col == selected_day.col and row == selected_day.row then
        border = Theme_config.calendar.day.today_border_color
      end

      local m = month_convert[tonumber(os.date("%m"))]
      local y = tonumber(os.date("%Y"))
      if (i == date.day) and (date.month == m) and (date.year == y) then
        bg = Theme_config.calendar.day.bg_focus
        fg = Theme_config.calendar.day.fg_focus
      end

      local day = wibox.widget {
        {
          {
            {
              {
                {
                  {
                    { -- Day
                      widget = wibox.widget.textbox,
                      align = "center",
                      valign = "center",
                      text = math.floor(i),
                      id = "day_text",
                    },
                    widget = wibox.container.margin,
                    margins = dpi(2),
                  },
                  id = "day_bg",
                  widget = wibox.container.background,
                  bg = bg,
                  shape = Theme_config.calendar.day.shape,
                  fg = fg,
                },
                widget = wibox.container.place,
                valign = "center",
                halign = "center",
              },
              {
                get_tasks_for_day(math.floor(i), date.month, date.year),
                widget = wibox.container.margin,
                margins = dpi(4)
              },
              id = "tasks",
              spacing = dpi(4),
              layout = wibox.layout.fixed.vertical
            },
            widget = wibox.container.margin,
            top = dpi(4)
          },
          id = "background",
          widget = wibox.container.background,
          bg = Theme_config.calendar.day.bg,
          fg = Theme_config.calendar.day.fg,
          border_color = border,
          border_width = Theme_config.calendar.day.border_width,
          shape = function(cr, width, height)
            gears.shape.rounded_rect(cr, width, height, dpi(8))
          end
        },
        widget = wibox.container.constraint,
        width = dpi(100),
        height = dpi(120),
        strategy = "exact"
      }

      -- update selected_day if the day is clicked
      day:buttons(
        gears.table.join(
          awful.button({}, 1, function()
            selected_day.col = col
            selected_day.row = row
            day:emit_signal("day::update_selected")
          end)
        )
      )

      day:connect_signal("day::update_selected", function()
        if col == selected_day.col and row == selected_day.row then
          capi.awesome.emit_signal("day::reset_border")
          day.background.border_color = Theme_config.calendar.day.today_border_color
        end
      end)

      capi.awesome.connect_signal("day::reset_border", function()
        day.background.border_color = Theme_config.calendar.day.border_color
      end)

      calendar_matrix:add_widget_at(day, row, col)
      col = col + 1
      if col == 8 then
        col = 1
        row = row + 1
      end
    end

    --next month
    local next_month = date.month
    if date.month == 12 then
      next_month = 1
    else
      next_month = next_month + 1
    end

    if months[date.month].last_day ~= 7 then
      for i = 1, 7 - months[date.month].last_day, 1 do
        local border = Theme_config.calendar.day.border_color
        local fg = Theme_config.calendar.day.fg_unfocus
        local bg = Theme_config.calendar.day.bg_unfocus
        if i == selected_day.col and months[date.month].weeks == selected_day.row then
          border = Theme_config.calendar.day.today_border_color
        end
        local m = month_convert[tonumber(os.date("%m")) + 1]
        if m == 13 then
          m = 1
        end
        local y = tonumber(os.date("%Y"))
        if (i == date.day) and (next_month == m) and (date.year == y) then
          bg = Theme_config.calendar.day.bg_focus
          fg = Theme_config.calendar.day.fg_focus
        end
        local day = wibox.widget {
          {
            {
              {
                {
                  {
                    {
                      { -- Day
                        widget = wibox.widget.textbox,
                        align = "center",
                        valign = "center",
                        text = math.floor(i),
                        id = "day_text",
                      },
                      widget = wibox.container.margin,
                      margins = dpi(2),
                    },
                    id = "day_bg",
                    widget = wibox.container.background,
                    bg = bg,
                    shape = Theme_config.calendar.day.shape,
                    fg = fg,
                  },
                  widget = wibox.container.place,
                  valign = "center",
                  halign = "center",
                },
                {
                  get_tasks_for_day(math.floor(i), next_month, date.year),
                  widget = wibox.container.margin,
                  margins = dpi(4)
                },
                id = "tasks",
                spacing = dpi(4),
                layout = wibox.layout.fixed.vertical
              },
              widget = wibox.container.margin,
              top = dpi(4)
            },
            id = "background",
            widget = wibox.container.background,
            bg = Theme_config.calendar.day.bg_unfocus,
            fg = Theme_config.calendar.day.fg_unfocus,
            border_color = border,
            border_width = Theme_config.calendar.day.border_width,
            shape = function(cr, width, height)
              gears.shape.rounded_rect(cr, width, height, dpi(8))
            end
          },
          widget = wibox.container.constraint,
          width = dpi(100),
          height = dpi(120),
          strategy = "exact"
        }

        -- update selected_day if the day is clicked
        day:buttons(
          gears.table.join(
            awful.button({}, 1, function()
              selected_day.col = i
              selected_day.row = months[date.month].weeks
              day:emit_signal("day::update_selected")
            end)
          )
        )

        day:connect_signal("day::update_selected", function()
          if i == selected_day.col and months[date.month].weeks == selected_day.row then
            capi.awesome.emit_signal("day::reset_border")
            day.background.border_color = Theme_config.calendar.day.today_border_color
          end
        end)

        capi.awesome.connect_signal("day::reset_border", function()
          day.background.border_color = Theme_config.calendar.day.border_color
        end)
        calendar_matrix:add_widget_at(day, months[date.month].weeks, months[date.month].last_day + i)
      end
    end

    return calendar_matrix
  end

  local function create_calendar_week_num()
    weeks:reset()
    local actual_fucking_date = date.month + 2
    if date.month == 11 then
      actual_fucking_date = 1
    elseif date.month == 12 then
      actual_fucking_date = 2
    end
    local start_week = actual_fucking_date * 4 - 3
    local weeknum = actual_fucking_date * 4 - 3
    if get_date_weekday("01", date.month, date.year) ~= 1 then
      weeknum = weeknum - 1
    end
    if actual_fucking_date == 1 then
      weeknum = 52
    end
    for i = start_week, start_week + get_weeks_in_month(date.month, date.year) - 1, 1 do
      weeks:add(wibox.widget {
        {
          {
            text = weeknum,
            id = "num",
            align = "center",
            valign = "top",
            widget = wibox.widget.textbox,
          },
          id = "background",
          fg = Theme_config.calendar.day.fg_unfocus,
          widget = wibox.container.background,
        },
        strategy = "exact",
        height = dpi(120),
        width = dpi(40),
        widget = wibox.container.constraint
      })
      if weeknum == 52 then
        weeknum = 1
      else
        weeknum = weeknum + 1
      end
    end
    return weeks
  end

  --- Calendar widget
  local calendar = wibox.widget {
    {
      {
        {
          {
            {
              {
                {
                  widget = wibox.widget.imagebox,
                  resize = false,
                  image = gears.color.recolor_image(icondir .. "add_ical.svg", Theme_config.calendar.add_ical.fg_focus),
                  halign = "center",
                  valign = "center"
                },
                id = "add_ical",
                shape = Theme_config.calendar.add_ical.shape,
                bg = Theme_config.calendar.add_ical.bg,
                widget = wibox.container.background
              },
              widget = wibox.container.margin,
              margins = dpi(4)
            },
            {
              {
                {
                  widget = wibox.widget.imagebox,
                  resize = false,
                  image = gears.color.recolor_image(icondir .. "add_task.svg", Theme_config.calendar.add_task.fg),
                  halign = "center",
                  valign = "center"
                },
                id = "add_task",
                shape = Theme_config.calendar.add_task.shape,
                bg = Theme_config.calendar.add_task.bg,
                widget = wibox.container.background
              },
              widget = wibox.container.margin,
              margins = dpi(4)
            },
            layout = wibox.layout.fixed.vertical
          },
          widget = wibox.container.constraint,
          strategy = "exact",
          height = dpi(75)
        },
        create_calendar_week_num(),
        id = "weekdaysnum",
        layout = wibox.layout.fixed.vertical
      },
      {
        {
          { --Header
            { -- Month switcher
              { -- Prev arrow
                widget = wibox.widget.imagebox,
                resize = true,
                image = icondir .. "chevron-left.svg",
                valign = "center",
                halign = "center",
                id = "prev_month",
              },
              {
                { -- Month
                  widget = wibox.widget.textbox,
                  text = months_table[date.month],
                  id = "month",
                  valign = "center",
                  align = "center"
                },
                widget = wibox.container.constraint,
                strategy = "exact",
                width = dpi(150)
              },
              { -- Next arrow
                widget = wibox.widget.imagebox,
                resize = true,
                image = icondir .. "chevron-right.svg",
                valign = "center",
                halign = "center",
                id = "next_month",
              },
              layout = wibox.layout.fixed.horizontal
            },
            nil,
            { -- Year switcher
              { -- Prev arrow
                widget = wibox.widget.imagebox,
                resize = true,
                image = icondir .. "chevron-left.svg",
                valign = "center",
                halign = "center",
                id = "prev_year"
              },
              {
                { -- Month
                  widget = wibox.widget.textbox,
                  text = date.year,
                  id = "year",
                  valign = "center",
                  align = "center"
                },
                widget = wibox.container.constraint,
                strategy = "exact",
                width = dpi(150)
              },
              { -- Next arrow
                widget = wibox.widget.imagebox,
                resize = true,
                image = icondir .. "chevron-right.svg",
                valign = "center",
                halign = "center",
                id = "next_year"
              },
              layout = wibox.layout.fixed.horizontal
            },
            layout = wibox.layout.align.horizontal
          },
          widget = wibox.container.constraint,
          height = dpi(40),
          strategy = "exact"
        },
        { -- Weekdays
          create_weekdays(),
          widget = wibox.container.background
        },
        create_calendar(),
        id = "calendar",
        spacing = dpi(5),
        layout = wibox.layout.fixed.vertical
      },
      id = "lay1",
      layout = wibox.layout.fixed.horizontal,
    },
    widget = wibox.container.background,
    bg = Theme_config.calendar.bg,
    border_color = Theme_config.calendar.border_color,
    border_width = Theme_config.calendar.border_width,
    border_strategy = "inner",
    fg = Theme_config.calendar.fg,
    shape = function(cr, width, height)
      gears.shape.rounded_rect(cr, width, height, dpi(4))
    end
  }

  local add_ical = calendar:get_children_by_id("add_ical")[1]
  local add_task = calendar:get_children_by_id("add_task")[1]

  add_ical:buttons(
    gears.table.join(
      awful.button({}, 1, function()
        awful.spawn.easy_async_with_shell(
          "zenity --file-selection --title='Select an iCalendar file' --file-filter='iCalendar File | *.ics'",
          function(path_to_file)
            path_to_file = string.gsub(path_to_file, "\n", "")
            if not path_to_file then return end
            ical_parser.new(path_to_file)
            tasks = create_tasks()
            calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num()
            calendar:get_children_by_id("calendar")[1].children[3] = create_calendar()
          end
        )
      end)
    )
  )

  Hover_signal(add_ical)
  Hover_signal(add_task)

  --- Popup that contains the calendar
  local cal_popup = awful.popup {
    widget = calendar,
    screen = s,
    ontop = true,
    bg = "#00000000",
    visible = false
  }

  --- Calendar switch month back
  calendar:get_children_by_id("prev_month")[1]:buttons(
    gears.table.join(
      awful.button({}, 1, function()
        date.month = date.month - 1
        if date.month == 0 then
          date.month = 12
        end
        if date.month == 10 then
          date.year = date.year - 1
        end
        calendar:get_children_by_id("month")[1].text = months_table[date.month]
        calendar:get_children_by_id("year")[1].text = date.year
        calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num()
        calendar:get_children_by_id("calendar")[1].children[3] = create_calendar()
      end)
    )
  )

  --- Calendar switch month forward
  calendar:get_children_by_id("next_month")[1]:buttons(
    gears.table.join(
      awful.button({}, 1, function()
        date.month = date.month + 1
        if date.month == 13 then
          date.month = 1
        end
        if date.month == 11 then
          date.year = date.year + 1
        end
        calendar:get_children_by_id("month")[1].text = months_table[date.month]
        calendar:get_children_by_id("year")[1].text = date.year
        calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num()
        calendar:get_children_by_id("calendar")[1].children[3] = create_calendar()
      end)
    )
  )

  --- Calendar switch year back
  calendar:get_children_by_id("prev_year")[1]:buttons(
    gears.table.join(
      awful.button({}, 1, function()
        date.year = date.year - 1
        calendar:get_children_by_id("year")[1].text = date.year
        calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num()
        calendar:get_children_by_id("calendar")[1].children[3] = create_calendar()
      end)
    )
  )

  --- Calendar switch year forward
  calendar:get_children_by_id("next_year")[1]:buttons(
    gears.table.join(
      awful.button({}, 1, function()
        date.year = date.year + 1
        calendar:get_children_by_id("year")[1].text = date.year
        calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num()
        calendar:get_children_by_id("calendar")[1].children[3] = create_calendar()
      end)
    )
  )

  --- Toggle calendar visibility
  capi.awesome.connect_signal("calendar::toggle", function(widget)
    if s == capi.mouse.screen then
      cal_popup.x = 3765
      cal_popup.y = 60
      cal_popup.visible = not cal_popup.visible
    end
  end)

end
