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

#include "UILabel.hpp"

using namespace Dawn;

void UILabel::getSelfQuads(UICanvas &ctx) {
  std::vector<struct UIShaderQuad> quads;
  if(this->texture == nullptr || this->text.empty()) return;

  glm::vec4 quad;
  glm::vec2 pos = glm::vec2(0, this->texture->fontSize);
  bool_t lastCharWasSpace = false;

  for(size_t i = 0; i < text.size(); i++) {
    wchar_t c = text[i];
    auto info = texture->getCharacterData(c);

    // Newline(s)
    if(c == L'\n') {
      pos.x = 0;
      pos.y += this->texture->fontSize;
      continue;
    }

    // Spaces
    if(c == L' ') {
      pos.x += info.advance.x;
      lastCharWasSpace = true;
      continue;
    }

    // Word Wrap
    if(wordWrap) {
      if(lastCharWasSpace) {
        // Scan ahead to next space
        float_t wordWidth = pos.x;// Start at current position and scan ahead.
        for(size_t j = i; j < text.size(); j++) {
          wchar_t c2 = text[j];
          if(c2 == L' ' || c2 == L'\n') {
            break;// If we hit another space, we are OK.
          }

          // Will this character fit on the row? If not the whole word will wrap.
          auto info2 = texture->getCharacterData(c);
          wordWidth += info.advance.x;
          if(wordWidth > size.x) {
            pos.x = 0;
            pos.y += this->texture->fontSize;
            break;
          }
        }

        lastCharWasSpace = false;
      }
    // } else if(pos.x + info.size.x > subAlignedPosition.x + size.x) {
    //   // Not word wrap, but instead just overflow characters.
    //   pos.x = 0;
    //   pos.y += this->texture->fontSize;
    }

    ctx.addQuad(
      {
        subAlignedPosition.x + pos.x + info.offset.x,
        subAlignedPosition.y + pos.y + info.offset.y,
        subAlignedPosition.x + pos.x + info.size.x + info.offset.x,
        subAlignedPosition.y + pos.y + info.size.y + info.offset.y
      },
      {
        info.quad.x,
        info.quad.y,
        info.quad.z,
        info.quad.w
      },
      this->color,
      UIShaderQuadStyle::FONT,
      texture->texture
    );
    pos += info.advance;
  }
}

float_t UILabel::getContentWidth() {
  if(this->texture == nullptr || this->text.empty()) return 0.0f;

  float_t lineWidth = 0.0f;
  float_t width = 0.0f;
  for(wchar_t c : text) {
    if(c == L'\n') {
      width = Math::max<float_t>(width, lineWidth);
      lineWidth = 0.0f;
      continue;
    }

    auto info = texture->getCharacterData(c);
    lineWidth += info.advance.x;
    if(
      this->hasExplicitWidth() &&
      lineWidth >= size.x
    ) return size.x;
  }
  width = Math::max<float_t>(width, lineWidth);
  return width;
}

float_t UILabel::getContentHeight() {
  if(this->texture == nullptr || this->text.empty()) return 0.0f;

  float_t height = this->texture->fontSize;
  float_t lineWidth = 0.0f;
  bool_t lastCharWasSpace = false;

  for(wchar_t c : text) {
    if(c == L'\n') {
      height += this->texture->fontSize;
      continue;
    }

    auto info = texture->getCharacterData(c);

    if(c == L' ') {
      lineWidth += info.advance.x;
      lastCharWasSpace = true;
      continue;
    }

    if(wordWrap) {
      if(lastCharWasSpace) {
        // Scan ahead to next space
        float_t wordWidth = lineWidth;// Start at current position and scan ahead.
        for(size_t j = 0; j < text.size(); j++) {
          wchar_t c2 = text[j];
          if(c2 == L' ' || c2 == L'\n') {
            break;// If we hit another space, we are OK.
          }

          // Will this character fit on the row? If not the whole word will wrap.
          auto info2 = texture->getCharacterData(c);
          wordWidth += info.advance.x;
          if(wordWidth > size.x) {
            height += this->texture->fontSize;
            lineWidth = 0.0f;
            break;
          }
        }

        lastCharWasSpace = false;
      }
    // } else if(lineWidth + info.size.x > size.x) {
    //   height += this->texture->fontSize;
    //   lineWidth = 0.0f;
    }
  }

  return height;
}

std::shared_ptr<TrueTypeTexture> UILabel::getFont() {
  return this->texture;
}

std::wstring UILabel::getText() {
  return this->text;
}

void UILabel::setFont(std::shared_ptr<TrueTypeTexture> texture) {
  this->texture = texture;
}

void UILabel::setText(const std::wstring &text) {
  this->text = text;
}