/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2008.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: src/sgx/graphics/textureanimation.cpp

*/

#include "sgx/core/core.hpp"
#include "sgx/graphics/graphics.hpp"

CSGXTextureAnimationState::CSGXTextureAnimationState(const CSGXTextureAnimationSequence *pSequence, const tUINT32 iStartFrame) :
m_pCurrentSequence(pSequence),
m_CurrentFrame(iStartFrame),
m_CurrentDirection(eForwards),
m_bIsRunning(TRUE),
m_bStaticCell(FALSE),
m_fTimeCum(0) {

	if (pSequence == NULL) {
		m_bStaticCell = TRUE;
		m_iCurrentStaticCell = iStartFrame;
		return;
	}
	// Fixup the initial frame and direction
	switch(pSequence->m_RunAs) {
		case CSGXTextureAnimationManager::eBackwards:
		case CSGXTextureAnimationManager::eBackwardsAndLoop:
		case CSGXTextureAnimationManager::ePingPongBackwards:
			m_CurrentFrame = pSequence->m_Frames.size();
			if (m_CurrentFrame) {
				--m_CurrentFrame;
			}
			m_CurrentDirection = eBackwards;
			break;
	}
}


//
// Implementation - animation data
//
CSGXTextureAnimationData *		
CSGXTextureAnimationManager::loadData(const sgxString &filename) {

	CSGXTextureAnimationData *pAnimData = createData();
	CSGXFile file(filename+".dat");

	if (file.open()) {
		tUINT32 sequenceID;
		tBYTE runas;
		tUINT16 frameCount;

		while(!file.isEOF()) {
			file >> sequenceID >> runas >> frameCount;

			CSGXTextureAnimationSequence *pSeq = addSequence(pAnimData, (CSGXTextureAnimationManager::tAnimationIterator)runas);

			pSeq->m_ID = sequenceID;

			for(tUINT16 i=0;i<frameCount;++i) {
				tUINT32		region;
				tREAL32		duration;
				file >> region >> duration;
				addFrame(pSeq, CSGXTextureAnimationFrame(region, duration));
			}
		}
		file.close();
	}

	return pAnimData;
}


CSGXTextureAnimationData *
CSGXTextureAnimationManager::createData() {
	return new CSGXTextureAnimationData();
}

CSGXTextureAnimationSequence *	
CSGXTextureAnimationManager::getSequenceFromIndex(const CSGXTextureAnimationData *pData, const tUINT32 sequenceIndex) {
	if (sequenceIndex >= 0 && sequenceIndex < pData->m_Sequences.size()) {
		return pData->m_Sequences[sequenceIndex];
	}
	return NULL;
}

CSGXTextureAnimationSequence *	
CSGXTextureAnimationManager::getSequence(const CSGXTextureAnimationData *pData, const tUINT32 sequenceID) {
	for(tUINT32 i=0;i<pData->m_Sequences.size();++i) {
		if (pData->m_Sequences[i]->m_ID == sequenceID) {
			return pData->m_Sequences[i];
		}
	}
	return NULL;
}


CSGXTextureAnimationSequence *	
CSGXTextureAnimationManager::addSequence(CSGXTextureAnimationData *pData, CSGXTextureAnimationManager::tAnimationIterator runsas) {
	CSGXTextureAnimationSequence *pSequence = new CSGXTextureAnimationSequence(runsas);
	pData->m_Sequences.push_back(pSequence);
	return pSequence;
}


tUINT32	
CSGXTextureAnimationManager::addFrame(CSGXTextureAnimationSequence *pSequence, const CSGXTextureAnimationFrame &frame) {
	return addFrame(pSequence, new CSGXTextureAnimationFrame(frame));
}

tUINT32	
CSGXTextureAnimationManager::addFrame(CSGXTextureAnimationSequence *pSequence, CSGXTextureAnimationFrame *pFrame) {
	pSequence->m_Frames.push_back(pFrame);
	return pSequence->m_Frames.size();
}




//
// Implementation - animation state
//
CSGXTextureAnimationState *
CSGXTextureAnimationManager::createState(const CSGXTextureAnimationSequence *pSequence, const tUINT32 iStartFrame) {
	return new CSGXTextureAnimationState(pSequence, iStartFrame);
}

void
CSGXTextureAnimationManager::updateState(CSGXTextureAnimationState *pState, const tREAL32 telaps) {

	if (pState->m_bStaticCell) {
		return;
	}
	
	CSGXTextureAnimationFrame *pFrame = pState->m_pCurrentSequence->m_Frames[pState->m_CurrentFrame];

	if (pState->m_bIsRunning) {
		pState->m_fTimeCum += telaps;
		while (pState->m_fTimeCum > pFrame->m_Duration) {
			tUINT32 lastFrame = pState->m_pCurrentSequence->m_Frames.size();
			if (lastFrame) {
				--lastFrame;
			}
			
			pState->m_fTimeCum -= pFrame->m_Duration;

			switch(pState->m_pCurrentSequence->m_RunAs) {
				case CSGXTextureAnimationManager::eForwards:
					if (pState->m_CurrentFrame != lastFrame) {
						++pState->m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationManager::eBackwards:
					if (pState->m_CurrentFrame != 0) {
						--pState->m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationManager::eForwardsAndLoop:
					if (pState->m_CurrentFrame == lastFrame) {
						pState->m_CurrentFrame = 0;
					} else {
						++pState->m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationManager::eBackwardsAndLoop:
					if (pState->m_CurrentFrame == 0) {
						pState->m_CurrentFrame = lastFrame;
					} else {
						--pState->m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationManager::ePingPongForwards:
				case CSGXTextureAnimationManager::ePingPongBackwards:
					if (lastFrame == 0) {
						// NULL - if there's only one frame, we can't ping 
						// pong so we stop here. It makes the other code
						// simpler also, otherwise lastFrame-1 would be -1.
					} else if (pState->m_CurrentDirection == CSGXTextureAnimationState::eForwards) {
						if (pState->m_CurrentFrame == lastFrame) {
							pState->m_CurrentFrame = lastFrame-1;
							pState->m_CurrentDirection = CSGXTextureAnimationState::eBackwards;
						} else {
							++pState->m_CurrentFrame;
						}
					} else {// backwards
						if (pState->m_CurrentFrame == 0) {
							pState->m_CurrentFrame = 1;
							pState->m_CurrentDirection = CSGXTextureAnimationState::eForwards;
						} else {
							--pState->m_CurrentFrame;
						}
					}
					break;
			}

		}
	}
}

void
CSGXTextureAnimationManager::playStaticCell(CSGXTextureAnimationState *pState, tUINT32 cell) {
	pState->m_bIsRunning = FALSE;
	pState->m_bStaticCell = TRUE;
	pState->m_iCurrentStaticCell = cell;
}

tBOOL
CSGXTextureAnimationManager::startAnimation(CSGXTextureAnimationState *pState, tBOOL running) {
	tBOOL wasRunning = pState->m_bIsRunning;
	pState->m_bIsRunning = running;
	pState->m_bStaticCell = FALSE;
	return wasRunning;
}

tBOOL
CSGXTextureAnimationManager::stopAnimation(CSGXTextureAnimationState *pState) {
	tBOOL wasRunning = pState->m_bIsRunning;
	pState->m_bIsRunning = FALSE;
	return wasRunning;
}

tUINT32
CSGXTextureAnimationManager::getCurrentFrame(const CSGXTextureAnimationState *pState) {
	return pState->m_CurrentFrame;
}

tUINT32
CSGXTextureAnimationManager::getCurrentCell(const CSGXTextureAnimationState *pState) {
	if (pState->m_bStaticCell) {
		return pState->m_iCurrentStaticCell;
	}
	
	CSGXTextureAnimationFrame *pFrame = pState->m_pCurrentSequence->m_Frames[pState->m_CurrentFrame];
	return pFrame->m_Region;
}


