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

#include "grid.h"

void gridInit(grid_t *grid) {
  uint8_t i;

  grid->columns = 1;
  grid->rows = 1;
  grid->gutterX = 0;
  grid->gutterY = 0;
  grid->borderX = 0;
  grid->borderY = 0;
  grid->width = 1;
  grid->height = 1;

  for(i = 0; i < GRID_COLUMN_COUNT_MAX; i++) {
    grid->columnDefinitions[i] = GRID_CEL_SIZE_AUTO;
  }
  for(i = 0; i < GRID_ROW_COUNT_MAX; i++) {
    grid->rowDefinitions[i] = GRID_CEL_SIZE_AUTO;
  }
}

void gridRecalculate(grid_t *grid) {
  uint8_t i, countFlexiColumns, countFlexiRows;
  float flexiWidth, flexiHeight, s, p, totalFlexiWidth, totalFlexiHeight;

  // Pass 1, determine the "flexible width size"
  countFlexiColumns = 0;
  countFlexiRows = 0;
  totalFlexiWidth = (
    grid->width -
    (grid->gutterX * mathMax(grid->columns-1, 0)) -
    (grid->borderX * 2.0f)
  );
  totalFlexiHeight = (
    grid->height -
    (grid->gutterY * mathMax(grid->rows-1, 0)) -
    (grid->borderY * 2.0f)
  );


  for(i = 0; i < grid->columns; i++) {
    if(grid->columnDefinitions[i] == GRID_CEL_SIZE_AUTO) {
      countFlexiColumns++;
    } else {
      totalFlexiWidth -= grid->columnDefinitions[i];
    }
  }

  for(i = 0; i < grid->rows; i++) {
    if(grid->rowDefinitions[i] == GRID_CEL_SIZE_AUTO) {
      countFlexiRows++;
    } else {
      totalFlexiHeight -= grid->rowDefinitions[i];
    }
  }

  // Determine the "flexi size for each flexi cell"
  flexiWidth = totalFlexiWidth / countFlexiColumns;
  flexiHeight = totalFlexiHeight / countFlexiRows;

  // Now set up the positions and sizes for each cell. 
  p = grid->borderX;
  for(i = 0; i < grid->columns; i++) {
    s = grid->columnDefinitions[i];
    if(s == GRID_CEL_SIZE_AUTO) s = flexiWidth;

    grid->columnPositions[i] = p;
    grid->columnSizes[i] = s;
    p += s + grid->gutterX;
  }

  p = grid->borderY;
  for(i = 0; i < grid->rows; i++) {
    s = grid->rowDefinitions[i];
    if(s == GRID_CEL_SIZE_AUTO) s = flexiHeight;

    grid->rowPositions[i] = p;
    grid->rowSizes[i] = s;
    p += s + grid->gutterY;
  }
}

void gridResize(grid_t *grid, float width, float height) {
  if(grid->width == width && grid->height == height) return;
  grid->width = width;
  grid->height = height;
  gridRecalculate(grid);
}

void gridGetChild(
  grid_t *grid, uint8_t col, uint8_t row, uint8_t columns, uint8_t rows,
  float *x, float *y, float *width, float *height
) {
  uint8_t i;
  float s;

  // Set positions
  *x = grid->columnPositions[col];
  *y = grid->rowPositions[row];

  // Calculate width.
  s = 0;
  for(i = col; i < col+columns; i++) {
    s += grid->columnSizes[i];
  }
  *width = s;

  // Calculate height
  s = 0;
  for(i = row; i < row+rows; i++) {
    s += grid->rowSizes[i];
  }
  *height = s;
}

align_t gridGetAndAlignChild(
  grid_t *grid, uint8_t col, uint8_t row, uint8_t columns, uint8_t rows,
  uint8_t alignX, uint8_t alignY, float width, float height
) {
  float gx, gy, gw, gh;

  // Get the size of the child first
  gridGetChild(grid, col, row, columns, rows, &gx, &gy, &gw, &gh);
  
  // Now align
  align_t align = alignmentGet(alignX, alignY, gw, gh, width, height, -1,-1);
  align.x += gx;
  align.y += gy;
  return align;
}