#ifndef __SNAPPINGANALYZER_H__
#define __SNAPPINGANALYZER_H__

#pragma once

#include "image.h"
#include "tinyxml/tinyxml.h"

static inline float ToPower( float x, int n )
{
	for (int i=1; i<n; i++)
		x *= x;
	return x;
}

struct CDistanceScaler
{
public:
	CDistanceScaler() : m_a(0.0f), m_b(0.0f), m_c(0.0f) {}

	void Init(float normalDistance, float minBump, float maxBump)
	{
		if (minBump <= 0.0f)
			minBump = 0.001f;
		minBump = -minBump;

		if (maxBump <= 0.0f)
			maxBump = 0.001f;

		m_a = maxBump - minBump;
		m_b = -logf( -minBump / (maxBump - minBump) ) / ToPower(normalDistance, DEGREE);
		m_c = minBump;
	}

	float GetBump( float dist ) const
	{
		return m_a * expf( -m_b*ToPower(dist, DEGREE) ) + m_c;
	}

private:
	float m_a, m_b, m_c;
	static const int DEGREE = 2;
};

class CPulseScaler
{
public:
	CPulseScaler() : m_a(0.0f), m_b(0.0f) {}

	void Init(float bump, float decay)
	{
		m_a = bump;
		m_b = logf(2.0f)/decay;
	}

	float GetBump( float t ) const
	{
		return m_a * expf( -m_b * t );
	}

private:
	float m_a, m_b;
};

#ifndef CLAMP
#define CLAMP(X, mn, mx) ((X)<(mn) ? (mn) : ((X)<(mx) ? (X) : (mx)))
#endif

class CDirScaler
{
public:
	CDirScaler() : m_a(0.0f), m_b(0.0f), m_pow(0.0f) {}

	void Init(float foi, float front, float back)
	{
		bool ok = true;
		ok &= foi > 10;
		ok &= front >= 0;
		ok &= back >= 0;
		if (ok && front == 0 && back == 0)
		{
			m_a = m_b = 0;
		}
		else if (ok)
		{
			m_pow = logf( 1.0f - 0.98f ) / logf( 0.5f - 0.5f * cosf(foi/2/180.0f*gf_PI) );
			m_a = front + back;
			m_b = -back;
		}
		else
		{
			m_a = m_b = m_pow = 0;
		}
	}

	float GetBump( float cosang ) const
	{
		float scale = 1.0f - powf( 0.5f - 0.5f * CLAMP(cosang,-1,1), m_pow );
		return m_a * scale + m_b;
	}

private:
	float m_a, m_b, m_pow;
};

class CSnappingAnalyzer
{
public:
	CSnappingAnalyzer(const string& filename);

	void Plot(CImage<iRGB>& image, uint32 group, bool opaque = true);

private:
	TiXmlDocument m_snappingXml;

	struct SScalers
	{
		mutable float priority;
		CDistanceScaler dis;
		CDirScaler dir;
		//CPulseScaler pulse; don't care pulse for now
	};
	std::map<uint32, SScalers> m_schedulerGroupScalerMap;

	std::map<string, uint32> m_entityClassSchedulerGroupMap;

	struct SPlotParams
	{
		uint32 group;
		Vec3 relativePos;
		float snappingDistance;
		float theoreticalBump;
	};
	std::vector<SPlotParams> m_plottingParameters;

	float m_maxSnappingDist;
	float m_worldRadius;
};

#endif

