/** \file AnimationShader.cpp
 * \brief The implementation of the 'AnimationShader' class.
 */

#include <io.h>
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <string.h>
using namespace std;

#include "AnimationShader.h"

#include "Shader.h"
#include "BoneCooker.h"
#include "AnimationCooker.h"

#include <glew.h>
#include <GL/glut.h>

namespace OSkA
{
	AnimationShader::AnimationShader()
	{
		setGlobalTime(0);
		actualShader=NULL;
	}

	AnimationShader::~AnimationShader()
	{
	}

	void AnimationShader::setGlobalTime(float time)
	{
		this->globalTime=time;
	}

	void AnimationShader::addGlobalTime(float time)
	{
		this->globalTime +=time;
	}

	float AnimationShader::getGlobalTime()
	{
		return globalTime;
	}

	void AnimationShader::enable()
	{
		if(actualShader==NULL)return;

		actualShader->enable();
		glEnableVertexAttribArray(locWeights);
		glEnableVertexAttribArray(locInfluences);
	}

	void AnimationShader::disable()
	{
		if(actualShader==NULL)return;

		glEnableVertexAttribArray(locWeights);
		glEnableVertexAttribArray(locInfluences);
		actualShader->disable();
	}

	void AnimationShader::setActualShader(ShaderInterface* actualShader, bool useTexture)
	{
		this->actualShader=actualShader;

		locInfluences=actualShader->getAttributeLocation("oskaInfluences");
		locWeights=actualShader->getAttributeLocation("oskaWeights");

		if( useTexture )
		{
			locAnimation = actualShader->getUniformLocation("oskaAnimationTex");
			locTime1 = actualShader->getUniformLocation("oskaTime1");
			locTime2 = actualShader->getUniformLocation("oskaTime2");
			locTime1Factor = actualShader->getUniformLocation("oskaTime1Factor");
			locOffset = actualShader->getUniformLocation("oskaOffset");
		}
		else
		{
			locBones = actualShader->getUniformLocation("oskaBones");
		}
	}

	ShaderInterface* AnimationShader::getActualShader()
	{
		return this->actualShader;
	}

	void AnimationShader::setBoneAssignements(BoneCooker* boneAssignements)
	{
		if(actualShader==NULL)return;

		actualShader->setVertexAttribPointer(locInfluences, 4, GL_FLOAT, GL_FALSE, 0, boneAssignements->getInfluenceArray());
		actualShader->setVertexAttribPointer(locWeights, 4, GL_FLOAT, GL_FALSE, 0, boneAssignements->getWeightArray());
	}

	void AnimationShader::setAnimationTexture(AnimationCooker* animation, unsigned int texUnit)
	{
		if(actualShader==NULL)return;

		glActiveTexture(GL_TEXTURE0 + texUnit);
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, animation->getAnimationTextureId());
		actualShader->setUniform1i(locAnimation, texUnit);
	}

	void AnimationShader::setActiveAnimation(AnimationCooker* animation, int animationIndex)
	{
		int offset = animation->getBonesCount()*3*animationIndex;
		actualShader->setUniform1i(locOffset, offset);
	}

	void AnimationShader::setAnimationTime(AnimationCooker* animation, int animationIndex)
	{
		if(actualShader==NULL)return;

		float timeStep = 1.0f/(float)(animation->getTimeSamples()-1);
		float timeInAnim = animation->getTimeInAnimation(getGlobalTime(), animationIndex)/animation->getLength(animationIndex);
		float time = timeStep * (float)(int)(timeInAnim / timeStep);
		float factor = (time+timeStep-timeInAnim)/timeStep;

		int time1 = time*animation->getTimeSamples();
		int time2 = (time1+1)%animation->getTimeSamples();

		actualShader->setUniform1i(locTime1, time1);
		actualShader->setUniform1i(locTime2, time2);
		actualShader->setUniform1f(locTime1Factor, factor);
	}

	void AnimationShader::setAnimationUniform(AnimationCooker* animation, int animationIndex)
	{
		float* boneTransform=animation->getBoneTransform(this->getGlobalTime(), animationIndex);
		this->getActualShader()->setUniformMatrix4fv(locBones, animation->getBonesCount(), false, boneTransform);
		delete[] boneTransform;
	}
}