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

#include "vncharacter.h"


void vnCharacterInit(
  vncharacter_t *character, texture_t *texture,
  int32_t baseWidth, int32_t baseHeight,
  int32_t faceX, int32_t faceY,
  int32_t faceWidth, int32_t faceHeight
) {
  character->x = 0;
  character->y = 0;
  character->z = 0;

  character->yaw = 0;
  character->pitch = 0;
  character->roll = 0;

  character->scaleX = 1;
  character->scaleY = 1;

  character->texture = texture;
  character->talking = false;
  character->blinkStart = 0;

  character->baseWidth = baseWidth;
  character->baseHeight = baseHeight;
  character->faceX = faceX;
  character->faceY = faceY;
  character->faceWidth = faceWidth;
  character->faceHeight = faceHeight;

  character->emotion = VN_CHARACTER_EMOTION_HAPPY;

  // Init the primitive.
  primitiveInit(&character->primitive,
    QUAD_VERTICE_COUNT * VN_CHARACTER_QUAD_COUNT,
    QUAD_INDICE_COUNT * VN_CHARACTER_QUAD_COUNT
  );

  // Buffer the base quad, this never changes (currently)
  _vnCharacterBuffer(character, 
    0, 0, baseWidth, baseHeight, 0, 0, 0
  );
  _vnCharacterFaceBuffer(character, 0, VN_CHARACTER_QUAD_EYEBROWS);
  _vnCharacterFaceBuffer(character, 0, VN_CHARACTER_QUAD_EYES);
  _vnCharacterFaceBuffer(character, 0, VN_CHARACTER_QUAD_MOUTH);
}

void _vnCharacterBuffer(vncharacter_t *character,
  int32_t x, int32_t y, int32_t width, int32_t height, int32_t tx, int32_t ty,
  int32_t i
) {
  // Calc size for each pixel.
  float ps = 1.0f / (float)character->baseHeight;// Prefer Height
  float tpx = 1.0f / (float)character->texture->width;
  float tpy = 1.0f / (float)character->texture->height;

  // Center on the X axis
  x -= (float)character->baseWidth / 2.0f;

  // Put on the feet
  y -= character->baseHeight;

  quadBuffer(&character->primitive, 0.001f * (float)i,
    (float)x * ps, 1 - (float)y * ps,
    (float)tx * tpx, (float)ty * tpy,
    (float)(x + width) * ps, 1 - (float)(y + height) * ps,
    (float)(tx + width) * tpx, (float)(ty + height) * tpy,
    i * QUAD_VERTICE_COUNT, i * QUAD_INDICE_COUNT
  );
}

void _vnCharacterFaceBuffer(vncharacter_t *character,
  int32_t col, int32_t i
) {
  _vnCharacterBuffer(character, 
    character->faceX, character->faceY,
    character->faceWidth, character->faceHeight,
    character->baseWidth + (character->faceWidth * col),
    character->faceHeight * (i -1),
    i
  );
}

void vnCharacterUpdate(vncharacter_t *character, engine_t *engine) {
  uint8_t eyes, mouth, eyebrows;
  float t;

  // Setup frames based on the emotion.
  mouth = character->emotion % 0x04;
  eyes = (character->emotion/0x04) % 0x04;
  eyebrows = ((character->emotion/0x04)/0x04) % 0x05;

  _vnCharacterFaceBuffer(character, eyebrows, VN_CHARACTER_QUAD_EYEBROWS);
  _vnCharacterFaceBuffer(character, eyes, VN_CHARACTER_QUAD_EYES);

  mouth *= VN_CHARACTER_TALKING_FRAME_COUNT;
  if(character->talking) {
    t = animForwardAndBackwardScaled(mathModFloat(
      engine->time.current * animTimeScaleFromFrameTime(3, 0.2f), 1.0f
    ));
    mouth += (uint8_t)(t * VN_CHARACTER_TALKING_FRAME_COUNT);
  }
  _vnCharacterFaceBuffer(character, mouth, VN_CHARACTER_QUAD_MOUTH);





  // float n;
  // // Update the blinking frames
  // n = (engine->time.current - character->blinkStart) * 3.0f;
  // if(n > 1.0f) {
  //   character->blinkStart = engine->time.current + randFloatRange(
  //     1, VN_CHARACTER_BLINK_TIME_RANGE_MAX
  //   );
  // } else if(n > 0) {
  //   n = easeInQuad(easeTimeToForwardAndBackward(n) * 4);
  //   _vnCharacterFaceBuffer(character,
  //     (int32_t)n, 1, VN_CHARACTER_QUAD_EYES
  //   );
  // }
  // if(character->talking) {
  //   n = easeTimeToForwardAndBackward(
  //     mathModFloat(engine->time.current * 1.6, 1)
  //   ) * 6.0f;
  //   _vnCharacterFaceBuffer(character,
  //     (int32_t)n, 2, VN_CHARACTER_QUAD_MOUTH
  //   );
  // } else {
  //   _vnCharacterFaceBuffer(character,
  //     0, 2, VN_CHARACTER_QUAD_MOUTH
  //   );
  // }

  // Update the scale frames for breathing / talk breathing
  float speed, amount;
  speed = 0.2f;
  amount = 300.0f;
  t = animForwardAndBackwardScaled(
    mathModFloat(engine->time.current, 1 / speed) * speed
  );
  t = easeInOutQuad(t) / amount - (1/(amount*2));
  character->scaleX = 1 + t;
  character->scaleY = 1 - t;
}

void vnCharacterRender(vncharacter_t *character, shader_t *shader) {
  shaderUsePositionAndScale(shader,
    character->x, character->y, character->z,
    character->pitch, character->yaw, character->roll,
    character->scaleX, character->scaleY, 1
  );

  shaderUseTexture(shader, character->texture);
  primitiveDraw(&character->primitive, 0, -1);
}

void vnCharacterDispose(vncharacter_t *character) {
  primitiveDispose(&character->primitive);
}