//  BlockyFroggy
//  Copyright © 2017 John Ryland.
//  All rights reserved.
#include "GameState.h"
#include "Graphics.h"


REGISTER_GAME_STATE(GameState)


void GameState::enter(GameGraphics& graphics)
{
  graphics.resetVertexBuffers(m_sim.objectLists());
}


void GameState::exit(GameGraphics& /*graphics*/)
{
}


float GameState::getTime()
{
  return m_sim.getTime();
}


void GameState::draw(GameGraphics& graphics)
{
  if (m_needReset) {
    // reset the vertex buffers
    graphics.resetVertexBuffers(m_sim.objectLists());
    m_needReset = false;
    m_frame = 0;
  }
  graphics.draw();
}


// temp
void GameState::getPlayerPos(float pos[3])
{
  const GameSim::GameObject& player = m_sim.objectLists().back()->front();
  pos[0] = player.m_x;
  pos[1] = player.m_y;
  pos[2] = m_sim.getPlayerHeight();
}


void GameState::getCameras(float cameraStyle[8], float lightParams[8], float debugCameraParams[8], float elapsed)
{
  const GameSim::GameObject& player = m_sim.objectLists().back()->front();
  float xOff = -player.m_x + 20;
  float yOff = -player.m_y - 100;
  float zOff =   20.0;  // height of camera, smaller is higher
  
  // lerp camera pos towards the player's position (avoids jumpy camera movements, feels nicer)
  static float lastXOff = xOff;
  static float lastYOff = yOff;
  //const float cameraLerpSpeed = 10.0;
  xOff = Math::lerp(lastXOff, xOff, 0.05);//a_elapsed / cameraLerpSpeed);
  yOff = Math::lerp(lastYOff, yOff, 0.05);//a_elapsed / cameraLerpSpeed);
  lastXOff = xOff;
  lastYOff = yOff;
  
  //  Layout of camera style parameters:
  // float cameraStyle[8] = { fov, rx, ry, rz, s, tx, ty, tz };
  
  // Frogger mode (perspective devolved to ortho and with top down view)
  float debugCameraStyle[8]      = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0035f, -20.0f, 20.0f, 80.0f }; // Debug Style - 2D top down mode
  //  float topDownCameraStyle[8]    = { 1.0f, 0.0f, 0.0f, 0.0f, 0.02f, -20.0f, 20.0f, 80.0f }; // Frogger Style - 2D top down mode

  //float topDownCameraStyle[8]    = { 5.0f,  0.0f, 0.0f, 0.0f, 0.15f, -20.0f, 60.0f, 80.0f }; // Frogger Style - 2D top down mode
  float topDownCameraStyle[8]        = { 5.0f, -45.0f, 0.0f, -20.0f, 0.15f, 0.0f, 20.0f, 0.0f }; // Crossy Road Style - nearly isometric / orthographic

  float isoCameraStyle[8]        = { 5.0f, -45.0f, 0.0f, -20.0f, 0.15f, 0.0f, 20.0f, 0.0f }; // Crossy Road Style - nearly isometric / orthographic
  float perspectiveCameraStyle[8] = { 65.0f, -45.0f, 0.0f, -20.0f, 1.0f, 0.0f, 20.0f, 0.0f }; // Normal as designed style with perspective
  
  float* defaultCameraStyle = perspectiveCameraStyle;
  
  bool useIso = true;
  if (useIso)
    defaultCameraStyle = isoCameraStyle;
  
#if 0
  const int cameraTransitionFrames = 30; // Fast for quicker debugging
  const int cameraWaitFrames = 50; // Fast for quicker debugging
#else
  const int cameraTransitionFrames = 150; // Nice value
  const int cameraWaitFrames = 250;
#endif

  const int cwf = cameraWaitFrames;
  const int ctf = cameraTransitionFrames;
//  float cameraStyle[8];
  if (m_frame < cwf)
    for (int i = 0; i < 8; i++)
      cameraStyle[i] = topDownCameraStyle[i];
  else if (m_frame < cwf+ctf)
    for (int i = 0; i < 8; i++)
      cameraStyle[i] = Math::lerp(topDownCameraStyle[i], isoCameraStyle[i], (m_frame-cwf) / float(ctf));
  else if (m_frame < 2*cwf+ctf)
    for (int i = 0; i < 8; i++)
      cameraStyle[i] = isoCameraStyle[i];
  else if (m_frame < 2*(cwf+ctf))
    for (int i = 0; i < 8; i++)
      cameraStyle[i] = Math::lerp(isoCameraStyle[i], defaultCameraStyle[i], (m_frame-(2*cwf+ctf)) / float(ctf));
  else
    for (int i = 0; i < 8; i++)
      cameraStyle[i] = defaultCameraStyle[i];
  
  /*
   if (debugCameraView)
   for (int i = 0; i < 8; i++)
   cameraStyle[i] = debugCameraStyle[i];
   */
  
  /*
  m_polyUniforms.m_shadowStrength = float(m_frame) / (3*cwf+2*ctf);
  if (float(m_frame) >= (3*cwf+2*ctf))
    m_polyUniforms.m_shadowStrength = 1.0f;
  */
  
  m_frame++;
  
  
  cameraStyle[4] *= 0.01f;
  cameraStyle[5] += xOff;
  cameraStyle[6] += yOff;
  cameraStyle[7] += zOff;
  
  /*
  m_camera.build(cameraStyle[0], a_aspect, &cameraStyle[1], cameraStyle[4] * 0.01f, xOff + cameraStyle[5], yOff + cameraStyle[6], zOff + cameraStyle[7]);
   */
  
  /*
   Log(LL_Trace, "mvp[] = {");
   Log(LL_Trace, "   %f  %f  %f  %f", m_modelViewProjectionMatrix[0],  m_modelViewProjectionMatrix[1],  m_modelViewProjectionMatrix[2],  m_modelViewProjectionMatrix[3]);
   Log(LL_Trace, "   %f  %f  %f  %f", m_modelViewProjectionMatrix[4],  m_modelViewProjectionMatrix[5],  m_modelViewProjectionMatrix[6],  m_modelViewProjectionMatrix[7]);
   Log(LL_Trace, "   %f  %f  %f  %f", m_modelViewProjectionMatrix[8],  m_modelViewProjectionMatrix[9],  m_modelViewProjectionMatrix[10], m_modelViewProjectionMatrix[11]);
   Log(LL_Trace, "   %f  %f  %f  %f", m_modelViewProjectionMatrix[12], m_modelViewProjectionMatrix[13], m_modelViewProjectionMatrix[14], m_modelViewProjectionMatrix[15]);
   Log(LL_Trace, "};");
  */
  
  /*
  if (debugCameraView)
  {
    for (int i = 0; i < 8; i++)
      cameraStyle[i] = debugCameraStyle[i];
    m_debugCamera.build(cameraStyle[0], a_aspect, &cameraStyle[1], cameraStyle[4] * 0.01f, xOff + cameraStyle[5], yOff + cameraStyle[6], zOff + cameraStyle[7]);
  }
   */

  for (int i = 0; i < 8; i++)
    debugCameraParams[i] = debugCameraStyle[i];
  debugCameraParams[4] *= 0.01f;
  debugCameraParams[5] += xOff;
  debugCameraParams[6] += yOff;
  debugCameraParams[7] += zOff;
  
  //m_cameraX-20.0, m_cameraY+45.0, 20.0

  float lightTemplate[8] = { 1.0f, -65.0f, 0.0f, -25.0f, 0.025f, -20.0f, 35.0f, 0.0f };
  for (int i = 0; i < 8; i++)
    lightParams[i] = lightTemplate[i];
/*
  lightParams[0] = 1.0f;   // FOV
  lightParams[1] = -65.0f; // rotX
  lightParams[2] = 0.0f;  // rotY
  lightParams[3] = -25.0f; // rotZ
  lightParams[4] = 0.025;//175f;// scale
 */
  lightParams[5] += xOff;
  lightParams[6] += yOff + 40.0;
  lightParams[7] += zOff;
}


bool GameState::update(GameUi::UpdateState& state, float elapsed)
{
  bool ret = true;
  if (m_isRunning)
  {
    m_sim.update(elapsed);
    m_time += elapsed;
    if (m_sim.isGameOver())
    {
      ret = false;
      notifyPlayerGameOver();
      /*
      m_isRunning = false;
      if ( m_score > m_highScore )
      {
        m_highScore = m_score;
      }
      m_restartCountDown = 10;
      */
    }
  }
  else
  {
    if (m_restartCountDown)
    {
      m_restartCountDown--;
    }
  }

  state.m_variables["SCORE"]      = std::to_string(m_score);
  state.m_variables["HIGH_SCORE"] = std::to_string(m_highScore);
  return ret;
}


bool GameState::touchesChanged(int x, int y, TouchState ts)
{
  if (ts == TouchDown) {
    m_touchMoving = false;
    m_touchX = x;
    m_touchY = y;
  } else if (ts == TouchMove) {
    m_touchMoving = true;
  } else {
    if (m_isRunning)
    {
      if (m_touchMoving)
      {
        float dx = m_touchX - x;
        float dy = m_touchY - y;
        if (dx < 0) dx = -dx;
        if (dy < 0) dy = -dy;
        if (dx > dy) {
          if (dx > 15)
          {
            m_sim.movePlayer((x > m_touchX) ? GameSim::Right : GameSim::Left);
          }
        } else {
          if (dy > 15)
          {
            m_sim.movePlayer((y > m_touchY) ? GameSim::Backward : GameSim::Forward);
            if (m_nominalScore >= 100)
              m_nominalScore += (y > m_touchY) ? -100 : 100;
            if (m_nominalScore > m_score)
              m_score = m_nominalScore;
          }
        }
      } else {
        m_sim.movePlayer(GameSim::Forward);
        m_nominalScore += 100;
        if (m_nominalScore > m_score)
          m_score = m_nominalScore;
      }

      if (m_sim.isGameOver())
      {
        notifyPlayerGameOver();
      }
    }
    else
    {
      if (!m_restartCountDown)
      {
        m_nominalScore = 0;
        m_score = 0;
        m_sim.reset();
        m_needReset = true;
        m_isRunning = true;
        m_time = 0.0;
      }
    }
  }
  return false; 
}


void GameState::notifyPlayerGameOver()
{
  m_isRunning = false;

  // Must be inited, and assume must be atleast 320x320
  if ( m_score > m_highScore )
  {
    m_highScore = m_score;
  }

  // Need to change states, go to EndGameState / EndScreenState or something, perhaps the state object is looked up from game flow steps
  //m_ui.onGameOver();

  m_restartCountDown = 10;
}


