/*********************************************************************

Name:		Raytracer
Author:		Kamil Szatkowski "Netrix" (C)
Website:	http://www.netrix.org.pl/
License:	GNU GPL

*********************************************************************/
#include "Camera.hpp"
#include "NLib/nLogger.hpp"
#include <stdio.h>
#include <string.h>
using namespace NIne;

#pragma warning(disable: 4996)	// fopen deprecated on windows

Camera::Camera()
: m_bLoaded(false)
{
}
/********************************************************/
bool Camera::loadCamera(const char* szFile)
{
	NAssert(m_bLoaded == false, "Release file first");

	FILE* pFile = fopen(szFile, "r");
	if(pFile == null)
	{
		NLogHead();
		NLogMessage("Failed to open file: %s", szFile);
		return true;
	}

	// States
	enum
	{
		E_NAME,
		E_VIEWPOINT,
		E_SCREEN,
		E_SCREEN_POINTS,
		E_RESOLUTION,
		E_END
	};
	
	NUint32 uState = E_NAME;
	NUint32 uLine = 0;
	char buffer[2048] = { 0 };
	NUint32 uWave = 0;

	while(uState < E_END && !ferror(pFile) && !feof(pFile))
	{
		fgets(buffer, sizeof(buffer), pFile);	// read line
		uLine++;

		if(0 == buffer[0] || 0xA == buffer[0] || 0xD == buffer[0])
		{ continue; }
		else if(buffer[0] == '/' && buffer[1] == '/')
		{ continue;	}
		else if(buffer[0] == ';' && buffer[1] == ';')
		{ continue;	}
		else if(uState == E_NAME)
		{
			if(sscanf(buffer, "%s %s", buffer, m_aName) < 2 || strstr(buffer, "Camera") == null)
			{
				uState = __LINE__;
				goto InvalidFile;
			}
			// Name fix
			for(NUint32 i = 0; i < CAMERA_NAME_SIZE; ++i)
			{
				if(m_aName[i] != 0 && i > 0)
				{
					strcpy(m_aName, m_aName + i);
					break;
				}
			}
			++uState;
		}
		else if(uState == E_VIEWPOINT)
		{
			if(sscanf(buffer, "%s %f %f %f", buffer, &m_v3ViewPoint.x, &m_v3ViewPoint.y, &m_v3ViewPoint.z) < 4 || strstr(buffer, "viewpoint") == null)
			{
				uState = __LINE__;
				goto InvalidFile;
			}
			++uState;
		}
		else if(uState == E_SCREEN)
		{
			if(!sscanf(buffer, "%s", buffer) || strstr(buffer, "screen") == null)
			{
				uState = __LINE__;
				goto InvalidFile;
			}
			uWave = 0;
			++uState;
		}
		else if(uState == E_SCREEN_POINTS)
		{
			if(sscanf(buffer, "%f %f %f", &m_av3ScreenPoints[uWave].x, &m_av3ScreenPoints[uWave].y, &m_av3ScreenPoints[uWave].z) < 3)
			{
				uState = __LINE__;
				goto InvalidFile;
			}
			if(++uWave >= SCREEN_POINTS_COUNT)
			{
				++uState;
			}
		}
		else if(uState == E_RESOLUTION)
		{
			if(sscanf(buffer, "%s %d %d", buffer, &m_auScreenSize[0], &m_auScreenSize[1]) < 3 || strstr(buffer, "resolution") == null)
			{
				uState = __LINE__;
				goto InvalidFile;
			}
			++uState;
		}
	}

	m_bLoaded = true;
	fclose(pFile);
	return false;

InvalidFile:
	NLogHead(); NLogMessage("Error occured while reading line: %u, in Attributes.cpp:%u\n", uLine, uState);
	fclose(pFile);
	return true;
}
/********************************************************/
void Camera::releaseCamera()
{
	m_bLoaded = false;
}
/********************************************************/
void Camera::applyDomainFactor(float fDomainFactor)
{
	m_v3ViewPoint *= fDomainFactor;

	for(NUint32 i = 0; i < 3; ++i)	// WARNING! - FIXED NUMBER: 3
	{
		m_av3ScreenPoints[i] *= fDomainFactor;
	}
}
/********************************************************/
void Camera::setCamera(const NIne::NMVector3f& v3ViewPoint, const NIne::NMVector3f& v3ViewDir)
{
	const NMVector3f& v0 = m_av3ScreenPoints[0];
	const NMVector3f& v1 = m_av3ScreenPoints[1];
	const NMVector3f& v2 = m_av3ScreenPoints[2];

	NMVector3f vOldHalfRight = (v1 - v0) * 0.5f;
	NMVector3f vOldHalfDown = (v2 - v0) * 0.5f;
	NMVector3f vScreenCenter = v0 + vOldHalfRight + vOldHalfDown;
	NMVector3f vOldViewDir = vScreenCenter - m_v3ViewPoint;
	NMVector3f vUp = NMVector3fCross(vOldViewDir, vOldHalfRight);

	float fWidth = NMVector3fLength(vOldHalfRight),
		  fHeight = NMVector3fLength(vOldHalfDown),
		  fDepth = NMVector3fLength(vOldViewDir);

	NMVector3f vNewHalfRight = NMVector3fNormalize(NMVector3fCross(vUp, v3ViewDir)) * fWidth;
	NMVector3f vNewHalfDown = NMVector3fNormalize(NMVector3fCross(vNewHalfRight, v3ViewDir)) * fHeight;
	NMVector3f vNewScreenCenter = v3ViewPoint + v3ViewDir * fDepth;

	m_av3ScreenPoints[0] = vNewScreenCenter - vNewHalfDown - vNewHalfRight;
	m_av3ScreenPoints[1] = vNewScreenCenter - vNewHalfDown + vNewHalfRight;
	m_av3ScreenPoints[2] = vNewScreenCenter + vNewHalfDown - vNewHalfRight;
	m_v3ViewPoint = v3ViewPoint;
}