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

#include "font.h"

// Due to some compiler bullshit, this is here.
#ifndef  STB_TRUETYPE_IMPLEMENTATION
  #define STB_TRUETYPE_IMPLEMENTATION
  #include <stb_truetype.h>
#endif

void fontInit(font_t *font, char *data) {
  int32_t i, s;
  s = FONT_TEXTURE_WIDTH * FONT_TEXTURE_HEIGHT;

  uint8_t *bitmapData = malloc(sizeof(uint8_t) * s);
  pixel_t *pixels = malloc(sizeof(pixel_t) * s);

  // STBTT Loads fonts as single channel values only.
  stbtt_BakeFontBitmap(
    data, 0, FONT_TEXTURE_SIZE, bitmapData,
    FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT,
    FONT_FIRST_CHAR, FONT_NUM_CHARS,
    font->characterData
  );

  for(i = 0; i < FONT_TEXTURE_WIDTH * FONT_TEXTURE_HEIGHT; i++) {
    pixels[i].r = 0xFF;
    pixels[i].g = 0xFF;
    pixels[i].b = 0xFF;
    pixels[i].a = bitmapData[i];
  }
  textureInit(&font->texture, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, pixels);

  free(bitmapData);
  free(pixels);
}

void fontDispose(font_t *font) {
  textureDispose(&font->texture);
}

float fontGetScale(float fontSize) {
  return fontSize / FONT_SIZE_DEFAULT * FONT_GLOBAL_SCALE;
}

void fontTextBuffer(font_t *font, primitive_t *primitive, fonttextinfo_t *info){
  stbtt_aligned_quad *quad;
  int32_t i;

  for(i = 0; i < info->realLength; i++) {
    quad = info->quads + i;
    quadBuffer(primitive, 0,
      quad->x0, quad->y0, quad->s0, quad->t0,
      quad->x1, quad->y1, quad->s1, quad->t1,
      i * QUAD_VERTICE_COUNT, i * QUAD_INDICE_COUNT
    );
  }
}

void fontTextInit(font_t *font, primitive_t *primitive, fonttextinfo_t *info) {
  primitiveInit(primitive,
    QUAD_VERTICE_COUNT * info->realLength,
    QUAD_INDICE_COUNT * info->realLength
  );
  fontTextBuffer(font, primitive, info);
}

void fontTextClamp(font_t *font, fonttextinfo_t *info, char *text,
  float maxWidth, float fontSize
) {
  int32_t i, j;
  char c;
  float x, y, wordX, scale;
  stbtt_aligned_quad *quad;

  // Get the font scale
  scale = fontGetScale(fontSize);

  // Adjust the max width to match the scale, and allow "no max width".
  maxWidth = maxWidth == -1 ? 9999999 : maxWidth * (1 / scale);

  /** Which index in the original text var is the current word from */
  int32_t wordStart = 0;

  // Setup Scales
  info->length = 0;
  info->realLength = 0;
  info->lineCount = 0;

  // Prepare the line counters
  info->lines[0].start = 0;
  info->lines[0].length = 0;

  // Reset Dimensions
  info->width = 0, info->height = 0;
  
  // Setup the initial loop state, and X/Y coords for the quad.
  i = 0;
  x = 0;
  y = FONT_INITIAL_LINE;
  wordX = 0;
  while(c = text[i++]) {
    info->length++;

    // When space, start of new word about to begin
    if(c == FONT_SPACE) {
      x += FONT_SPACE_SIZE * scale;

      // Did this space cause a newline?
      if(x > maxWidth) {
        info->lineCount++;
        info->lines[info->lineCount].start = info->realLength;
        info->lines[info->lineCount].length = 0;

        y += FONT_LINE_HEIGHT;
        x = 0;
      }
      wordX = x;
      wordStart = info->realLength;
      continue;
    }

    // New line.
    if(c == FONT_NEWLINE) {
      info->lineCount++;
      info->lines[info->lineCount].start = info->realLength;
      info->lines[info->lineCount].length = 0;

      wordStart = info->realLength;
      y += FONT_LINE_HEIGHT;
      x = 0;
      continue;
    }

    // Generate the quad.
    quad = info->quads + info->realLength;
    stbtt_GetBakedQuad(font->characterData,
      FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT,
      ((int32_t)c)-FONT_FIRST_CHAR, &x, &y, quad, FONT_FILL_MODE
    );
    
    // Now measure the width of the line (take the right side of that quad)
    // if(x > maxWidth) {
    if(quad->x1 > maxWidth) {
      // We've exceeded the edge, go back to the start of the word and newline.
      x = quad->x1 - wordX;
      for(j = wordStart; j <= info->realLength; j++) {
        quad = info->quads + j;
        quad->x0 -= wordX;
        quad->x1 -= wordX;
        quad->y0 += FONT_LINE_HEIGHT;
        quad->y1 += FONT_LINE_HEIGHT;
      }

      // Go back to the previous (still current) line and remove the chars
      info->lines[info->lineCount].length -= info->realLength - wordStart;

      // Next line begins with this word
      info->lineCount++;
      info->lines[info->lineCount].start = wordStart;
      info->lines[info->lineCount].length = info->realLength - wordStart;

      y += FONT_LINE_HEIGHT;
      wordX = 0;
    }

    info->lines[info->lineCount].length++;
    info->realLength++;
  }
  
  info->lineCount++;
  
  for(j = 0; j < info->realLength; j++) {
    quad = info->quads + j;

    // Scale the Quad
    if(scale != 1.0) {
      quad->x0 *= scale;
      quad->x1 *= scale;
      quad->y0 *= scale;
      quad->y1 *= scale;
    }

    // Update the dimensions.
    info->width = mathMax(info->width, quad->x1);
    info->height = mathMax(info->height, quad->y1);
  }
}

int32_t fontGetLineCharCount(fonttextinfo_t *info,int32_t start,int32_t count) {
  int32_t charCount, i, m;
  m = mathMin(start+count, info->lineCount);

  charCount = 0;
  for(i = start; i < m; i++) {
    charCount += info->lines[i].length;
  }

  return charCount;
}