/*
 * Game.cpp
 *
 *  Created on: Feb 9, 2020
 *      Author: ayoungblood
 *      Special Thanks to: Carl Birch of Let's Make Games
 *      				   Nic Allen
 *      				   Brian Lhota
 */

#include "Game.hpp"
#include "../assetmgr/TextureManager.h"
// #include "../assetmgr/Map.h"
#include "../ecs/Components.h"
#include "Collision.h"
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include "../assetmgr/AssetManager.h"
// #include "../ui/UIText.h"
#include "../ui/UINineSlice.h"
#include "../cjson/cJSON.h"
#include "tmxparser.h"


// Map* map;
Manager manager;
UINineSlice* my9Slice;
UINineSlice* scoreboard9Slice;
UINineSlice* debugBox;

GameStateManager* Game::gsm = new GameStateManager();

SDL_Renderer* Game::renderer = nullptr;
SDL_Event Game::event;

SDL_Rect Game::camera;
SDL_Rect Game::levelMap;

AssetManager* Game::assets = new AssetManager(&manager);

bool Game::isRunning = false;
bool Game::debugMenu = false;

auto& player(manager.addEntity());
Vector2D Game::playerPosition;
Vector2D Game::pVel;

// auto& enemy(manager.addEntity());
auto& puppy(manager.addEntity());

// auto& scoreboard(manager.addEntity());
auto& uiInfo(manager.addEntity());
auto& uiJumpInfo(manager.addEntity());
auto& uiCamXInfo(manager.addEntity());
auto& uiCamYInfo(manager.addEntity());
auto& uiPlayerXInfo(manager.addEntity());
auto& uiPlayerYInfo(manager.addEntity());
auto& uiTextInstructions(manager.addEntity());

auto& gameScene(manager.addEntity());

bool Game::debugCollisionBoxes = false;
bool Game::gravityOnPlayer = true;
bool Game::playerIsGrounded = false;
bool Game::playerIsJumping = false;

int gScale = 0;
int last_time;
int current_time;
int diff_time;

std::string Game::BoolToString(bool b) {
    std::string myString;
    if (b) {
        myString = "true";
    } else {
        myString = "false";
    }
    return myString;
}

Game::Game() {
	// TODO Auto-generated constructor stub

}

Game::~Game() {
	// TODO Auto-generated destructor stub
}

void Game::init(const char *title, int width, int height, bool fullscreen, int globalScale)
{
    camera = { 0, 0, width, height };
    int flags = 0;
    gScale = globalScale;
    if(fullscreen)
    {
        flags = SDL_WINDOW_FULLSCREEN;
    }

    if(SDL_Init(SDL_INIT_EVERYTHING) == 0)
    {
        window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags);
        SDL_SetWindowBordered(window,SDL_FALSE);
        if(!window)
        {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Couldn't create window: %s", SDL_GetError());
        }
        renderer = SDL_CreateRenderer(window, -1, 0);
        if(!renderer)
        {
        	SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s", SDL_GetError());
        	isRunning = false;
        }
        else
        {
        	SDL_SetRenderDrawColor(renderer, 255,255,255,255);
            SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
        	isRunning = true;
        }
        //Initialize SDL_mixer
        if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048)<0)
        {
        	SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Couldn't initialize SDL audio mixer!, Error: %s", SDL_GetError());
        }

        
//  Trying out the halsafar/libtmx-parser
        tmxparser::TmxReturn error;
        tmxparser::TmxMap map;
        error = tmxparser::parseFromFile("assets/maps/testmapb64.tmx", &map, "assets/textures/tiles/");
//         current_time = SDL_GetTicks();
        if (!error)
        {
//             printf("Yay! Tile map loaded with no errors.\n");
            tmxparser::TmxLayer layer = map.layerCollection[0];
            levelMap.x = 0;
            levelMap.y = 0;
            levelMap.w = map.width*map.tileWidth*gScale;
            levelMap.h = map.height*map.tileHeight*gScale;
//             std::cout << "Map Width Tiles: " << map.width << " Map Tile Width: " << map.tileWidth <<std::endl;
        } else {
            std::cout << "Encountered error loading map file: " << error << std::endl;
        }
        
//         std::cout << "levelMap.w - camera.w: " << levelMap.w-camera.w << std::endl;
        
        assets->AddTexture("player", "assets/textures/actors/firefighter.png");
        assets->AddTexture("font", "assets/textures/ui/ui-font-cloud-sans.png");
        assets->AddTexture("textBox", "assets/textures/ui/ui-element-cloud.png");
        assets->AddTexture("puppy","assets/textures/actors/beaglepuppy.png");
        assets->AddTexture("collider","assets/textures/ColTex.png");
        
        assets->AddMusicTrack("simonZ","assets/audio/music/sillypuppy.ogg");
        assets->AddMusicTrack("simonZ","assets/audio/music/victory.ogg");
        
        assets->AddSoundClip("bwoop","assets/audio/sfx/bwoop.wav");
        assets->AddSoundClip("bark1","assets/audio/sfx/Bark1.wav");        
        assets->AddSoundClip("bark2","assets/audio/sfx/Bark2.wav");
        
//         map = new Map("terrain",globalScale,16);
//         std::string myText = "Find lost puppies!\nThey need your help!";
        std::string myText = "Press U to Start";
        
        uiTextInstructions.addComponent<TransformComponent>(18,22,138,20,gScale);
        uiTextInstructions.addComponent<UITextComponent>("font",myText,8,12,gScale);
        uiTextInstructions.addGroup(groupUI_Layer1);
        
        SDL_Rect myDestRect = SDL_Rect();
        myDestRect.x = 12;
        myDestRect.y = 8;
        myDestRect.w = 160;
        myDestRect.h = 40;
        my9Slice = new UINineSlice("textBox");
        my9Slice->MakeSlices("textBox",32,32,14,16,14,16,myDestRect,globalScale,Game::groupUI_Layer0);
        
        uiInfo.addComponent<TransformComponent>((camera.w-94)*gScale,10*gScale,72,96,gScale);
//         uiInfo.addComponent<UITextComponent>("font", "CollisionHori:    Vert:    Jump:    P.y :    P.dy:    YVec:    ", 8, 12, 1);
        uiInfo.addComponent<UITextComponent>("font", "Camera   CamX:    CamY:     P.x:    P.y :    P.dy:    YVec:    ", 8, 12, gScale);
        uiInfo.addGroup(groupUI_Layer3);  

        uiCamXInfo.addComponent<TransformComponent>((camera.w-48)*gScale,24*gScale,40,12,gScale);
        uiCamXInfo.addComponent<UITextComponent>("font", "nan", 8, 12, gScale);
        uiCamXInfo.addGroup(groupUI_Layer3);
        
        uiCamYInfo.addComponent<TransformComponent>((camera.w-48)*gScale,36*gScale,40,12,gScale);
        uiCamYInfo.addComponent<UITextComponent>("font", "nan", 8, 12, 1);
        uiCamYInfo.addGroup(groupUI_Layer3);
        
        uiPlayerXInfo.addComponent<TransformComponent>((camera.w-48)*gScale,48*gScale,40,12,gScale);
        uiPlayerXInfo.addComponent<UITextComponent>("font", "nan", 8, 12, 1);
        uiPlayerXInfo.addGroup(groupUI_Layer3);
          
        uiPlayerYInfo.addComponent<TransformComponent>((camera.w-48)*gScale,60*gScale,40,12,gScale);
        uiPlayerYInfo.addComponent<UITextComponent>("font", "nan", 8, 12, 1);
        uiPlayerYInfo.addGroup(groupUI_Layer3);
        
//         uiJumpInfo.addComponent<TransformComponent>(camera.w-48,48,40,12,gScale);
//         uiJumpInfo.addComponent<UITextComponent>("font", "false", 8, 12, 1);
//         uiJumpInfo.addGroup(groupUI_Layer3);     
        
        // debug UI box
        SDL_Rect debugBoxRect = SDL_Rect();
        debugBoxRect.x = camera.w-(100*gScale);
        debugBoxRect.y = 4*gScale;
        debugBoxRect.w = 98*gScale;
        debugBoxRect.h = 100*gScale;
        debugBox = new UINineSlice("textBox");
        debugBox->MakeSlices("textBox",32,32,14,16,14,16,debugBoxRect,1,Game::groupUI_Layer2);
        
        //ecs implementation

//         player.addComponent<TransformComponent>(860*globalScale,640*globalScale,22,42,globalScale);
        player.addComponent<TransformComponent>(150,100,22,42,globalScale); // 180,120
        player.addComponent<SpriteComponent>("player", SpriteComponent::spriteAnimation, "assets/textures/actors/firefighter.json");

//         player.addComponent<PlayerController>(0.0,0.0,false,false,Vector2D().Zero());
        player.addComponent<ColliderComponent>("player",16*globalScale,32*globalScale, true, 2*globalScale,10*globalScale, "collider");
        player.addComponent<KeyboardController>();
        player.addGroup(groupPlayers);
        
        gameScene.addComponent<TileMapComponent>(map,gScale,player.getComponent<TransformComponent>().position.x+player.getComponent<TransformComponent>().width/2,player.getComponent<TransformComponent>().position.y+player.getComponent<TransformComponent>().height/2); //150,100
        gameScene.addGroup(groupMap);
        
        playerPosition = Vector2D().Zero();
        pVel = Vector2D().Zero();
        
        puppy.addComponent<TransformComponent>(1024*globalScale,210*globalScale,36,30,globalScale);
        puppy.addComponent<SpriteComponent>("puppy", SpriteComponent::spriteObject);
        puppy.addGroup(groupObjects);
        
    } else {
    		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't Initialize SDL: %s", SDL_GetError());
            isRunning = false;
    }
}

// ====== Setup groups
auto& tiles(manager.getGroup(Game::groupMap));
auto& players(manager.getGroup(Game::groupPlayers));
auto& colliders(manager.getGroup(Game::groupColliders));
auto& objects(manager.getGroup(Game::groupObjects));
// auto& enemies(manager.getGroup(Game::groupEnemies));
// auto& projectiles(manager.getGroup(Game::groupProjectiles));
auto& gui(manager.getGroup(Game::groupUI_Layer0));
auto& uiText(manager.getGroup(Game::groupUI_Layer1));
auto& debugBG(manager.getGroup(Game::groupUI_Layer2));
auto& debugText(manager.getGroup(Game::groupUI_Layer3));

void Game::handleEvents()
{
    SDL_PollEvent(&event);
    switch (event.type)
    {
        case SDL_QUIT:
            isRunning = false;
            break;
        default:
            break;
    }

}

void Game::update()
{
	SDL_Rect playerCol = player.getComponent<ColliderComponent>().collider;
	Vector2D playerPos = player.getComponent<TransformComponent>().position;
    
//     if (gsm->currentState == GameStateManager::ST_INIT)
//     {
//         const char* initText = "Loading...";
//         text->ParseString(initText,12,22,gScale,"init");
//         const char* titleText = "Beagle Rescue";
//         const char* gameOverText = "Game Over";
//     }
    
	if (Mix_PlayingMusic() == 0 && gsm->currentState == GameStateManager::ST_COREGAME)
	{
//		std::cout << "Play Music Now" << std::endl;
// 		Mix_PlayMusic(assets->GetMusicTrack("simonZ"), -1);
	}
	if (Mix_PlayingMusic() != 0 && gsm->currentState != GameStateManager::ST_COREGAME)
    {
        Mix_HaltMusic();
    }
//     if (Mix_PlayChannel(-1, Game::assets->GetSoundClip("bark1"),0) == 0)
//     {
//         Mix_PlayChannel(-1, Game::assets->GetSoundClip("bark1"),0);
//     }

	manager.refresh();
	manager.update();

	for (auto& c : colliders)
	{
		SDL_Rect cCol = c->getComponent<ColliderComponent>().collider;
		if(Collision::AABB(cCol, playerCol))
		{
//             if(!playerIsGrounded){       
//                 player.getComponent<SpriteComponent>().Play("Idle");                
//             }
//             playerIsGrounded = true;
//             player.getComponent<TransformComponent>().position.y = player.getComponent<TransformComponent>().lastSafePos.y;
//             gravityOnPlayer = false;
		}

	}
	
// 	Gravity
//     if (gravityOnPlayer){
//     player.getComponent<TransformComponent>().position.y += 3*gScale;
//     uiJumpInfo.getComponent<UITextComponent>().updateString("true");
//     } else {
//     uiJumpInfo.getComponent<UITextComponent>().updateString("false");
//     }
// 	for(auto& p: projectiles)
// 	{
// 		if(Collision::AABB(player.getComponent<ColliderComponent>().collider, p->getComponent<ColliderComponent>().collider))
// 		{
// 			std::cout << "Projectile hit player" << std::endl;
// 			p->destroy();
// 		}
// 	}

	camera.x = player.getComponent<TransformComponent>().position.x - camera.w/2 + player.getComponent<TransformComponent>().width/2;
	camera.y = player.getComponent<TransformComponent>().position.y - camera.h/2 + player.getComponent<TransformComponent>().height/2;
    uiCamXInfo.getComponent<UITextComponent>().updateString(std::to_string(camera.x));
    uiCamYInfo.getComponent<UITextComponent>().updateString(std::to_string(camera.y));
    int playerX = player.getComponent<TransformComponent>().position.x;
    int playerY = player.getComponent<TransformComponent>().position.y;

    uiPlayerXInfo.getComponent<UITextComponent>().updateString(std::to_string(playerX));
    uiPlayerYInfo.getComponent<UITextComponent>().updateString(std::to_string(playerY));    
    

    playerPosition.x = playerX;
    playerPosition.y = playerY;
    
    pVel.x = player.getComponent<TransformComponent>().velocity.x;
    pVel.y = player.getComponent<TransformComponent>().velocity.y;
    
	if(camera.x < 0)
		camera.x = 0;
	if (camera.y < 0)
		camera.y = 0;
	if (camera.x > levelMap.w-camera.w)
		camera.x = levelMap.w-camera.w;
	if (camera.y > levelMap.h-camera.h)
		camera.y = levelMap.h-camera.h;
    if (Game::debugCollisionBoxes)
    {
        for (auto& c: colliders)
        {
            c->getComponent<ColliderComponent>().hidden = false;
        }
        for (auto& p: players)
        {
            p->getComponent<ColliderComponent>().hidden = false;
        }
    } else {
        for (auto& c: colliders)
        {
            c->getComponent<ColliderComponent>().hidden = true;
        }
        for (auto& p: players)
        {
            p->getComponent<ColliderComponent>().hidden = true;
        }
    }
}

void Game::render()
{
	SDL_RenderClear(renderer);
//     if (gsm->currentState==GameStateManager::ST_COREGAME)
//     {
//       printf("Core Game state\n");  
//     }
    for (auto& t : tiles)
	{
		t->draw();
	}

	for (auto& c : colliders)
	{
		c->draw();
	}

    for (auto& o : objects)
    {
        o->draw();
    }	
//     
// 	for (auto& e : enemies)
// 	{
// 		e->draw();
// 	}

	for (auto& p : players)
	{
		p->draw();
	}
    if (gsm->currentState==GameStateManager::ST_TITLESCREEN||gsm->currentState==GameStateManager::ST_INIT||gsm->currentState==GameStateManager::ST_GAMEOVER){
        for (auto& guiElement : gui)
        {
            guiElement->draw();
        }
        for (auto& text : uiText)
        {
            text->draw();
        }
    }
    if (debugMenu)
    {
        for (auto& guiElement : debugBG)
        {
            guiElement->draw();
        }
        for (auto& text : debugText)
        {
            text->draw();
        }
    }
    
    SDL_RenderPresent(renderer);
}

void Game::clean()
{
	SDL_DestroyWindow(window);
	SDL_DestroyRenderer(renderer);
	IMG_Quit();
	Mix_Quit();
	SDL_Quit();
	printf("Game Cleaned\n");
}

void Game::drawLine(Vector2D srcpt, Vector2D destpt, int red, int green, int blue)
{
    SDL_SetRenderDrawColor(renderer, red, green, blue, 200);
    SDL_RenderDrawLine(renderer, srcpt.x, srcpt.y, destpt.x, destpt.y);
}
