#include "visualisation.h"

Visualisation::Visualisation()
{
	// Set some initial values.
	HasOverlay = FALSE;
	IsSilent = FALSE;
	Clock = 0;
}

Visualisation::Visualisation(std::string name)
{
	// Set some initial values.
	Name = name;
	HasOverlay = FALSE;
	IsSilent = FALSE;
	Clock = 0;
}

Visualisation::~Visualisation() { }

VOID Visualisation::Release()
{
	// Release the overlay sprite.
	if (HasOverlay)
		Overlay.Release();

	// Release the visualisation texture.
	if (Texture != NULL)
	{
		Texture->Release();
		Texture = NULL;
	}

	// Release the visualisation surface.
	if (Surface != NULL)
	{
		Surface->Release();
		Surface = NULL;
	}

	// Release the Direct3D Device.
	if (Device != NULL)
	{
		Device->Release();
		Device = NULL;
	}

	// Release all of the plugins in the visualisation.
	for(int i = 0; i < Plugins.size();i++)
		Plugins[i]->Quit();

	// Clear the plugin list.
	Plugins.clear();
}

VOID Visualisation::Init(IDirect3DDevice9* device)
{
	Device = device;

	// Get the viewport size.
	D3DVIEWPORT9 DeviceViewport;
	Device->GetViewport(&DeviceViewport);

	// Create the render texture and surface from the device.
	Device->CreateTexture(DeviceViewport.Width, DeviceViewport.Height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &Texture, NULL);
	Texture->GetSurfaceLevel(0, &Surface);
	
	// Initialise the overlay sprite.
	Overlay.Init(Device);

	// Initialise all of the plugins in the visualisation with the device.
	for(int i = 0; i < Plugins.size();i++)
		Plugins[i]->Init(Device);
}

VOID Visualisation::Render(PluginData* Data)
{
	// Increment the clock.
	Clock++;

	// If there are plugins to render..
	if (Plugins.size() > 0)
	{
		for(int i = 0;i < Plugins.size();i++)
		{
			// Set the plugin attributes.
			SetProperties(Plugins[i], Properties[i], Data);

			// Render the plugin with the plugin data.
			Plugins[i]->Render(Data);
		}

		return;
	}

	// Render the overla sprite.
	if (HasOverlay)
		Overlay.Render();
}

VOID Visualisation::CycleColor(ColorData* color, float speed)
{
	// Calculate some inital values.
	float stages = 6.0f;
    float timeModSpeed = (Clock % (int)speed);
    float speedDivStages = speed / stages;
    float transitionSpeed = 1.0f / speedDivStages;

	// Add green component.
    if (timeModSpeed < speedDivStages)
    {
		if (color->G < 1.0f)
			color->G += transitionSpeed;
    }
	// Remove red component.
    else if (timeModSpeed < speedDivStages * 2.0f) 
    {
		if (color->R > 0.0f)
			color->R -= transitionSpeed;
    }
	// Add blue component.
    else if (timeModSpeed < speedDivStages * 3.0f)
    {
		if (color->B < 1.0f)
			color->B += transitionSpeed;
    }
	// Remove green component.
    else if (timeModSpeed < speedDivStages * 4.0f)
    {
		if (color->G > 0.0f)
			color->G -= transitionSpeed;
    }
	// Add red component.
    else if (timeModSpeed < speedDivStages * 5.0f)
    {
		if (color->R < 1.0f)
			color->R += transitionSpeed;
    }
	// Remove blue component.
    else if (timeModSpeed < speed)
    {
        if (color->B > 0.0f)
			color->B -= transitionSpeed;
    }
}

VOID Visualisation::SetProperties(Plugin* plugin, PluginAttributes properties, PluginData* Data)
{
	// Set the color properties of the plugin.
	if (properties.Cycle <= 0)
	{
		// No cycle, just set the color attribute.
		plugin->Properties->Color = properties.Color;
	}
	else
	{
		// Calculate and set a cycling color.
		plugin->Properties->Color.A = properties.Color.A;
		CycleColor(&plugin->Properties->Color, 5000.0f / properties.Cycle);
	}

	// Apply Position.
	plugin->Properties->Position = properties.Position;

	// Apply Rotation.
	plugin->Properties->Rotation.X = properties.Rotation.X + (timeGetTime() * properties.Spin.X)/2000.0f;
	plugin->Properties->Rotation.Y = properties.Rotation.Y + (timeGetTime() * properties.Spin.Y)/2000.0f;
	plugin->Properties->Rotation.Z = properties.Rotation.Z + (timeGetTime() * properties.Spin.Z)/2000.0f;

	// Apply Color Cycle.
	plugin->Properties->Cycle = properties.Cycle;

	// Apply Spin Velocity.
	plugin->Properties->Spin = properties.Spin;

	// Apply Resolution.
	plugin->Properties->Resolution = properties.Resolution;

	// Create matrices for transformations.
	D3DXMATRIX finalMatrix, worldMatrix, viewMatrix, projMatrix, translationMatrix, rotationXMatrix, rotationYMatrix, rotationZMatrix;

	// Get the device transform matrices.
	Device->GetTransform(D3DTS_VIEW, &viewMatrix);
	Device->GetTransform(D3DTS_PROJECTION, &projMatrix);
	Device->GetTransform(D3DTS_WORLD, &worldMatrix);

	// Translate the plugin.
	D3DXMatrixTranslation(&translationMatrix, 
		plugin->Properties->Position.X, 
		plugin->Properties->Position.Y, 
		plugin->Properties->Position.Z + (Data->Audio.Average*(180.0f*(450.0f+abs(plugin->Properties->Position.Z)))));

	// Rotate the plugin.
	D3DXMatrixRotationX(&rotationXMatrix, plugin->Properties->Rotation.X);
	D3DXMatrixRotationY(&rotationYMatrix, plugin->Properties->Rotation.Y);
	D3DXMatrixRotationZ(&rotationZMatrix, plugin->Properties->Rotation.Z);

	// Calculate the plugin world matrix.
	worldMatrix = rotationXMatrix * rotationYMatrix * rotationZMatrix * translationMatrix;

	// Send the transformations back into the device.
	Device->SetTransform(D3DTS_VIEW, &viewMatrix);
	Device->SetTransform(D3DTS_PROJECTION, &projMatrix);
	Device->SetTransform(D3DTS_WORLD, &worldMatrix);

	// Set the world matrix value in the plugin data object.
	Data->World = worldMatrix * viewMatrix * projMatrix;
}