local Event = require("gfx.event")

local Element = {
  x = 1,
  y = 1,
  width = 1,
  height = 1,
  bgColor = colors.black,
  fgColor = colors.white,
  window = nil,
  parent = nil,
  visible = true,
  dirty = true,
  id = "",
  onClick = nil
}
Element.__index = Element


function Element:new(o)
  local obj = o or {}

  setmetatable(obj, self)
  obj.__index = obj

  if obj.parent then
    obj:_reload()
  end

  return obj
end

function Element:draw()
  local dirty = self:_isDirty()
  if dirty then
    local win = self:_getWindow()
  -- Calling draw() without a valid window is a logical error
  ---@diagnostic disable-next-line: need-check-nil
    win.setCursorPos(1, 1)
  end
  self.dirty = false
  return dirty
end

function Element:getX()
  return self.x
end

function Element:getY()
  return self.y
end

function Element:setPos(x, y)
  if (x ~= nil and self.x ~= x) or (y ~= nil and self.y ~= y) then
    self:setDirty()
    self.x = x or self.x
    self.y = y or self.y

    self:_reload()
  end
end

function Element:setX(x)
  if self.x ~= x then
    self:setDirty()
    self.x = x
    self:_reload()
  end
end

function Element:setY(y)
  if self.y ~= y then
    self:setDirty()
    self.y = y
    self:_reload()
  end
end

function Element:setParent(parent)
  if self.parent ~= parent then
    self:setDirty()
    self.parent = parent
    self:_reload()
  end
end

function Element:setFgColor(color)
  if color ~= self.fgColor then
    self:setDirty()
    self.fgColor = color

    local win = self:_getWindow()
    if win ~= nil then
      win.setTextColor(color)
    end
  end
end

function Element:setBgColor(color)
  if color ~= self.bgColor then
    self:setDirty()
    self.bgColor = color

    local win = self:_getWindow()
    if win ~= nil then
      win.setBackgroundColor(color)
    end
  end
end

function Element:getFgColor()
  return self.fgColor
end

function Element:getBgColor()
  return self.bgColor
end

function Element:_getWindow()
  return self.window
end

function Element:_setWindow(window)
  self.window = window
end

function Element:setVisible(visible)
  self:_getWindow().setVisible(visible)
end

function Element:_isDirty()
  return self.dirty
end

function Element:resize(opts)
  if (opts.width ~= nil and self.x ~= opts.width) or (opts.height ~= nil and self.y ~= opts.height) then
    self:setDirty()
    self.width = opts.width or self.width
    self.height = opts.height or self.height
    self:_reload()
  end
end

function Element:setWidth(width)
  if width ~= self.width then
    self:setDirty()
    self.width = width
    self:_reload()
  end
end

function Element:setHeight(height)
  if height ~= self.height then
    self:setDirty()
    self.height = height
    self:_reload()
  end
end

function Element:getWidth()
  return self.width
end

function Element:getHeight()
  return self.height
end

function Element:getPos()
  return self.x, self.y
end

function Element:isVisible()
  return self.visible
end

function Element:setDirty(fullInvalidate)
  self.dirty = true
end

function Element:findById(id)
  if self:getId() == id then
    return self
  else
    return nil
  end
end

function Element:getId()
  return self.id
end

function Element:redraw()
  self:_getWindow().redraw()
end

function Element:handleEvent(evt)
  if Event.isClickEvent(evt) and Event.containsClick(self, evt) and self.onClick ~= nil then
    local x, y, source = Event.getClickParams(evt)
    return not not self.onClick(self, x, y, source)
  end
  return false
end

function Element:setOnClick(onClick)
  self.onClick = onClick
end

function Element:_reload()
  if self.parent ~= nil then
    local win = window.create(self.parent, self:getX(), self:getY(), self:getWidth(), self:getHeight(), self:isVisible())
    win.setBackgroundColor(self:getBgColor())
    win.setTextColor(self:getFgColor())
    self:_setWindow(win)
  end
end

return Element