//  DescriptionHere - game.cpp
//  Created by John Ryland (jryland@xiaofrog.com), 29/10/2017
//  Copyright (c) 2017 InvertedLogic
//  All rights reserved.
#include <cstdio>
#include <cstdlib>
#include "game.h"

bool createNewGame(Game& a_game, int a_width, int a_height, int a_numTileTypes)
{
  bool okay = false;
  if (a_width == 0 || a_height == 0 || a_numTileTypes == 0)
    okay = createBoard(a_game.m_board, 0, 0);
  else
    okay = generatePlayableBoard(a_game.m_board, a_width, a_height, a_numTileTypes);
  if (okay)
  {
    printBoard(a_game.m_board);
    printf("hasAMatch3 returned %s\n", hasAMatch3(a_game.m_board) ? "true" : "false");
    printf("hasMoveAvailable returned %s\n", hasMoveAvailable(a_game.m_board) ? "true" : "false");
    a_game.m_time = 0;
    a_game.m_score = 0;
    updateTiles(a_game);
  }
  if (!okay || !loadGameDB(a_game.m_gameDB))
    return false;
  return true;
}

void destroyGame(Game& a_game)
{
  destroyBoard(a_game.m_board);
}

bool processInput(Game& a_game, const Input& a_input)
{
  int x1 = a_input.startTileX;
  int y1 = a_input.startTileY;
  int x2 = a_input.endTileX;
  int y2 = a_input.endTileY;
  return swapTiles(a_game, x1, y1, x2, y2);
}

// normalize the game state
// directly translates the state of the board in to the tiles state so they are synced
void updateTiles(Game& a_game)
{
  a_game.m_boardTileMap.clear();
  a_game.m_tiles.clear();
  int idx = 0;
  for (int j = 0; j < a_game.height(); j++) {
    for (int i = 0; i < a_game.width(); i++) {
      Tile t;
      t.type = BoardEvent::None;
      t.userInit = false;
      t.newX = t.x = i;
      t.newY = t.y = j;
      t.tileTypeId = a_game.m_board.m_board[idx];
      a_game.m_tiles.push_back(t);
      a_game.m_boardTileMap.push_back(idx);
      idx++;
    }
  }
}

void addTile(Game& a_game, int idx, int x, int y, int value)
{
  BoardEvent added;
  added.type = BoardEvent::Added;
  added.itemIndex = idx;
  added.x = x;
  added.y = y;
  added.value = value;
  added.userInit = false;
  a_game.m_board.m_board[y*a_game.width() + x] = value;
  a_game.m_events.push_back(added);
}

void removeTile(Game& a_game, int idx, int x, int y)
{
  BoardEvent removed;
  removed.type = BoardEvent::Removed;
  removed.itemIndex = idx;
  removed.x = x;
  removed.y = y;
  removed.value = a_game.m_board.m_board[y*a_game.width() + x];
  removed.userInit = false;
  a_game.m_events.push_back(removed);
}

void moveTile(Game& a_game, int idx, int x, int y, int x2, int y2)
{
  BoardEvent moved;
  moved.type = BoardEvent::Moved;
  moved.itemIndex = idx;
  moved.x = x;
  moved.y = y;
  moved.newX = x2;
  moved.newY = y2;
  moved.value = a_game.m_board.m_board[y*a_game.width() + x];
  moved.userInit = false;
  a_game.m_events.push_back(moved);
}

void swapTile(Game& a_game, int idx, int idx2, int x1, int y1, int x2, int y2)
{
  BoardEvent swapped;
  swapped.type = BoardEvent::Swapped;
  swapped.itemIndex = idx;
  swapped.newItemIndex = idx2;
  swapped.x = x1;
  swapped.y = y1;
  swapped.newX = x2;
  swapped.newY = y2;
  swapped.value = a_game.m_board.m_board[y1*a_game.width() + x1];
  swapped.userInit = false;
  a_game.m_events.push_back(swapped);
}

void moveTilesDown(Game& a_game, int idx, int x, int y)
{
  for (int i = y; i >= 0; i--) {
    int idx = i * a_game.width() + x;
    moveTile(a_game, idx, x, i, x, i+1);
    a_game.m_board.m_board[(i+1)*a_game.width() + x] = a_game.m_board.m_board[i*a_game.width() + x];
  }
}

bool swapTiles(Game& a_game, int x1, int y1, int x2, int y2)
{
  Board& a_board = a_game.m_board;
  int w = a_game.width();
  int h = a_game.height();
  int newIdx = w*h;

  if (!canSwapTiles(a_board, x1, y1, x2, y2))
    return false;

  // This lets up work out the index in to the canvas items / tile list
  // TODO: needs to be used - perhaps the BoardEvent struct needs a field added

  // a_game.m_boardTileMap.push_back(idx);

  int idx = y1 * w + x1;
  int idx2 = y2 * w + x2;
  swapTile(a_game, idx, idx2, x1, y1, x2, y2);
  //swapTile(a_game, idx, x2, y2, x1, y1);

  // Actually swap them
  int tmp = a_board.m_board[y1*w + x1];
  a_board.m_board[y1*w + x1] = a_board.m_board[y2*w + x2];
  a_board.m_board[y2*w + x2] = tmp;

  bool hasMatches = true;
  while (hasMatches)
  {
    int x1, y1, x2, y2, x3, y3;
    hasMatches = getMatch3(a_board, x1, y1, x2, y2, x3, y3);
    if (hasMatches)
    {
      idx = y1 * w + x1;
      removeTile(a_game, idx, x1, y1);
      moveTilesDown(a_game, idx, x1, y1-1);
      addTile(a_game, newIdx++, x1, 0, rand() % 5);

      idx = y2 * w + x2;
      removeTile(a_game, idx, x2, y2);
      moveTilesDown(a_game, idx, x2, y2-1);
      addTile(a_game, newIdx++, x2, 0, rand() % 5);

      idx = y3 * w + x3;
      removeTile(a_game, idx, x3, y3);
      moveTilesDown(a_game, idx, x3, y3-1);
      addTile(a_game, newIdx++, x3, 0, rand() % 5);
    }

    // need to update mapping
  }

  if (!hasMoveAvailable(a_game.m_board))
  {
    BoardEvent noMoreMoves;
    noMoreMoves.type = BoardEvent::NoMoreMoves;
    noMoreMoves.userInit = false;
    a_game.m_events.push_back(noMoreMoves);
  }

  //delete[] remapper;
  return true;
}


