-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.

local M = {  }

local highlight = require'lualine.highlight'

-- set upper or lower case
local function apply_case(status, options)
  -- Donn't work on components that emit vim statusline escaped chars
  if status:find('%%') and not status:find('%%%%') then return status end
  if options.upper == true then
    return status:upper()
  elseif options.lower == true then
    return status:lower()
  end
  return status
end

-- Adds spaces to left and right of a component
local function apply_padding(status, options)
  local l_padding = (options.left_padding  or options.padding or 1)
  local r_padding = (options.right_padding or options.padding or 1)
  if l_padding then
    if status:find('%%#.*#') == 1 then
      -- When component has changed the highlight at begining
      -- we will add the padding after the highlight
      local pre_highlight = vim.fn.matchlist(status, [[\(%#.\{-\}#\)]])[2]
      status = pre_highlight .. string.rep(' ', l_padding)..
        status:sub(#pre_highlight + 1, #status)
    else
      status = string.rep(' ', l_padding)..status
    end
  end
  if r_padding then status = status..string.rep(' ', r_padding) end
  return status
end

-- Applies custom highlights for component
local function apply_highlights(status, options)
  if options.color_highlight then
    status = highlight.component_format_highlight(options.color_highlight) .. status
  end
  return status
end

-- Apply icon in front of component
local function apply_icon(status, options)
  if options.icons_enabled and options.icon then
    status = options.icon .. ' ' .. status
  end
  return status
end

-- Apply separator at end of component only when
-- custom highlights haven't affected background
local function apply_spearator(status, options, highlight_name)
    local separator
    if options.separator and #options.separator > 0 then
      separator = options.separator
    elseif options.component_separators then
      if options.self.section < 'lualine_x' then separator = options.component_separators[1]
      else separator = options.component_separators[2] end
      if not separator then
        options.separator_applied = nil
        return status
      end
      options.separator = separator
    else
      options.separator_applied = nil
      return status
    end
    separator = highlight_name .. separator
    status = status .. separator
    options.separator_applied = separator
  return status
end

local function strip_separator(status, options)
  if options.separator_applied then
    status = status:sub(1, #status - #options.separator_applied)
    options.separator_applied = nil
  end
  return status
end

-- Returns formated string for a section
function M.draw_section(section, highlight_name)
  local status = {}
  local drawn_components = {}
  for _, component in pairs(section) do
    local localstatus = component[1]()
    if #localstatus > 0 then
      local custom_highlight_at_begining = localstatus:find('%%#.*#') == 1 or component.color ~= nil
      -- Apply modifier functions for options
      if component.format then localstatus = component.format(localstatus) end
      localstatus = apply_icon(localstatus, component)
      localstatus = apply_case(localstatus, component)
      localstatus = apply_padding(localstatus, component)
      localstatus = apply_highlights(localstatus, component)
      localstatus = apply_spearator(localstatus, component, highlight_name)
      if custom_highlight_at_begining or
        (#drawn_components > 0 and not drawn_components[#drawn_components].separator_applied)then
        -- Don't prepend with old highlight when the component changes it imidiately
        -- Or when it was already applied with separator
        table.insert(status, localstatus)
      else
        table.insert(status, highlight_name .. localstatus)
      end
      table.insert(drawn_components, component)
    end
  end
  -- Draw nothing when all the components were empty
  if #status == 0 then return '' end
  -- Remove separators sorounding custom highlighted component
  for i=1,#status do
    if (drawn_components[i].color and drawn_components[i].color.bg)
      or drawn_components[i].custom_highlight then
      status[i] = strip_separator(status[i], drawn_components[i])
      if i > 1 then
        status[i - 1] = strip_separator(status[i - 1], drawn_components[i - 1])
      end
    end
  end
  status[#status] = strip_separator(status[#status], drawn_components[#status])
  return table.concat(status)
end


return M