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

#include "vntextbox.h"

void vnTextBoxInit(vntextbox_t *box, font_t *font, texture_t *texture) {
  box->font = font;
  box->widthMax = 400;
  box->text = NULL;
  box->x = 0, box->y = 0;
  box->linesMax = 3;
  box->lineCurrent = 0;
  box->state = 0;


  frameInit(&box->frame);
  box->frame.texture = texture;
}

void vnTextBoxSetText(vntextbox_t *box, char *text) {
  box->text = text;
  box->lineCurrent = 0;
  box->state = 0;
  box->textScroll = 0;
  vnTextBoxRebuffer(box);
}

void vnTextBoxRebuffer(vntextbox_t *box) {
  if(box->primitive.indiceCount > 0) {
    primitiveDispose(&box->primitive);
    box->primitive.indiceCount = 0;
  }

  if(box->text == NULL) return;

  // Determine size of the textbox inside the frame.
  float textMaxWidth = box->widthMax - FRAME_BORDER_SIZE_FULL;

  // Rebuffer the text.
  fontTextClamp(
    box->font, &box->textInfo, box->text, textMaxWidth, VN_TEXTBOX_FONT_SIZE
  );
  fontTextInit(box->font, &box->primitive, &box->textInfo);

  // Resize the frame
  frameSetSize(
    &box->frame, box->widthMax, FONT_LINE_HEIGHT * box->linesMax
  );
}

void vnTextBoxUpdate(vntextbox_t *box, engine_t *engine) {
  int32_t i;
  if(box->state & VN_TEXTBOX_STATE_CLOSED > 0) return;

  // "Next text box"
  if(!vnTextBoxHasScrolled(box)) {
    i = inputIsDown(&engine->input, INPUT_ACCEPT) ? 2 : 1;
    box->textScroll += engine->time.delta  * VN_TEXTBOX_SCROLL_SPEED * i;
    return;
  }

  if(!inputIsPressed(&engine->input, INPUT_ACCEPT)) return;

  if(vnTextBoxCanBeClosed(box)) {
    // Box can be closed.
    box->state |= VN_TEXTBOX_STATE_CLOSED;
    return;
  }

  // In theory this is always false
  if(box->linesMax < 1) return;

  // More lines to scroll
  box->lineCurrent += box->linesMax;
}

void vnTextBoxRender(vntextbox_t *box, shader_t *shader) {
  int32_t charStart, charCount;
  float yOffset;
  if(box->text == NULL || box->state & VN_TEXTBOX_STATE_CLOSED > 0) return;

  // Determine where we're rendering the indices up to.
  charCount = box->textScroll;
  if(charCount == 0) return;

  // Clamp to lines if necessary.
  if(box->linesMax > 0) {
    if(box->lineCurrent >= box->textInfo.lineCount) return;
    charStart = box->textInfo.lines[box->lineCurrent].start;
    charCount -= charStart;
    yOffset = FONT_LINE_HEIGHT * box->lineCurrent;
  } else {
    charStart = 0;
    yOffset = 0;
  }

  // Convert characters into indices, don't render if we can't
  charStart *= QUAD_INDICE_COUNT;
  if(charStart >= box->primitive.indiceCount) return;

  // Clamp
  charCount = mathMin(
    charCount * QUAD_INDICE_COUNT,
    box->primitive.indiceCount - charStart
  );
  if(charCount < 1) return;

  // Render the debug box.
  box->frame.x = box->x;
  box->frame.y = box->y - yOffset;
  frameRender(&box->frame, shader);
  
  // Render the Text Box
  shaderUsePosition(shader,
    box->x + FRAME_BORDER_SIZE, box->y - yOffset + FRAME_BORDER_SIZE, 0,
    0,0,0
  );
  shaderUseTexture(shader, &box->font->texture);
  primitiveDraw(&box->primitive, charStart, charCount);
}

void vnTextBoxDispose(vntextbox_t *box) {
  frameDispose(&box->frame);
  
  if(box->primitive.indiceCount > 0) {
    primitiveDispose(&box->primitive);
    box->primitive.indiceCount = 0;
  }
}

bool vnTextBoxHasScrolled(vntextbox_t *box) {
  int32_t currentChar;
  currentChar = box->textScroll;

  // Are we clamping lines?
  if(box->linesMax > 0) {
    return (
      currentChar - box->textInfo.lines[box->lineCurrent].start// "Line" space
    ) >= fontGetLineCharCount(
      &box->textInfo, box->lineCurrent, box->linesMax
    );
  }

  return currentChar > box->textInfo.realLength;
}

bool vnTextBoxCanBeClosed(vntextbox_t *box) {
  if(!vnTextBoxHasScrolled(box)) return false;
  if(box->linesMax < 1) return true;
  return box->lineCurrent + box->linesMax >= box->textInfo.lineCount;
}