/// Copyright (c) 2010 Ruben Penalva. All rights reserved.
/// Redistribution and use in source and binary forms, with or without modification, 
/// are permitted provided that the following conditions are met:
///    1. Redistributions of source code must retain the above copyright notice, this 
///    list of conditions and the following disclaimer.
///    2. Redistributions in binary form must reproduce the above copyright notice,
///    this list of conditions and the following disclaimer in the documentation and/or
///    other materials provided with the distribution.
///    3. The name of the author may not be used to endorse or promote products derived
///    from this software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
/// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
/// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 
/// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
/// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
/// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/// @file Runner.cpp
/// This source file is part of CUDA Ray Casting test.
/// The file holds the Runner class implementation.
/// @author Ruben Penalva (ruben.penalva@rpenalva.com)
#include "Runner.h"

#include "Vector3.h"
#include "Matrix4x4.h"
#include "boost/smart_ptr/shared_array.hpp"
#include <sstream>

using namespace CUDARayCasting;

const Vector3 Runner::m_fontColor(0.0f, 0.0f, 1.0f);

enum Actions
{
	IncreaseStep,
	DecreaseStep,
	IncreaseStepNumber,
	DecreaseStepNumber,
	IncreaseAnimationTime,
	DecreaseAnimationTime,
	Pause,
	Play
};

Runner::Runner(int width, int height)	:	m_render(width, height), 
											m_width(width), m_height(height), 
											m_lastElapsedTime(0.0f)
{
	m_entries.push_back(GlutWrapper::MenuEntry("Increase Step", IncreaseStep));
	m_entries.push_back(GlutWrapper::MenuEntry("Decrease Step", DecreaseStep));
	m_entries.push_back(GlutWrapper::MenuEntry("Increase Step Number", IncreaseStepNumber));
	m_entries.push_back(GlutWrapper::MenuEntry("Decrease Step Number", DecreaseStepNumber));
	m_entries.push_back(GlutWrapper::MenuEntry("Increase Animation Time", IncreaseAnimationTime));
	m_entries.push_back(GlutWrapper::MenuEntry("Decrease Animation Time", DecreaseAnimationTime));
	m_entries.push_back(GlutWrapper::MenuEntry("Pause", Pause));
	m_entries.push_back(GlutWrapper::MenuEntry("Play", Play));
}

Runner::~Runner(void)
{
}
	
void Runner::InitializeOpengl(void)
{
	const int width = 128;
	//const int width = 32;
	//const int height = 32;
	//const int depth = 32;
	const int height = 256;
	const int depth = 256;
	boost::shared_array<unsigned char> volumeData(m_logic.LoadVolumeData());

	//boost::shared_array<unsigned char> volumeData(new unsigned char[width  * height * depth]);
	//memset(volumeData.get(), 0x00, sizeof(unsigned char) * width * height * depth);
	//for (int i = 0; i < width; ++i)
	//{
	//	for (int j = 0; j < height; ++j)
	//	{
	//		for (int k = 0; k < depth; ++k)
	//		{
	//			Vector3 position(i, j, k);
	//			Vector3 origin(0, 0, 0);
	//			if ((position - origin).Length() < 20)
	//			{
	//				volumeData[i * height * depth + j * depth + k] = 0xff;
	//			}
	//		}
	//	}
	//}
	
	const int transferFunctionWidth = 256;
	boost::shared_array<float> transferFunctionPtr(m_logic.LoadTransferFunction());

	m_render.Initialize(volumeData, width, height, depth, transferFunctionPtr, 
						transferFunctionWidth, m_logic.GetStep(), m_logic.GetStepNumber(),
						m_logic.GetViewMatrix(), m_logic.GetVolumeMatrix(), 
						m_logic.GetBboxMin(), m_logic.GetBboxMax());
}

void Runner::Run(float elapsedTime)
{
	m_lastElapsedTime = elapsedTime;

	Update(elapsedTime);

	Render(elapsedTime);
}

void Runner::Reshape(int width, int height)
{
	m_render.Reshape(width, height);
}

void Runner::ProcessMenuEvents(int option)
{
	switch(option)
	{
		case IncreaseStep:
			m_logic.IncreaseStep();
			m_render.SetStep(m_logic.GetStep());
			break;
		
		case DecreaseStep:
			m_logic.DecreaseStep();
			m_render.SetStep(m_logic.GetStep());
			break;
		
		case IncreaseStepNumber:
			m_logic.IncreaseStepNumber();
			m_render.SetStepNumber(m_logic.GetStepNumber());
			break;
		
		case DecreaseStepNumber:
			m_logic.DecreaseStepNumber();
			m_render.SetStepNumber(m_logic.GetStepNumber());
			break;
		
		case IncreaseAnimationTime:
			m_logic.IncreaseAnimationTime();
			break;
		
		case DecreaseAnimationTime:
			m_logic.DecreaseAnimationTime();
			break;
		
		case Pause:
			m_logic.Pause();
			break;
		
		case Play:
			m_logic.Play();
			break;

		default:
			break;
	}
	
}

void Runner::ProcessMouse(const GlutWrapper::MouseState& mouseState)
{
	m_logic.ProcessMouse(mouseState, m_lastElapsedTime);
}

const std::vector<GlutWrapper::MenuEntry>& Runner::GetMenuEntries(void) const
{
	return m_entries;
}

void Runner::Render(float elapsedTime)
{	
	m_render.BeginFrame();

	m_render.DrawVolume();

	DisplayMetrics(elapsedTime);

	m_render.EndFrame();
}	

void Runner::Update(float elapsedTime) 
{
	m_logic.UpdateTransferFunction(elapsedTime);

	m_render.UpdateTransferFunction();

	m_render.SetCamera(m_logic.GetViewMatrix());

	m_render.SetVolumeMatrix(m_logic.GetVolumeMatrix());
}

void Runner::DisplayMetrics(float elapsedTime)
{
	float animationTime = m_logic.GetAnimationTime();
	float step = m_logic.GetStep();
	int stepNumber = m_logic.GetStepNumber();
	int windowWidth = m_logic.GetDensityWindowWidth();
	int windowBegin = m_logic.GetDensityWindowBegin();
	int windowEnd =  m_logic.GetDensityWindowEnd();

	std::stringstream statistics;
	statistics	<< "Frame time: " << elapsedTime << "s";
	m_render.DrawString(statistics.str(), m_width - 230, 85, m_fontColor);
	
	statistics.str("");

	statistics << "Animation time:" << animationTime << "s";
	m_render.DrawString(statistics.str(), m_width - 230, 70, m_fontColor);

	statistics.str("");

	statistics << "Step size: " << step;
	m_render.DrawString(statistics.str(), m_width - 230, 55, m_fontColor);

	statistics.str("");

	statistics << "Number of steps: " << stepNumber;
	m_render.DrawString(statistics.str(), m_width - 230, 40, m_fontColor);

	statistics.str("");

	statistics << "Density window: " << windowBegin << ", " << windowEnd;
	m_render.DrawString(statistics.str(), m_width - 230, 25, m_fontColor);

	statistics.str("");

	statistics << "Density window width: " << windowWidth;
	m_render.DrawString(statistics.str(), m_width - 230, 10, m_fontColor);
}