#ifndef LIGHTPRINTER_H
#define LIGHTPRINTER_H


#include "./math/Mathematics.h"
#include <vector>
#include <map>
#include <string>
#include "./illumination/tricol.h"
#include <iostream>

#include "./illumination/Light.h"


using namespace std;
using namespace ANALYTICAL_GEOMETRY;

template<typename STREAM>
class LightStream
{
protected:
	string lightTypeTag;
	string positionTag;
	string directionTag;
	string innerConeTag;
	string outerConeTag;
	string rectVec1Tag;
	string rectVec2Tag;
	string colorTag;
	string attenuationTag;
	string directionalTag;
	string intensityTag;
	string arrayDelimerTag;
	string arrayBeginTag;
	string arrayEndTag;
	string recordDelimerTag;
	string omniLightPhong;
	string spotLightPhong;
	string rectangleLightPhong;
	string lightDTIT;
	string size1Tag;
	string size2Tag;
	string keyLightTag;
	string ambientOcclusionTag;
	string indirectFactorTag;
	string shadowTag;

	void printColor( STREAM& stream, float *colorArr);
	void printAttenuation( STREAM& stream, float attenuationExp );
	void printDirectional( STREAM& stream, float directionalExp);

	void printIntensity( STREAM& stream, float intensity);
	void printPosition( STREAM& stream, float *positionArr);
	void printRectangleVectors( STREAM& stream, float *vec1Arr, float *vec2Arr );
	void printCones( STREAM& stream, float innerCone, float outerCone );
	void printDirection(STREAM& stream, float *directionArr);

	void printKeyLight( STREAM& stream, bool isKeyLight);
	void printSize1( STREAM& stream, float size1);
	void printSize2( STREAM& stream, float size2);

	void printAmbientOcclustion( STREAM& stream, bool useAmbientOcclustion);
	void printShadow( STREAM& stream, bool castShadow);
	void printIndirectFactor( STREAM& stream, float indirectFactor );
public:
	LightStream(void);
	void print(  STREAM& stream, Light& light );
	//friend STREAM& operator<<( STREAM& stream, Light& light );
};

template<typename STREAM> LightStream<STREAM>::LightStream(void)
{
	lightTypeTag= "Type:";
	positionTag= "Position:";
	directionTag= "Direction:";
	innerConeTag= "InnerCone:";
	outerConeTag="OuterCone:";
	rectVec1Tag="RectVec1:";
	rectVec2Tag="RectVec2:";
	colorTag="Color:";
	attenuationTag="Attenuation:";
	directionalTag="Directional:";
	intensityTag="Intensity:";
	arrayDelimerTag= ",";
	arrayBeginTag="[";
	arrayEndTag="]";
	recordDelimerTag="; ";
	omniLightPhong="Omnilight";
	spotLightPhong="Spotlight";
	rectangleLightPhong="Rectanglelight";
	lightDTIT = "LightDTIT";
	size1Tag= "Size1:";
	size2Tag= "Size2:";
	keyLightTag ="KeyLight:";
	ambientOcclusionTag = "AmbientOcclusion:";
	indirectFactorTag = "IndirectFactor:";
	shadowTag ="CastShadow:";
}
	
template<typename STREAM> void LightStream<STREAM>::printShadow( STREAM& stream, bool castShadow)
{
	stream<<shadowTag<<castShadow<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printAmbientOcclustion( STREAM& stream, bool useAmbientOcclustion)
{
	stream<<ambientOcclusionTag<<useAmbientOcclustion<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printIndirectFactor( STREAM& stream, float indirectFactor )
{
	stream<<indirectFactorTag<<indirectFactor<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printColor(STREAM& stream, float *colorArr)
{
	stream<<colorTag<<arrayBeginTag<<colorArr[0]<<arrayDelimerTag<< colorArr[1]<<arrayDelimerTag<< colorArr[2]<<arrayEndTag<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printAttenuation(STREAM& stream, float attenuationExp)
{
	stream<<attenuationTag<<attenuationExp<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printDirectional(STREAM& stream, float directionalExp)
{
	stream<<directionalTag<<directionalExp<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printDirection(STREAM& stream, float *directionArr)
{
	stream<<directionTag<<arrayBeginTag<<directionArr[0]<<arrayDelimerTag<< directionArr[1]<<arrayDelimerTag<< directionArr[2]<<arrayEndTag<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printIntensity(STREAM& stream, float intensity)
{
	stream<<intensityTag<<intensity<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printKeyLight( STREAM& stream, bool isKeyLight)
{
	stream<<keyLightTag<<isKeyLight<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printSize1( STREAM& stream, float size1)
{
	stream<<size1Tag<<size1<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printSize2( STREAM& stream, float size2)
{
	stream<<size2Tag<<size2<<recordDelimerTag;
}
template<typename STREAM> void LightStream<STREAM>::printPosition(STREAM& stream, float *positionArr)
{
	stream<<positionTag<<arrayBeginTag<<positionArr[0]<<arrayDelimerTag<< positionArr[1]<<arrayDelimerTag<< positionArr[2]<<arrayEndTag<<recordDelimerTag;
}

template<typename STREAM> void LightStream<STREAM>::printRectangleVectors(STREAM& stream, float *vec1Arr, float *vec2Arr )
{
	stream<<rectVec1Tag<<arrayBeginTag<<vec1Arr[0]<<arrayDelimerTag<< vec1Arr[1]<<arrayDelimerTag<<vec1Arr[2]<<arrayEndTag<<recordDelimerTag;
	stream<<rectVec2Tag<<arrayBeginTag<<vec2Arr[0]<<arrayDelimerTag<< vec2Arr[1]<<arrayDelimerTag<<vec2Arr[2]<<arrayEndTag<<recordDelimerTag;
}

template<typename STREAM> void LightStream<STREAM>::printCones(STREAM& stream, float innerCone, float outerCone )
{
	stream << innerConeTag << innerCone << recordDelimerTag;
	stream << outerConeTag << outerCone << recordDelimerTag;
}

template<typename STREAM> void LightStream<STREAM>::print(  STREAM& stream, Light& light )
{
	switch ( light.getLightType() )
	{
	case OMNI_PHONG:
	{
		OmniLightPhong* omni = (OmniLightPhong*) &light;

		stream<< lightTypeTag << omniLightPhong << recordDelimerTag;
		printPosition( stream, omni->position.getArrPtr() );
		printColor( stream, omni->color );
		printAttenuation( stream, omni->attenuationExp );
		printIntensity( stream, omni->intensity );
	}
	break;
	
	case SPOT_PHONG_VRML:
	{
		SpotLightPhongVRML* spot = (SpotLightPhongVRML*) &light;

		stream<< lightTypeTag << spotLightPhong << recordDelimerTag;
		printPosition( stream, spot->position.getArrPtr() );
		printColor( stream, spot->color );
		printAttenuation( stream, spot->attenuationExp );
		printIntensity( stream, spot->intensity );
		printDirection( stream, spot->direction.getArrPtr() );
		printCones( stream, spot->innerConeCos, spot->outerConeCos );
	}
	break;

	case SPOT_PHONG:
	{
		SpotLightPhong* spot = (SpotLightPhong*) &light;

		stream<< lightTypeTag << spotLightPhong << recordDelimerTag;
		printPosition( stream, spot->position.getArrPtr() );
		printColor( stream, spot->color );
		printAttenuation( stream, spot->attenuationExp );
		printDirectional(  stream, spot->directionalExp );
		printIntensity( stream, spot->intensity );
		printDirection( stream, spot->direction.getArrPtr() );
	}
	break;

	case RECTANGLE_PHONG:
	{
		RectangleLightPhong* rect = (RectangleLightPhong*) &light;

		stream<< lightTypeTag << rectangleLightPhong << recordDelimerTag;
		printPosition( stream, rect->position.getArrPtr() );
		printColor( stream, rect->color );
		printAttenuation( stream, rect->attenuationExp );
		printIntensity( stream, rect->intensity );
		printRectangleVectors( stream, rect->vec1.getArrPtr(), rect->vec2.getArrPtr() );
	}
	break;

	case DTIT_LIGHT:
	{
		DTITLight *lightDTIT =  (DTITLight*) &light;
	
		stream<< lightTypeTag << this->lightDTIT << recordDelimerTag;

		printPosition( stream, lightDTIT->position.getArrPtr() );
		printColor( stream, lightDTIT->color.data()  );
		printAttenuation( stream, lightDTIT->attenuation );
		printIntensity( stream, lightDTIT->intensity );
		printRectangleVectors( stream, lightDTIT->tangent.getArrPtr(), lightDTIT->bitangent.getArrPtr() );
		printDirection( stream, lightDTIT->direction.getArrPtr() );
		printDirectional( stream, lightDTIT->directionalExp );
		printSize1( stream, lightDTIT->size2[0] );
		printSize2( stream, lightDTIT->size2[1] );
		printKeyLight( stream, lightDTIT->isKeyLight );
		printAmbientOcclustion( stream, lightDTIT->multiplyByAmbientOcclusion );
		printShadow( stream, lightDTIT->castShadow );
		printIndirectFactor( stream, lightDTIT->indirectFactor);


	}
	break;

	default:
		assert(!" Unsupported light type" );
		break;

	}
}

template<typename STREAM> STREAM& operator<<( STREAM& stream, Light& light )
{
	static LightStream<STREAM> settings;
	settings.print( stream, light );
	return stream;
}

#endif