#include "config.h"
#include "layered_surface.h"
#include "texture.h"

__BEGIN_BRDFLIB_SOURCE

// describes the surface to a given stream
//========================================================================


CSurfaceLayered::~CSurfaceLayered()
{
	for (int i = 0; i < (int)_layers.size(); i++)
	{
		delete _layers[i];
	}
}

//========================================================================

void 
CSurfaceLayered::AddLayer(CSurface* surface, CTexture* thickness, 
						  CTexture* absorbtion)
{
	CSurfaceLayer* newLayer = new CSurfaceLayer(surface,thickness,absorbtion);
	_layers.push_back(newLayer);
}

//========================================================================

void 
CSurfaceLayered::AddLayer(CSurfaceLayer* layer)
{
	_layers.push_back(layer);
}

//========================================================================

void 
CSurfaceLayered::InsertLayer(CSurface* surface, CTexture* thickness, 
							 CTexture* absorbtion, int index)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::InsertLayer() : given index is out of bounds!");
		return;
	}
	CSurfaceLayer* newLayer = new CSurfaceLayer(surface,thickness,absorbtion);
	_layers.insert(_layers.begin()+index,newLayer);
}

//========================================================================

void 
CSurfaceLayered::InsertLayer(CSurfaceLayer* layer, int index)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::InsertLayer() : given index is out of bounds!");
		return;
	}
	_layers.insert(_layers.begin()+index,layer);
}

//========================================================================

void 
CSurfaceLayered::ReplaceLayer(CSurface* surface, CTexture* thickness, 
							  CTexture* absorbtion, int index)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::ReplaceLayer() : given index is out of bounds!");
		return;
	}
	CSurfaceLayer* newLayer = new CSurfaceLayer(surface,thickness,absorbtion);
	AssignPointer(_layers[index],newLayer);
	
}

//========================================================================

void 
CSurfaceLayered::ReplaceLayer(CSurfaceLayer* layer, int index)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::ReplaceLayer() : given index is out of bounds!");
		return;
	}
	AssignPointer(_layers[index],layer);
}

//========================================================================

void 
CSurfaceLayered::SwapLayers(int index1, int index2)
{
	if (index1 >= (int)_layers.size() || index1 < 0 ||
		index2 >= (int)_layers.size() || index2 < 0)
	{
		WARNING_INFO("CSurfaceLayered::SwapLayers() : bad indexing.");
		return;
	}
	CSurfaceLayer* aux = _layers[index1];
	_layers[index1] = _layers[index2];
	_layers[index2] = aux;
}

//========================================================================

void 
CSurfaceLayered::ShiftLayer(int index, bool directionUp)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::SwapLayers() : index is out of bounds!");
		return;
	}
	if ((index == 0 && directionUp) || 
		(index == (int)_layers.size()-1 && !directionUp))
	{
		// shifting to nowhere
		return;
	}
	CSurfaceLayer* aux = _layers[index];
	int index2 = directionUp ? (index-1) : (index+1);
	_layers[index] = _layers[index2];
	_layers[index2] = aux;
}

//========================================================================

const CSurfaceLayer* 
CSurfaceLayered::GetLayer(int index)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::GetLayer() : given index is out of bounds!");
		return NULL;
	}
	return _layers[index];
}

//========================================================================

void 
CSurfaceLayered::RemoveLayer(int index)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::RemoveLayer() : given index is out of bounds!");
		return;
	}
	delete _layers[index];
	_layers.erase(_layers.begin()+index);
}

//========================================================================

void 
CSurfaceLayered::SetLayerSurface(int index, CSurface* surface)
{
	if (index >= (int)_layers.size() || index < 0)
	{
		WARNING_INFO("CSurfaceLayered::SetLayerSurface() : given index is out of bounds!");
		return;
	}
	if (_layers[index]->surface != surface)
	{
		AssignPointer(_layers[index]->surface,surface);
	}
}

//========================================================================

void
CSurfaceLayered::Describe(ostream &app, int ind) const
{
	for (int i = 0; i < (int)_layers.size(); i++)
	{
		_layers[i]->surface->Describe(app,ind);
	}
}

//========================================================================

vector<CSurfaceLayer*>&
CSurfaceLayered::DupLayers() const
{
	vector<CSurfaceLayer*> layersDC;
	for (int i = 0; i < (int)_layers.size(); i++)
	{
		CSurface* surfaceDC = _layers[i]->surface->Dup();
		CValues t;
		_layers[i]->thickness->GetValue(t,CHitPointInfo::constMapInfo);
		CTexture* thicknessDC = new CConstantTexture(t);
		_layers[i]->absorbtion->GetValue(t,CHitPointInfo::constMapInfo);
		CTexture* absorbtionDC = new CConstantTexture(t);
		CSurfaceLayer* layerDC = new CSurfaceLayer(surfaceDC,thicknessDC,absorbtionDC);
		layersDC.push_back(layerDC);
	}
	return layersDC;
}

//========================================================================

int 
CSurfaceLayered::GetNumParameters() const
{
	return (int)_layers.size();
}
//========================================================================

CValues
CSurfaceLayered::GetParameter(int parameterIndex) const
{
	if (parameterIndex >= (int)_layers.size())
	{
		WARNING_INFO("CSurfaceLayered::GetParameter() :\n\
parameter index out of bounds.");
		return CValues(0,0,0);
	}
	// 4 values of the layer - thickness + 3 components of absorption
	// surface pointer is given another way
	CValues c(5); 
	CValues aux(3);
	_layers[parameterIndex]->thickness->GetValue(aux,CHitPointInfo::constMapInfo);
	c.Set(0,aux.Get(0));
	aux.Set(0);
	_layers[parameterIndex]->absorbtion->GetValue(aux,CHitPointInfo::constMapInfo);
	c.Set(1,aux.Get(0));
	c.Set(2,aux.Get(1));
	c.Set(3,aux.Get(2));
	return c;
}
//========================================================================

CValues
CSurfaceLayered::GetParameterConstraints(int parameterIndex) const
{
	// constraints are set before the layer itself is constructed
	// and as default there is no layer in the layered surface. So
	// we shouldnt check the index, because it will always fail the
	// test.
	CValues c;
	_setBasicConstraints(EE_Layer,c);
	return c;
}
//========================================================================

const char* 
CSurfaceLayered::GetParameterDescription(int parameterIndex) const
{
	//if (parameterIndex < (int)_layers.size())
	//{
		return BrdfStrings::KLayerDesc;
	//}
	//return BrdfStrings::KInvalidParameter;
}
//========================================================================

CSurface::EParameterUIType
CSurfaceLayered::GetParameterSyntax(int parameterIndex) const
{
	if (parameterIndex >= (int)_layers.size())
	{
		WARNING_INFO("CSurfaceLayered::GetParameterSyntax() :\n\
parameter index out of bounds.");
		return EE_Unknown;
	}
	return EE_Layer;
}
//========================================================================

const char*
CSurfaceLayered::GetParameterTitle(int parameterIndex) const
{
	//if (parameterIndex < (int)_layers.size())
	//{
		
		return BrdfStrings::KLayerTitle;
		
	//}
	//return BrdfStrings::KInvalidParameter;
}
//========================================================================

void
CSurfaceLayered::SetParameter(int parameterIndex, const CValues& value)
{
	// expected value has 4 components :
	// 0 : thickness of the layer
	// 1-3 : wavelength dependent absorption of the layer
	if (parameterIndex > (int)_layers.size())
	{
		WARNING_INFO("CSurfaceLayered::SetParameter() :\n\
parameter index out of bounds. It should be lesser than (for editing)\n\
or equal to (adding new) the number of existing layers.");
		return;
	}
	else if (parameterIndex == (int)_layers.size())
	{
		// add layer at the end of the vector
		CValues val(CValues::EE_Weigth3);
		val.Set(value.Get(0));
		CTexture* th = new CConstantTexture(val);
		val.Set(0,value.Get(1));
		val.Set(1,value.Get(2));
		val.Set(2,value.Get(3));
		CTexture* absorb = new CConstantTexture(val);
		CSurfaceLayer* newLayer = new CSurfaceLayer(NULL,th,absorb);
		AddLayer(newLayer);
	}
	else
	{
		// edit layer properties
		CValues val(CValues::EE_Weigth3);
		_layers[parameterIndex]->thickness->GetValue(val,
			CHitPointInfo::constMapInfo);
		if (val.Get(0) != value.Get(0))
		{
			val.Set(value.Get(0));
			CTexture* newThick = new CConstantTexture(val);
			AssignPointer(_layers[parameterIndex]->thickness,newThick);
		}
		_layers[parameterIndex]->absorbtion->GetValue(val,
			CHitPointInfo::constMapInfo);
		if (val.Get(0) != value.Get(1) || 
			val.Get(1) != value.Get(2) ||
			val.Get(2) != value.Get(3))
		{
			val.Set(0,value.Get(1));
			val.Set(1,value.Get(2));
			val.Set(2,value.Get(3));
			CTexture* newAbs = new CConstantTexture(val);
			AssignPointer(_layers[parameterIndex]->absorbtion,newAbs);
		}
	}
}
//========================================================================


__END_BRDFLIB_SOURCE