#include ".\Character.h"

Character::Character( CharacterManager* newCharacterManager, CollisionManager* newCollisionManager, TextManager* newText, DialogueManager* newDialogue, int tileX, int tileY, int trigger, int dialogue ){
	initCharacter( newCharacterManager, newCollisionManager, newText, newDialogue, tileX, tileY, trigger, dialogue );
}

Character::Character( CharacterManager* newCharacterManager, CollisionManager* newCollisionManager, TextManager* newText, DialogueManager* newDialogue, int tileX, int tileY, int dialogue ){
	initCharacter( newCharacterManager, newCollisionManager, newText, newDialogue, tileX, tileY, -1, dialogue);
}

Character::~Character(void){
	if ( tile ) {
		delete tile;
		tile = NULL;
	}
	if ( characterSprite ) {
		delete characterSprite;
		characterSprite = NULL;
	}
    SpriteState* spriteState;
    vector<SpriteState*>::iterator iter;
    for(iter = spriteStates.begin(); iter != spriteStates.end(); iter++){
        if(*iter){
            spriteState = (*iter);
            spriteStates.erase(iter--);
            if(spriteState->boundingBox){
                delete spriteState->boundingBox;
                spriteState->boundingBox = 0;
            }
            delete spriteState;
            spriteState = 0;
        }
    }
	//STATE state;
	while(!stateStack.empty()){
		stateStack.pop();
	}
}

void Character::initCharacter(CharacterManager* newCharacterManager, CollisionManager* newCollisionManager, TextManager* newText, DialogueManager* newDialogue, int tileX, int tileY, int trigger, int _dialogue){
	tile = new TILE();
	dialogue = _dialogue;
	facing = GameCore::DIRECTION::DOWN;
	yOffset = 0;

    characterManager = newCharacterManager;
	collisionManager = newCollisionManager;

	textManager = newText;
	dialogueManager = newDialogue;
	isWalking = false;
    isCasting = false;
    isDoneCasting = true;
    isAttacking = false;
    isAttackReady = false;
    clippingRect = new RECT();
    triggeredBy = trigger;

	facing = GameCore::DIRECTION::DOWN;

    path = new vector<POINT*>();

	alpha = 0;
	dirAttackedFrom = GameCore::DIRECTION::DOWN;
	toMove = 5;

    nextX = -1;
    nextY = -1;
    pushOrPop = false;
    ticksPassed = 0;
    spriteAnimateCount = 0;
    experience = 0;
    expEarnedForKilling = 0;
    animationTickCount = 0;

    aStar.setScreenManager(characterManager->getScreenManager());
}

void Character::setSpriteState( Character::STATE state ){
    currentSpriteState = getSpriteState(state);
}

bool Character::move( GameCore::DIRECTION aDirection, GameCore::DIRECTION clipDirection ) {
	isWalking = true;

	if ( aDirection == GameCore::DIRECTION::UP ) {
		setY( getY() - Y_INCREMENT );
	} else if ( aDirection == GameCore::DIRECTION::DOWN ) {
		setY( getY() + Y_INCREMENT );
	} else if ( aDirection == GameCore::DIRECTION::LEFT ) {
		setX( getX() - X_INCREMENT );
	} else if ( aDirection == GameCore::DIRECTION::RIGHT ) {
		setX( getX() + X_INCREMENT );
	}
	setFacing( clipDirection );
	return true;
}

bool Character::move( GameCore::DIRECTION aDirection ) {
    return move(aDirection, aDirection);
}

HRESULT Character::tick() {
    ticksPassed++;
	switch( stateStack.top() ) {
		case STATE::WALKING:
			walking();
			break;
		case STATE::ATTACK:
			attack();
			break;
		case STATE::ATTACK_READY:
			attackReady();
			break;
		case STATE::MAGIC:
			magic();
			break;
        case STATE::CASTING:
			casting();
			break;
		case STATE::ATTACKED:
			attacked();
			break;
		case STATE::CREATION:
			creation();
			break;
		case STATE::DEAD:
			dead();
			break;
		case STATE::DYING:
			dying();
			break;
		case STATE::IDLE:
			idle();
			break;
	}
	return S_OK;
}

void Character::setClip( GameCore::DIRECTION direction ) {
	int xOffset = ( int ) direction;

	int x = currentSpriteState->x;
	int clipWidth = currentSpriteState->width;

	clippingRect->left = x + ( xOffset * clipWidth );
	clippingRect->right = clippingRect->left + clipWidth - 1;

	facing = direction;
}

SpriteStamp* Character::getCharacterSpriteStamp() {
	return characterSprite;
}

SpriteStamp* Character::getCharacterHeadSpriteStamp() {
	return characterHeadSprite;
}

int Character::getX() {
	return ( int ) characterSprite->getPosition()->x;
}

int Character::getY() {
	return ( int ) characterSprite->getPosition()->y;
}

void Character::setX( int newX ) {
	D3DXVECTOR3* position = characterSprite->getPosition();
	position->x = ( float ) newX;
}

void Character::setY( int newY ) {
	D3DXVECTOR3* position = characterSprite->getPosition();
	position->y = ( float ) newY;
}

RECT* Character::getBoundingBox() {
	return currentSpriteState->boundingBox;
}

void Character::setWalking( bool walk ) {
	isWalking = walk;
}

bool Character::getWalking() {
	return isWalking;
}

void Character::setTextureId( int fullTextureId, int tileX, int tileY, int fullX, int fullY, RECT* boundingBox ) {
    RECT* copyOfBoundingBox = new RECT();

    copyOfBoundingBox->bottom = boundingBox->bottom;
    copyOfBoundingBox->left   = boundingBox->left;
    copyOfBoundingBox->right  = boundingBox->right;
    copyOfBoundingBox->top    = boundingBox->top;
	characterSprite = new SpriteStamp( fullTextureId, fullX, fullY, copyOfBoundingBox );
	clippingRect->top = 0;
	clippingRect->bottom = currentSpriteState->height - 1;
	characterSprite->setClippingRect( clippingRect );
}

void Character::setPosition( int tileX, int tileY, RECT* boundingBox ) {
	int boundingCenterX = (boundingBox->right - boundingBox->left)/2 + boundingBox->left;
	int boundingCenterY = (boundingBox->bottom - boundingBox->top)/2 + boundingBox->top;

	int x = tileX * TILE_WIDTH;
	int y = ( tileY / 2 ) * TILE_HEIGHT;;
	if ( tileY % 2 == 1 ) {
		x += ( TILE_WIDTH / 2 );
		y += ( TILE_HEIGHT / 2 );
	}
	x += ( TILE_WIDTH / 2 );
	y += ( TILE_HEIGHT / 2 );
	x -= boundingCenterX;
	y -= boundingCenterY;

	characterSprite->setPosition( new D3DXVECTOR3( ( float ) x, ( float ) y, 1.0 ) );
}

void Character::setHeadTextureId( int headTextureId ) {
    if(headTextureId){
        characterHeadSprite = new SpriteStamp(headTextureId, CHARACTER_HEAD_WIDTH, CHARACTER_HEAD_HEIGHT, NULL);
        characterHeadSprite->setPosition( new D3DXVECTOR3( 0.0, 0.0, 1.0 ) );
    }
    else{
        characterHeadSprite = 0;
    }
}

Character::STATE Character::getState() {
	return stateStack.top();
}

void Character::pushState( STATE newState ) {
    if(stateStack.size() > 0){
        SpriteState* newSpriteState = getSpriteState(newState);

	    int halfWidthOld  = currentSpriteState->width  / 2;
	    int halfHeightOld = currentSpriteState->height / 2;
        int halfWidthNew  = newSpriteState->width  / 2;
	    int halfHeightNew = newSpriteState->height / 2;

        if(pushOrPop){
            nextX -= ( halfWidthNew - halfWidthOld );
            nextY -= ( halfHeightNew - halfHeightOld );
        } else {
	        nextX = ( getX() - ( halfWidthNew - halfWidthOld ) );
	        nextY = ( getY() - ( halfHeightNew - halfHeightOld ) );
        }
    }
	stateStack.push(newState);
    setSpriteState( newState );
    yOffset = 0;
    pushOrPop = true;
}

void Character::popState(){
    if(stateStack.size() > 1){
        SpriteState* oldSpriteState = getSpriteState(stateStack.top());
        stateStack.pop();
        SpriteState* newSpriteState = getSpriteState(stateStack.top());
        setSpriteState( stateStack.top() );

	    int halfWidthOld  = oldSpriteState->width  / 2;
	    int halfHeightOld = oldSpriteState->height / 2;
        int halfWidthNew  = newSpriteState->width  / 2;
	    int halfHeightNew = newSpriteState->height / 2;

        if(pushOrPop){
            nextX -= ( halfWidthNew - halfWidthOld );
            nextY -= ( halfHeightNew - halfHeightOld );
        } else {
	        nextX = ( getX() - ( halfWidthNew - halfWidthOld ) );
	        nextY = ( getY() - ( halfHeightNew - halfHeightOld ) );
        }
        pushOrPop = true;
    } 
    yOffset = 0;
}

int Character::getHP() {
	return HP;
}

void Character::setHP(int newHP) {
	HP = newHP;
	if( getType() != MAIN ) {
		if( HP <= (HP_MAX * .25) ) {
			characterSprite->setModulationColor( D3DCOLOR_RGBA( 255, 0, 0, 255 ) );
		}
	}
}

int  Character::getMP(){
    return MP;
}
void Character::setMP( int newMP ){
    MP = newMP;
}

int Character::getTrigger() {
    return triggeredBy;
}

GameCore::DIRECTION Character::getFacing() {
	return facing;
}

void Character::setFacing( GameCore::DIRECTION aDirection ) {
	facing = aDirection;
	setClip( facing );
}

void Character::setAttackedFrom( GameCore::DIRECTION aDirection ){
    attackedFrom = aDirection;
    setClip( facing );
}

void Character::getPathTo(POINT* destination){
	TILE*  temp;
	POINT* origin;
    vector<POINT*>::iterator iter;
	// clean old solution

	origin = new POINT();
	origin->x = getX() + (getBoundingBox()->right - getBoundingBox()->left)/2 + getBoundingBox()->left;
	origin->y = getY() + (getBoundingBox()->bottom - getBoundingBox()->top)/2 + getBoundingBox()->top;
	temp = characterManager->getScreenManager()->getTileFromWorldPoint( origin );
	origin->x = temp->x;
	origin->y = temp->y;
	delete temp;
    aStar.run(origin, destination);
    path = aStar.getSolution();
//	characterManager->getPathAlgorithm()->run(origin, destination);
//	path = characterManager->getPathAlgorithm()->getSolution();
}

void Character::moveToNextPointInPath(){
	if(path->size() > 0){
        POINT* q = new POINT();
	    q->x = getX() + (getBoundingBox()->right - getBoundingBox()->left)/2 + getBoundingBox()->left;
	    q->y = getY() + (getBoundingBox()->bottom - getBoundingBox()->top)/2 + getBoundingBox()->top;
	    TILE* t = characterManager->getScreenManager()->getTileFromWorldPoint(q);
        TilePoints* tp = characterManager->getCollisionManager()->getTilePoints((*path->begin())->x, (*path->begin())->y);

        POINT* left  = tp->getLeft();
        POINT* right = tp->getRight();
        POINT* up    = tp->getTop();
        POINT* down  = tp->getBottom();
        int leftXDiff  = abs(left->x  - q->x);
        int leftYDiff  = abs(left->y  - q->y);
        int rightXDiff = abs(right->x - q->x);
        int rightYDiff = abs(right->y - q->y);
        int upXDiff    = abs(up->x    - q->x);
        int upYDiff    = abs(up->y    - q->y);
        int downXDiff  = abs(down->x  - q->x);
        int downYDiff  = abs(down->y  - q->y);

        int xDiff = t->x - (*path->begin())->x;
		int yDiff = t->y - (*path->begin())->y;
        if( (xDiff == 0) && (yDiff == 0)         ||
            (leftXDiff  < 20 && leftYDiff  < 20) ||
            (rightXDiff < 20 && rightYDiff < 20) ||
            (upXDiff    < 20 && upYDiff    < 20) ||
            (downXDiff  < 20 && downYDiff  < 20) ){
            path->erase(path->begin());
            xDiff = t->x - (*path->begin())->x;
		    yDiff = t->y - (*path->begin())->y;
        }
        delete t;
        if(xDiff < 0){
            characterManager->moveCharacter(this, GameCore::DIRECTION::RIGHT);
        } else if (xDiff > 0){
            characterManager->moveCharacter(this, GameCore::DIRECTION::LEFT);
        }
        if(yDiff < 0){
            characterManager->moveCharacter(this, GameCore::DIRECTION::DOWN);
        } else if (yDiff > 0){
            characterManager->moveCharacter(this, GameCore::DIRECTION::UP);
        }
	}
}

TILE* Character::getTile(){
	POINT* p = new POINT();

	p->x = getX() + (getBoundingBox()->right - getBoundingBox()->left)/2 + getBoundingBox()->left;
	p->y = getY() + (getBoundingBox()->bottom - getBoundingBox()->top)/2 + getBoundingBox()->top;
	tile = characterManager->getScreenManager()->getTileFromWorldPoint(p);
	return tile;
}

void Character::addSpriteState( int _x, int _y, int _numFrames, RECT* _boundingBox, int _width, int _height, Character::STATE _state){
    SpriteState* s = new SpriteState();

    s->x           = _x;
    s->y           = _y;
    s->numFrames   = _numFrames;
    s->boundingBox = _boundingBox;
    s->width       = _width;
    s->height      = _height;
    s->state       = _state;
    spriteStates.insert(spriteStates.begin(), s);
}

SpriteState* Character::getSpriteState( Character::STATE state ){
    SpriteState* result = 0;
    vector<SpriteState*>::iterator iter;

    iter = spriteStates.begin();
    while( (iter != spriteStates.end()) && ((*iter)->state != state) ){
        iter++;
    }
    if(iter != spriteStates.end()){
        result = (*iter);
    } else if( spriteStates.size() > 0 ){
        result = (*spriteStates.begin());
    } else {
        result = 0;
    }
    return result;
}

void Character::animateSprite(Character::SPEED slowDown){
    int value = 0;
    switch(slowDown){
        case Character::SPEED::NONE:
            value = 0;
            break;
        case Character::SPEED::NORMAL:
            value = 1;
            break;
        case Character::SPEED::HALF:
            value = 2;
            break;
        case Character::SPEED::THIRD:
            value = 3;
            break;
        case Character::SPEED::FOURTH:
            value = 4;
            break;
        case Character::SPEED::FIFTH:
            value = 5;
            break;
        case Character::SPEED::TENTH:
            value = 10;
            break;
        case Character::SPEED::TWENTIETH:
            value = 20;
            break;
        case Character::SPEED::HUNDREDTH:
            value = 100;
            break;
    }
    if((spriteAnimateCount % value) == 0  || (yOffset == 0)){
        // set position to correct animation
        if((yOffset == 0) && pushOrPop){
            if((nextX != -1) && (nextY != -1)){
                setX(nextX);
                setY(nextY);
            }
            spriteAnimateCount = 0;
            pushOrPop = false;
        }
        spriteAnimateCount = ( spriteAnimateCount + 1 ) % value;
        int numFrames = currentSpriteState->numFrames;

        if(yOffset < numFrames){
            int y = currentSpriteState->y;
            int clipHeight = currentSpriteState->height;
            
            
            clippingRect->top = y + ( yOffset * clipHeight );
            clippingRect->bottom = clippingRect->top + clipHeight - 1;
            setClip(facing);
        }
        yOffset++;
    } else {
        spriteAnimateCount++;
    }
}

void Character::setDoneCasting(bool done){
    isDoneCasting = done;
}

SpriteState* Character::getCurrentSpriteState(){
    return currentSpriteState;
}

void Character::getCurrentSpriteState(SpriteState* spriteState){
    currentSpriteState = spriteState;
}

bool Character::getIsAttacking(){
    return isAttacking;
}

void Character::setIsAttacking(bool attacking){
    isAttacking = attacking;
}

bool Character::getIsAttackReady(){
    return isAttackReady;
}

void Character::setIsAttackReady(bool attacking){
    isAttackReady = attacking;
}

int Character::getStrength(){
    return strength;
}

int Character::getExperience(){
    return experience;
}

void Character::addExperience(int _experience){
    experience += _experience;
}

bool Character::getDoneCasting(){
    return isDoneCasting;
}

void Character::setAttacker(Character* _attacker){
	attacker = _attacker;
}

void Character::takeDamage(){
    if(attacker){
        int damage = attacker->getStrength();
        HP -= damage;

	    char* damageText;
        damageText = (char*)malloc(sizeof(char)*(5+1));

	    itoa(damage,damageText,10);
        textManager->addString(getX(),getY()-TILE_HEIGHT,damageText,15,15,Text::TEXT_COORDINATES::WORLD,Text::TEXT_EFFECT::GRAVITY );
    }
}


void Character::faceMainCharacter(){
    Character* main = characterManager->getMainCharacter();

    int xDiff = getX() - main->getX();
    int yDiff = getY() - main->getY();
    if (abs(xDiff) > abs(yDiff)){
        if(xDiff > 0){
            setFacing(GameCore::LEFT);
        } else {
            setFacing(GameCore::RIGHT);
        }
    } else {
        if(yDiff > 0){
            setFacing(GameCore::UP);
        } else {
            setFacing(GameCore::DOWN);
        }
    }
}

void Character::setAttackee(Character* _attackee){
    attackee = _attackee;
}

void Character::setLocation( Character::LOCATION _location ) {
	location = _location;
}

Character::LOCATION Character::getLocation() {
	return location;
}

int Character::getHP_MAX() {
	return HP_MAX;
}

int Character::getMP_MAX() {
	return MP_MAX;
}
