/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     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: sgx/src/sgx/graphics/textureanimation.cpp

*/

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

CSGXTextureAnimationManager *CSGXTextureAnimationManager::m_pSingleton;


//
//
//
CSGXTextureAnimationManager *
CSGXTextureAnimationManager::get() {
	if (!m_pSingleton) {
		m_pSingleton = new CSGXTextureAnimationManager();
		m_pSingleton->initialize();
	}
	return m_pSingleton;
}

CSGXTextureAnimationData *
CSGXTextureAnimationManager::registerScenarioAnimation(const sgxString &filename) {
CSGXTextureAnimationData *p = loadData(filename);
	p && registerScenarioObject(p);
	return p;
}

CSGXTextureAnimationData *
CSGXTextureAnimationManager::registerGlobalAnimation(const sgxString &filename) {
CSGXTextureAnimationData *p = loadData(filename);
	p && registerGlobalObject(p);
	return p;
}


//
//
//
tUINT32	
CSGXTextureAnimationSequence::addFrame(CSGXTextureAnimationFrame *pFrame) {
	m_Frames.push_back(pFrame);
	return m_Frames.size();
}

tUINT32	
CSGXTextureAnimationSequence::addFrame(const CSGXTextureAnimationFrame &frame) {
	return addFrame(new CSGXTextureAnimationFrame(frame));
}

CSGXTextureAnimationSequence::~CSGXTextureAnimationSequence() {
	sgxVector<CSGXTextureAnimationFrame *>::iterator it = m_Frames.begin();
	for(;it!=m_Frames.end();++it) {
		delete (*it);
	}

}

CSGXTextureAnimationData::~CSGXTextureAnimationData() {
	sgxVector<CSGXTextureAnimationSequence *>::iterator it = m_Sequences.begin();
	for(;it!=m_Sequences.end();++it) {
		delete (*it);
	}

}

CSGXTextureAnimationSequence *	
CSGXTextureAnimationData::addSequence(CSGXTextureAnimationSequence::tAnimationIterator runsas) {
	CSGXTextureAnimationSequence *pSequence = new CSGXTextureAnimationSequence(runsas);
	m_Sequences.push_back(pSequence);
	return pSequence;
}

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

CSGXTextureAnimationSequence *	
CSGXTextureAnimationData::getSequence(const sgxString &sequenceID) const{
	return getSequence(sgxGetCRC32(sequenceID));
}

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


//
//
//
CSGXTextureAnimationState::CSGXTextureAnimationState(const CSGXTextureAnimationSequence *pSequence, const tUINT32 iStartFrame)  {
	m_pAnimationData = NULL;
	startSequence(pSequence, iStartFrame);
}
void 
CSGXTextureAnimationState::setAnimationDataSet(CSGXTextureAnimationData *pData) {
	m_pAnimationData = pData;
}

void 
CSGXTextureAnimationState::startSequence(const sgxString &name, const tUINT32 iStartFrame) {
	if (m_pAnimationData) {
		startSequence(m_pAnimationData->getSequence(name), iStartFrame);
	}
}

void
CSGXTextureAnimationState::startSequence(const CSGXTextureAnimationSequence *pSequence, const tUINT32 iStartFrame)  {
	
	m_pCurrentSequence = pSequence;
	m_CurrentFrame = iStartFrame;
	m_CurrentDirection = CSGXTextureAnimationSequence::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 CSGXTextureAnimationSequence::eBackwards:
		case CSGXTextureAnimationSequence::eBackwardsAndLoop:
		case CSGXTextureAnimationSequence::ePingPongBackwards:
			m_CurrentFrame = pSequence->m_Frames.size();
			if (m_CurrentFrame) {
				--m_CurrentFrame;
			}
			m_CurrentDirection = CSGXTextureAnimationSequence::eBackwards;
			break;
	}
}

void CSGXTextureAnimationState::update(const tREAL32 telaps) {

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

	m_bExtent = FALSE;

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

			switch(m_pCurrentSequence->m_RunAs) {
				case CSGXTextureAnimationSequence::eForwards:
					if (m_CurrentFrame == lastFrame) {
						m_bExtent = TRUE;
					} else {
						++m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationSequence::eBackwards:
					if (m_CurrentFrame == 0) {
						m_bExtent = TRUE;
					} else {
						--m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationSequence::eForwardsAndLoop:
					if (m_CurrentFrame == lastFrame) {
						m_CurrentFrame = 0;
						m_bExtent = TRUE;
					} else {
						++m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationSequence::eBackwardsAndLoop:
					if (m_CurrentFrame == 0) {
						m_CurrentFrame = lastFrame;
						m_bExtent = TRUE;
					} else {
						--m_CurrentFrame;
					}
					break;
				case CSGXTextureAnimationSequence::ePingPongForwards:
				case CSGXTextureAnimationSequence::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 (m_CurrentDirection == CSGXTextureAnimationSequence::eForwards) {
						if (m_CurrentFrame == lastFrame) {
							m_CurrentFrame = lastFrame-1;
							m_CurrentDirection = CSGXTextureAnimationSequence::eBackwards;
							m_bExtent = m_pCurrentSequence->m_RunAs==CSGXTextureAnimationSequence::ePingPongBackwards?TRUE:FALSE;
						} else {
							++m_CurrentFrame;
						}
					} else {// backwards
						if (m_CurrentFrame == 0) {
							m_CurrentFrame = 1;
							m_CurrentDirection = CSGXTextureAnimationSequence::eForwards;
							m_bExtent = m_pCurrentSequence->m_RunAs==CSGXTextureAnimationSequence::ePingPongForwards?TRUE:FALSE;
						} else {
							--m_CurrentFrame;
						}
					}
					break;
			}

		}
	}
}


void
CSGXTextureAnimationState::playStaticCell(tUINT32 cell) {
	m_bIsRunning = FALSE;
	m_bStaticCell = TRUE;
	m_iCurrentStaticCell = cell;
}

tBOOL
CSGXTextureAnimationState::startAnimation(tBOOL running) {
	tBOOL wasRunning = m_bIsRunning;
	m_bIsRunning = running;
	m_bStaticCell = FALSE;
	return wasRunning;
}

tBOOL
CSGXTextureAnimationState::stopAnimation() {
	tBOOL wasRunning = m_bIsRunning;
	m_bIsRunning = FALSE;
	return wasRunning;
}

tUINT32
CSGXTextureAnimationState::getCurrentFrame() const {
	return m_CurrentFrame;
}

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


//
// 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 = pAnimData->addSequence((CSGXTextureAnimationSequence::tAnimationIterator)runas);

			pSeq->m_ID = sequenceID;

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

	return pAnimData;
}


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




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


