#include "precompiled.h"

#include "lib/ogl.h"
#include "lib/res/res.h"
#include "Material.h"
#include "ps/Player.h"
#include "ps/Game.h"
#include "ps/Overlay.h" // for CColor

CMaterial NullMaterial;
CMaterial IdentityMaterial;

// Values as taken straight from the Blue Book (god bless the Blue Book)
static SMaterialColor IdentityDiffuse(0.8f, 0.8f, 0.8f, 1.0f);
static SMaterialColor IdentityAmbient(0.2f, 0.2f, 0.2f, 1.0f);
static SMaterialColor IdentitySpecular(0.0f, 0.0f, 0.0f, 1.0f);
static SMaterialColor IdentityEmissive(0.0f, 0.0f, 0.0f, 1.0f);

static SMaterialColor BrokenColor(1.0f, 0.0f, 1.0f, 1.0f);

bool SMaterialColor::operator ==(const SMaterialColor color)
{
	return (
		r == color.r &&
		g == color.g &&
		b == color.b &&
		a == color.a
		);
}

CMaterial::CMaterial()
	: m_Diffuse(IdentityDiffuse),
	m_Ambient(IdentityAmbient),
	m_Specular(IdentitySpecular),
	m_Emissive(IdentityEmissive),
	m_SpecularPower(0.0f),
	m_Alpha(false),
	m_PlayerID(PLAYER_NONE),
	m_TextureColor(BrokenColor)
{
	ComputeHash();
}

CMaterial::CMaterial(const CMaterial &material)
{
	(*this) = material;
}

CMaterial::~CMaterial()
{
}

void CMaterial::operator =(const CMaterial &material)
{
	m_Diffuse = material.m_Diffuse;
	m_Ambient = material.m_Ambient;
	m_Specular = material.m_Specular;
	m_Emissive = material.m_Emissive;

	m_SpecularPower = material.m_SpecularPower;
	m_Alpha = material.m_Alpha;
	m_PlayerID = material.m_PlayerID;
	m_TextureColor = material.m_TextureColor;
	ComputeHash();
}

bool CMaterial::operator ==(const CMaterial &material)
{
	return(
		m_Texture == m_Texture &&
		m_Diffuse == material.m_Diffuse &&
		m_Ambient == material.m_Ambient &&
		m_Specular == material.m_Specular &&
		m_Emissive == material.m_Emissive &&
		m_SpecularPower == material.m_SpecularPower &&
		m_Alpha == material.m_Alpha &&
		m_PlayerID == material.m_PlayerID &&
		m_TextureColor == material.m_TextureColor
	);
}

void CMaterial::Bind()
{
    glMaterialf(GL_FRONT, GL_SHININESS, m_SpecularPower);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, &m_Diffuse.r);
    glMaterialfv(GL_FRONT, GL_AMBIENT, &m_Ambient.r);
    glMaterialfv(GL_FRONT, GL_SPECULAR, &m_Specular.r);
    glMaterialfv(GL_FRONT, GL_EMISSION, &m_Emissive.r);

    oglCheck();
}

void CMaterial::Unbind()
{
}

SMaterialColor CMaterial::GetDiffuse()
{
    return m_Diffuse;
}

SMaterialColor CMaterial::GetAmbient()
{
    return m_Ambient;
}

SMaterialColor CMaterial::GetSpecular()
{
    return m_Specular;
}

SMaterialColor CMaterial::GetEmissive()
{
    return m_Emissive;
}

SMaterialColor CMaterial::GetPlayerColor()
{
	debug_assert(m_PlayerID != PLAYER_NONE);
		// because this should never be called unless IsPlayer returned true

	if (m_PlayerID == PLAYER_OTHER /* TODO: or if player-colour is globally disabled */ )
		return m_TextureColor;

	if (m_PlayerID >= 0)
	{
		CPlayer* player = g_Game->GetPlayer(m_PlayerID);
		if (player)
		{
			const SPlayerColour& c (player->GetColour());
			return SMaterialColor(c.r, c.g, c.b, c.a);
		}
	}

	// Oops, something failed.
	return BrokenColor;
}

void CMaterial::SetPlayerColor(int id)
{
	if (m_PlayerID == PLAYER_COMINGSOON || m_PlayerID >= 0)
		m_PlayerID = id;
}

void CMaterial::SetPlayerColor(CColor &colour)
{
	m_TextureColor = SMaterialColor(colour.r, colour.g, colour.b, colour.a);
}


void CMaterial::SetTexture(const CStr &texture)
{
    m_Texture = texture;
    ComputeHash();
}

void CMaterial::SetVertexProgram(const CStr &prog)
{
    m_VertexProgram = prog;
    ComputeHash();
}

void CMaterial::SetFragmentProgram(const CStr &prog)
{
    m_FragmentProgram = prog;
    ComputeHash();
}

void CMaterial::SetDiffuse(const SMaterialColor &color)
{
	m_Diffuse = color;
    ComputeHash();
}

void CMaterial::SetAmbient(const SMaterialColor &color)
{
	m_Ambient = color;
    ComputeHash();
}

void CMaterial::SetSpecular(const SMaterialColor &color)
{
	m_Specular = color;
    ComputeHash();
}

void CMaterial::SetEmissive(const SMaterialColor &color)
{
	m_Emissive = color;
    ComputeHash();
}

void CMaterial::SetSpecularPower(float power)
{
    m_SpecularPower = power;
    ComputeHash();
}

void CMaterial::SetUsesAlpha(bool flag)
{
    m_Alpha = flag;
    ComputeHash();
}

void CMaterial::ComputeHash()
{
    m_Hash = 
        m_Diffuse.Sum() +
        m_Ambient.Sum() +
        m_Specular.Sum() +
        m_Emissive.Sum() +
        m_SpecularPower +
        (float)m_Texture.GetHashCode() +
        (float)m_VertexProgram.GetHashCode() +
        (float)m_FragmentProgram.GetHashCode();
}
