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

#include "BitmapFont.hpp"

using namespace Dawn;

void BitmapFont::buffer(
  std::string text,
  float_t fontSize,
  float_t maxWidth,
  Mesh *mesh,
  struct FontMeasure *info
) {
  assertNotNull(mesh);
  assertNotNull(info);
  assertTrue(maxWidth == -1 || maxWidth > 0);
  assertTrue(this->isReady());
  
  // Initialize primitive
  mesh->createBuffers(
    QUAD_VERTICE_COUNT * text.size(),
    QUAD_INDICE_COUNT * text.size()
  );

  // Setup Scales
  info->length = 0;
  info->realLength = 0;
  info->lines.clear();
  info->lineHeight = this->getLineHeight(fontSize);

  // Prepare the line counters
  info->addLine(0, 0);

  // Reset Dimensions
  char c;
  Tile tile;
  info->width = info->height = 0;
  float_t x = 0;
  float_t y = 0;
  size_t i = 0;
  size_t j = 0;
  size_t wordStart = 0;
  glm::vec2 xy0(0, 0);
  glm::vec2 tileSize =
    glm::vec2(tileset->getTileWidth(0), tileset->getTileHeight(0)) *
    (fontSize / this->getDefaultFontSize())
  ;

  // Buffer quads
  while(c = text[i++]) {
    if(c == FONT_SPACE) {
      wordStart = i;
      
      // Did this space cause a newline?
      if(maxWidth != -1 && xy0.x > maxWidth) {
        info->addLine(i, 0);
        info->width = mathMax<float_t>(info->width, xy0.x);
        xy0.x = 0;
        xy0.y += tileSize.y;
        info->height = mathMax<float_t>(info->height, xy0.y);
        continue;
      }

      xy0.x += tileSize.x;
      continue;
    }
    
    if(c == FONT_NEWLINE) {
      info->addLine(i, 0);
      info->width = mathMax<float_t>(info->width, xy0.x);
      xy0.x = 0;
      xy0.y += tileSize.y;
      info->height = mathMax<float_t>(info->height, xy0.y);
      wordStart = i;
      continue;
    }

    // Check for wrapping, todo.
    if(maxWidth != -1 && (xy0.x+tileSize.x) > maxWidth) {
      // We've exceeded the edge on THIS character. We first need to go back to
      // the start of the word and push it to the new line
      size_t charsToBeRewound = ((i - 1) - wordStart);

      info->width = mathMax<float_t>(
        info->width,
        (xy0.x - (tileSize.x * charsToBeRewound)) -
        (wordStart > 0 && text[wordStart - 1] == FONT_SPACE ? tileSize.x : 0)
      );

      xy0.x = 0;
      xy0.y += tileSize.y;

      j -= charsToBeRewound;// Rewind j back to wordStart.
      for(auto k = wordStart; k < (i-1); k++) {
        char c2 = text[k];
        tile = this->tileset->getTile(c2);
        QuadMesh::bufferQuadMesh(mesh,
          xy0, tile.uv0,
          xy0+tileSize, tile.uv1,
          j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT
        );
        xy0.x += tileSize.x;
        j++;
      }
    }

    tile = this->tileset->getTile(c);
    QuadMesh::bufferQuadMesh(mesh,
      xy0, tile.uv0,
      xy0+tileSize, tile.uv1,
      j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT
    );
    xy0.x += tileSize.x;
    j++;
  }

  info->width = mathMax<float_t>(info->width, xy0.x);
  info->height = mathMax<float_t>(info->height, xy0.y + (xy0.x > 0 ? tileSize.y : 0));
}

bool_t BitmapFont::isReady() {
  if(this->texture == nullptr) return false;
  if(this->tileset == nullptr) return false;
  return this->texture->isReady();
}

Texture * BitmapFont::getTexture() {
  return this->texture;
}

void BitmapFont::draw(Mesh *mesh, int32_t start, int32_t len) {
  assertNotNull(mesh);

  mesh->draw(
    MESH_DRAW_MODE_TRIANGLES,
    start * QUAD_INDICE_COUNT,
    len == -1 ? len : len * QUAD_INDICE_COUNT
  );
}

float_t BitmapFont::getLineHeight(float_t fontSize) {
  return tileset->getTileHeight(0);
}

float_t BitmapFont::getDefaultFontSize() {
  return tileset->getTileHeight(0);
}