#include "RPGXEngine.h"

//constructor
RPGXEngine::RPGXEngine( Timing* newTimer, HINSTANCE newHInstance, HWND newHWnd )
{
	//initialize all member pointers to zero
	pd3dDevice = 0;
	pd3d = 0;
	timer = newTimer;
    textManager = new TextManager();
	textureManager = new TextureManager();
	spriteRenderer = new SpriteRenderer();
	mapManager = new MapManager();
	screenManager = new ScreenManager();
	keyboardManager = new KeyboardManager();
	characterManager = new CharacterManager();
	groundManager = new GroundManager();
	collisionManager = new CollisionManager();
	dialogueManager = new DialogueManager();
	eventManager = new EventManager();
	triggerManager = new TriggerManager();
    soundManager = new SoundManager();
    weatherManager = new WeatherManager();
	enemySpawner = new EnemySpawner();
	splashManager = NULL;
	hInstance = newHInstance;
	hWnd = newHWnd;
	tintManager = new TintManager();

	worldTickCount = 0;
    dayTimer = 0;

	readyToAttack = false;
    readyToMagic = false;
    hud = new HUD(textManager);
    mainCharHead = new SpriteStamp(70, new D3DXVECTOR3(10.0, 10.0, 1.0), 16, 16, 0);
}

RPGXEngine::~RPGXEngine() 
{
	//release everything and return all pointers to zero
	if ( pd3dDevice ) {
		GameError( "Killing D3D Device" );
		pd3dDevice->Release();
		pd3dDevice = 0;
	}
	if ( pd3d ){
		GameError( "Killing D3D COM Object" );
		pd3d->Release();
		pd3d = 0;
	}
	if ( textureManager ) {
        delete textureManager;
		textureManager = 0;
	}
	if ( spriteRenderer ) {
		delete spriteRenderer;
		spriteRenderer = 0;
	}
	if ( mapManager ) {
		delete mapManager;
		mapManager = 0;
	}
	if ( screenManager ) {
		delete screenManager;
		screenManager = 0;
	}
	if ( keyboardManager ) {
		keyboardManager->ShutdownInput();
		delete keyboardManager;
		keyboardManager = 0;
	}
	if ( dialogueManager ) { // must die before TextManager
		delete dialogueManager;
		dialogueManager = 0;
	}
    if ( textManager ) {
        delete textManager;
        textManager = 0;
    }
	if ( characterManager ) {
		delete characterManager;
		characterManager = 0;
	}
	if ( eventManager ) {
		delete eventManager;
		eventManager = 0;
	}
	if ( triggerManager ) {
		delete triggerManager;
		triggerManager = 0;
	}
	if ( collisionManager ) {
		delete collisionManager;
		collisionManager = 0;
	}
    if ( splashManager ) {
		delete splashManager;
		splashManager = 0;
	}
	if ( soundManager ) {
		delete soundManager;
		soundManager = NULL;
	}
    if(tintManager){
        delete tintManager;
        tintManager = 0;
    }
    if ( weatherManager ) {
        delete weatherManager;
        weatherManager = NULL;
    }
	if ( enemySpawner ) {
		delete enemySpawner;
		enemySpawner = NULL;
	}
    if (mainCharHead){
        delete mainCharHead;
        mainCharHead = 0;
    }
}

HRESULT RPGXEngine::setupD3D(HWND hWnd)
{
	pd3d = Direct3DCreate9( D3D_SDK_VERSION );
	if (pd3d == 0)
	{
		GameError("COULD NOT CREATE DX9 OBJECT");
		return E_FAIL;
	}

	//find out about current display
	HRESULT r = 0;
	D3DDISPLAYMODE d3ddm;
	r = pd3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
	if (FAILED(r))
	{
		GameError("Could Not Get Display Adapter Information");
		return E_FAIL;
	}

	// get window mode from user
	bool windowed = true;
	if ( MessageBox( NULL, "Create in fullscreen mode?", "", MB_YESNO ) == IDYES ) {
			windowed = false;
	}

	//create display parameters for device "creation"
    D3DFORMAT FullScreenFormat = D3DFMT_A8R8G8B8;

	ZeroMemory( &d3dPresentParameters, sizeof( D3DPRESENT_PARAMETERS ));
	//set to non-full screen
	d3dPresentParameters.Windowed = windowed;
	//tell DirectX that it is allowed to mess with the back buffer
	d3dPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
	//set the back buffer to the same format as the primary surface
	d3dPresentParameters.BackBufferFormat = windowed ? d3ddm.Format : FullScreenFormat;
	d3dPresentParameters.BackBufferWidth = ScreenManager::getWindowWidth();
	d3dPresentParameters.BackBufferHeight = ScreenManager::getWindowHeight();
	d3dPresentParameters.BackBufferCount = 1;
	//set target window to render to
	d3dPresentParameters.hDeviceWindow = hWnd;
	//turn on the depth buffer
	d3dPresentParameters.EnableAutoDepthStencil = TRUE;
	d3dPresentParameters.AutoDepthStencilFormat = D3DFMT_D16; //16bit depth buffer
    //set the refresh rate - not used if windowed
	d3dPresentParameters.FullScreen_RefreshRateInHz = windowed ? 0 : 60;
	//allow access to the back buffer directly for 2D work (slow...)
	d3dPresentParameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

	//get a handle to the device (watch out if you have two...)
	r = pd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
		D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dPresentParameters, &pd3dDevice);

    if (FAILED(r))
	{
		GameError("Could not create device...");
		return E_FAIL;
	}

	//---------setup Render States-----------
	//turn on Z Buffer
	pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

	//change it so it works
    pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATEREQUAL);

	// Turn off culling, so we see the front and back of the triangle
    pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

    // Turn off D3D lighting, since we are providing our own vertex colors
    pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

	// Turn on alpha
	pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, D3DZB_TRUE );

	//random attempt to do something with texturing
	pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);

	//clear the back buffer and then present
	pd3dDevice->Clear( 0, 0, D3DCLEAR_TARGET, D3DCOLOR_ARGB( 0, 0, 25, 50), 0.0f, 0);
	pd3dDevice->Present(NULL, NULL, NULL, NULL);

	// Now that direct3d is setup, we can initialize our game components
	return initGame();
}

HRESULT RPGXEngine::initGame() {
	TilePoints* aTile = collisionManager->getTilePoints( 1, 3 );
	HRESULT r = S_OK;

	// Load textures
	textureManager->setD3DDevice( pd3dDevice );

    r = textureManager->loadMasks( "files/masks/masks.txt" );
    if ( FAILED( r ) ) {
		GameError( "Error loading texture masks" );
		return E_FAIL;
	}

	r = textureManager->loadTexturesFromFile( "files/textures.txt" );
	if ( FAILED( r ) ) {
		GameError( "Error loading textures" );
		return E_FAIL;
	}

	// Initialize the screen manager
	screenManager->setMapManager( mapManager );
	screenManager->setSpriteRenderer( spriteRenderer );

	// Initialize the sprite renderer
	r = spriteRenderer->init( pd3dDevice, textureManager, screenManager );

	if ( FAILED( r ) ) {
		GameError( "Error loading sprite renderer" );
		return E_FAIL;
	}

    // Init weather manager
    weatherManager->init( spriteRenderer, screenManager );

	// Initialize ground render
	groundManager->init( spriteRenderer, screenManager );
	groundManager->loadGround( "files/levels/world" );

    // TextManager initialization
    textManager->init(spriteRenderer);
    textManager->loadAlphabet( 3, 512, 256, 16, 32, 32 );
    textManager->loadAlphabet( 4, 512, 256, 16, 32, 32 );
	textManager->loadAlphabet( 15, 512, 256, 16, 27, 38 );
    // set up hud display
    //hud->displayFrameRate();
    hud->displayCharInfo();

	// DialogueManager initialization
    dialogueManager->setSpriteRenderer(spriteRenderer);
    dialogueManager->setTextManager(textManager);
    dialogueManager->setScreenManager(screenManager);
    dialogueManager->setBackground(18,19,20);
    dialogueManager->initFileDialogue("text/dialogue.txt");

	// Initialize main character
    characterManager->init( groundManager, textManager, collisionManager, 
        screenManager, textureManager,  pd3dDevice, dialogueManager, triggerManager, soundManager);
	InitGame* initGame = new InitGame( -1 );
	initGame->init( characterManager, textManager, dialogueManager, triggerManager );
	initGame->activate( -1, -1 );
	mainCharacter = ( MainCharacter* ) characterManager->getMainCharacter();

	// Load the enemy spawner
	enemySpawner->init( characterManager, textManager, dialogueManager, screenManager );

	// Load the first map
	r = mapManager->loadMap( "files/levels/world" );
	
	if ( FAILED( r ) ) {
		GameError( "Error loading map file" );
		return E_FAIL;
	}

	triggerManager->init( collisionManager, eventManager );
	r = triggerManager->loadTriggers( "files/triggers.txt" );
	if ( FAILED(r) ) {
		GameError( "Error loading trigger file" );
		return E_FAIL;
	}

	// Start keyboard input checking
	keyboardManager->InitializeInput( hInstance );
	keyboardManager->Initialize( hWnd );

    // Init splash manager and load up initial splash
	splashManager = new SplashManager( keyboardManager, spriteRenderer );
	splashManager->loadSplash( 900, DIK_RETURN, NULL );
	splashManager->loadSplash( 24, DIK_RETURN, NULL );
	splashManager->loadSplash( 25, DIK_RETURN, NULL );

	CreateEnemy_Desert* creation = new CreateEnemy_Desert( 1, splashManager );
	creation->init( characterManager, textManager, dialogueManager, triggerManager, soundManager );
	eventManager->addEvent( 1, creation );
	ActivateTown* town = new ActivateTown( 2 );
	town->init( characterManager, textManager, dialogueManager, triggerManager, soundManager );
	eventManager->addEvent( 2, town );

	// Load up sound manager and start looping
    soundManager->loadSoundFromFile( "music/list.txt" );
	soundManager->playSound( 4, true );

	tintManager->init( spriteRenderer );
	tintManager->loadTint( 902 );

	return S_OK;
}

LPDIRECT3DDEVICE9& RPGXEngine::getDeviceHandle(){
	return pd3dDevice;
}

LPDIRECT3D9& RPGXEngine::getD3D(){
	return pd3d;
}

HRESULT RPGXEngine::render(){
    HRESULT r;
    // begin sprite
    r = spriteRenderer->beginSprite();
    if ( FAILED( r ) ) {
		return E_FAIL;
	}

	// Render the background tiles
	r = screenManager->render();
	//if ( FAILED( r ) ) {
	//	GameError( "Error rendering background tiles" );
	//	return r;
	//}

	// Render stuff on the ground

	groundManager->renderGround();

    // Render weather
    weatherManager->render();

	//Render tint
	tintManager->render();

	// Dialogue render - must be before text render
    dialogueManager->render();
	// Render text
    textManager->render();

    //character image
    spriteRenderer->renderSpriteToScreen(mainCharHead);

	// draw character bounding boxes cheaply
	//characterManager->drawBounds( pd3dDevice );

    // update hud
    hud->updateHP( characterManager->getMainCharacter()->getHP() );
    hud->updateMP( characterManager->getMainCharacter()->getMP() );
    hud->updateEXP( ((MainCharacter*)characterManager->getMainCharacter())->getExperience() );
    //hud->updateEXPNeededToLevel(((MainCharacter*)characterManager->getMainCharacter())->expNeededforNextLevel());
    hud->updateStrength(characterManager->getMainCharacter()->getStrength());
    hud->updateLevel(((MainCharacter*)characterManager->getMainCharacter())->getLevel());
	// change char x
	POINT* aPoint = new POINT();
	POINT* boundCenter = new POINT();
	RECT* oldBoundingBox = mainCharacter->getBoundingBox();
	boundCenter->x = ( oldBoundingBox->right - oldBoundingBox->left ) / 2 + mainCharacter->getX() + oldBoundingBox->left;
	boundCenter->y = ( oldBoundingBox->bottom - oldBoundingBox->top ) / 2 + mainCharacter->getY() + oldBoundingBox->top;
	aPoint->x = boundCenter->x;
	aPoint->y = boundCenter->y;
	TILE* aTile = screenManager->getTileFromWorldPoint( aPoint );
    
    //hud->updateFrameRate( timer->GetFrameRate() );
    //hud->updateMainTileX(aTile->x);
    //hud->updateMainTileY(aTile->y);
    
	if ( aPoint != NULL ) {
		delete aPoint;
		aPoint = NULL;
	}
	if ( boundCenter != NULL ) {
		delete boundCenter;
		boundCenter = NULL;
	}

    // end sprite
    r = spriteRenderer->endSprite();
    if ( FAILED( r ) ) {
		return E_FAIL;
	}
	return S_OK;
}

void RPGXEngine::gameLoop( bool tick ) {
	HRESULT r = S_OK;

	if ( mainCharacter->getState() == Character::DEAD ) {
		splashManager->loadSplash( 901, DIK_RETURN, new ExitAfterSplash() );
        while(mainCharacter->getState() != Character::STATE::CREATION){
            mainCharacter->popState();
        }
	}

    if ( !splashManager->isSplashDisplaying() ) {
	    // Query input
	    if ( getWindowFocus() ) {
		    keyboardManager->CheckState();
	    }

	    if ( keyboardManager->IsKeyDown( DIK_ESCAPE ) ) {
			if ( getWindowFocus() ) {
				PostQuitMessage( 0 );
			}
	    }

        if( !mainCharacter->getIsAttackReady() && keyboardManager->IsKeyDown( DIK_A )     && 
                              mainCharacter->getState() != Character::STATE::ATTACK       &&
                              mainCharacter->getState() != Character::STATE::ATTACKED     &&
                              mainCharacter->getState() != Character::STATE::ATTACK_READY &&
                              mainCharacter->getState() != Character::STATE::MAGIC        &&
                              mainCharacter->getState() != Character::STATE::CASTING      ){
            mainCharacter->setIsAttackReady(true);
            mainCharacter->pushState( Character::ATTACK_READY );
            readyToMagic = false;
		}
		if( mainCharacter->getIsAttacking() && keyboardManager->IsKeyUp( DIK_A ) &&
                     mainCharacter->getState() == Character::STATE::ATTACK_READY ){
            mainCharacter->pushState(Character::STATE::ATTACK);
		}

        if( mainCharacter->getDoneCasting() && keyboardManager->IsKeyDown( DIK_Q ) &&
                              mainCharacter->getState() != Character::MAGIC        &&
                              mainCharacter->getState() != Character::CASTING      &&
			                  mainCharacter->getState() != Character::ATTACK       &&
                              mainCharacter->getState() != Character::ATTACKED     &&
                              mainCharacter->getState() != Character::ATTACK_READY ){
            readyToMagic = true;
        }
        if( readyToMagic && keyboardManager->IsKeyUp( DIK_Q )        && 
                mainCharacter->getState() != Character::MAGIC        &&
                mainCharacter->getState() != Character::CASTING      &&
			    mainCharacter->getState() != Character::ATTACK       &&
                mainCharacter->getState() != Character::ATTACKED     &&
                mainCharacter->getState() != Character::ATTACK_READY ){
            readyToMagic = false;
            mainCharacter->pushState(Character::STATE::MAGIC);
		}
	    // Moves character based on input
        if ( keyboardManager->movement()                                 &&
             mainCharacter->getState() != Character::STATE::ATTACK       &&
             mainCharacter->getState() != Character::STATE::ATTACKED     &&
             mainCharacter->getState() != Character::STATE::ATTACK_READY &&
             mainCharacter->getState() != Character::STATE::MAGIC        &&
             mainCharacter->getState() != Character::STATE::CASTING      ){
            if(keyboardManager->movementUp()){
                mainCharacter->addMovement(GameCore::DIRECTION::UP);
            } else if (keyboardManager->movementUpRight()){
                mainCharacter->addMovement(GameCore::DIRECTION::RIGHT);
                mainCharacter->addMovement(GameCore::DIRECTION::UP);
            } else if (keyboardManager->movementRight()){
                mainCharacter->addMovement(GameCore::DIRECTION::RIGHT);
            } else if (keyboardManager->movementDownRight()){
                mainCharacter->addMovement(GameCore::DIRECTION::RIGHT);
                mainCharacter->addMovement(GameCore::DIRECTION::DOWN);
            } else if (keyboardManager->movementDown()){
                mainCharacter->addMovement(GameCore::DIRECTION::DOWN);
            } else if (keyboardManager->movementDownLeft()){
                mainCharacter->addMovement(GameCore::DIRECTION::LEFT);
                mainCharacter->addMovement(GameCore::DIRECTION::DOWN);
            } else if (keyboardManager->movementLeft()){
                mainCharacter->addMovement(GameCore::DIRECTION::LEFT);
            } else if (keyboardManager->movementUpLeft()){
                mainCharacter->addMovement(GameCore::DIRECTION::LEFT);
                mainCharacter->addMovement(GameCore::DIRECTION::UP);
            }

            if( mainCharacter->getState() != Character::WALKING){
                mainCharacter->setWalking(true);
                mainCharacter->pushState(Character::STATE::WALKING);
            }
        }
	    // Don't have him walk if he hasn't moved
        else if (mainCharacter->getWalking()){
            characterManager->stopMovingMainChar();
	    }

	    // tick stuff off
	    if ( tick ) {
		    characterManager->tick();
		    textManager->tick();
            weatherManager->tick();

			// spawn enemies if a certain amount of time has gone by
			if ( mainCharacter->getLocation() == MainCharacter::LOCATION::WORLD ) {
				worldTickCount++;

				if ( worldTickCount > 400 ) {
					enemySpawner->spawnEnemies();
					worldTickCount = 0;
				}
			} 

            if( dayTimer < 10 ) {
				dayTimer++;
			} else {
				tintManager->tick();
				dayTimer = 0;
			}
	    }

	    // Move screen if main char moved out of bounding box
	    if ( mainCharacter->getX() + mainCharacter->getBoundingBox()->left <= 
			    screenManager->getScreenX() + ScreenManager::getWindowBoundLeft() ) {
			    screenManager->moveScreenRight();
	    }
	    if ( mainCharacter->getX() + mainCharacter->getBoundingBox()->right >= 
			    screenManager->getScreenX() + ScreenManager::getWindowBoundRight() ) {
		    screenManager->moveScreenLeft();
	    }
	    if ( mainCharacter->getY() + mainCharacter->getBoundingBox()->bottom >= 
		    screenManager->getScreenY() + ScreenManager::getWindowBoundBottom() ) {
		    screenManager->moveScreenUp();
	    }
	    if ( mainCharacter->getY() + mainCharacter->getBoundingBox()->top <= 
			    screenManager->getScreenY() + ScreenManager::getWindowBoundTop() ) {
		    screenManager->moveScreenDown();
	    }

	    // Render everything
	    r = render();
	    if ( FAILED( r ) ) {
		    GameError( "Game Loop Render failed" );
		    return;
	    }
	} else {
		if ( FAILED( spriteRenderer->beginSprite() ) ) {
			GameError( "Could not begin sprite for splash." );
			return;
		}

		if ( FAILED( splashManager->render() ) ) {
			GameError( "Could not render splash!" );
			return;
		}

		if ( FAILED( spriteRenderer->endSprite() ) ) {
			GameError( "Could not end sprite for splash." );
			return;
		}

		// Check for escape
		if ( keyboardManager->IsKeyDown( DIK_ESCAPE ) ) {
				if ( getWindowFocus() )
				{
					PostQuitMessage( 0 );
				}
		}
	}
}

