// Copyright (c) 2022 Dominic Masters
// 
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

#include "UIGrid.hpp"

using namespace Dawn;

UIGrid::UIGrid(UICanvas *canvas) : UIComponent(canvas) {

}

void UIGrid::updatePositions() {
  UIComponent::updatePositions();

  this->sizeCol = (
    this->width - (this->gutterX * (this->columns - 1))
  ) / this->columns;
  this->sizeRow = (
    this->height - (this->gutterY * (this->rows - 1))
  ) / this->rows;

  auto it = this->gridChildren.begin();
  while(it != this->gridChildren.end()) {
    this->alignChild(it->first, it->second);
    ++it;
  }
}

std::vector<struct ShaderPassItem> UIGrid::getSelfPassItems(
  glm::mat4 projection,
  glm::mat4 view,
  glm::mat4 transform
) {
  return std::vector<struct ShaderPassItem>();
}

void UIGrid::onChildAligned(UIComponent *child) {
  if(this->alignmentListenerLocked) return;
  assertNotNull(child);
  assertMapHasKey(this->gridChildren, child);
  this->alignmentListenerLocked = true;
  this->alignChild(child, this->gridChildren[child]);
  this->alignmentListenerLocked = false;
}

void UIGrid::alignChild(UIComponent *child, struct UIGridPosition pos) {
  assertNotNull(child);

  float_t gridX = (this->sizeCol * pos.x) + (this->gutterX * pos.x);
  float_t gridY = (this->sizeRow * pos.y) + (this->gutterY * pos.y);

  // Hack for when content width is undefined.
  child->width = this->sizeCol;
  child->height = this->sizeRow;

  // Alignment
  float_t x, y, sizeX, sizeY;
  UIComponent::calculateDimensions(
    pos.alignX,
    &x,
    &sizeX,
    this->sizeCol,
    child->getContentWidth(),
    glm::vec2(0, 0)
  );
  UIComponent::calculateDimensions(
    pos.alignY,
    &y,
    &sizeY,
    this->sizeRow,
    child->getContentHeight(),
    glm::vec2(0, 0)
  );

  child->setTransform(
    UI_COMPONENT_ALIGN_START, UI_COMPONENT_ALIGN_START,
    glm::vec4(gridX + x, gridY + y, sizeX, sizeY),
    0.0f
  );
}

void UIGrid::setGridSize(
  int32_t columns, int32_t rows,
  float_t gutterX, float_t gutterY
) {
  this->rows = rows;
  this->columns = columns;
  this->gutterX = gutterX;
  this->gutterY = gutterY;

  // TODO: Need to fix children here.

  this->gridChildren.clear();
  this->updatePositions();
}

void UIGrid::addToGrid(
  UIComponent *ui,
  int32_t x, int32_t y,
  enum UIComponentAlign alignX, enum UIComponentAlign alignY
) {
  assertTrue(x >= 0 && x < this->columns);
  assertTrue(y >= 0 && y < this->rows);
  this->addChild(ui);
  struct UIGridPosition pos;
  pos.x = x;
  pos.y = y;
  pos.alignX = alignX;
  pos.alignY = alignY;
  this->gridChildren[ui] = pos;
  this->alignChild(ui, pos);

  // Re-Add event listener
  ui->eventAlignmentUpdated.addListener(this, &UIGrid::onChildAligned);
}

int32_t UIGrid::getRows() {
  return this->rows;
}

int32_t UIGrid::getColumns() {
  return this->columns;
}

void UIGrid::removeChild(UIComponent *component) {
  UIComponent::removeChild(component);
  assertUnreachable();
}