// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "stdpch.h"
#include "system.h"

#include <nel/3d/driver.h>
#include <nel/3d/dru.h>
#include <QtOpenGL/QGLWidget>

CSystem::CSystem()
{
	GatherSysInfo();
#ifdef Q_OS_WIN32
	GatherD3DInfo();
#endif
	GatherOpenGLInfo();
}

CSystem::~CSystem()
{
}

bool CSystem::parseDriverVersion(const std::string &device, uint64 driver, std::string &version)
{
	// file version
	uint32 version1 = driver >> 48;
	uint32 version2 = (driver >> 32) & 0xffff;
	uint32 version3 = (driver >> 16) & 0xffff;
	uint32 version4 = driver & 0xffff;

	if (device.find("NVIDIA") != std::string::npos)
	{
		// nvidia should be something like 9.18.13.2018 and 9.18.13.1422
		// which respectively corresponds to drivers 320.18 and 314.22
		uint32 nvVersionMajor = (version3 % 10) * 100 + (version4 / 100);
		uint32 nvVersionMinor = version4 % 100;

		version = NLMISC::toString("%u.%u", nvVersionMajor, nvVersionMinor);
	}
	else
	{
		version = NLMISC::toString("%u.%u.%u.%u", version1, version2, version3, version4);
	}

	return true;
}

void CSystem::GatherSysInfo()
{
	std::string device;
	uint64 driver;

	if( NLMISC::CSystemInfo::getVideoInfo( device, driver ) )
	{
		sysInfo.videoDevice = device;

		CSystem::parseDriverVersion(device, driver, sysInfo.videoDriverVersion);
	}
	else
	{
		sysInfo.videoDevice = "video card";
		sysInfo.videoDriverVersion = "0.0.0.0";
	}

	sysInfo.osName   = NLMISC::CSystemInfo::getOS();
	sysInfo.cpuName  = NLMISC::CSystemInfo::getProc();
	sysInfo.totalRAM = NLMISC::CSystemInfo::totalPhysicalMemory();
	sysInfo.totalRAM /= ( 1024 * 1024 );
}

#ifdef Q_OS_WIN32
void CSystem::GatherD3DInfo()
{
	NL3D::IDriver *driver = NULL;
	try
	{
		driver = NL3D::CDRU::createD3DDriver();

		NL3D::IDriver::CAdapter adapter;

		if( driver->getAdapter( 0xffffffff, adapter ) )
		{
			d3dInfo.device        = adapter.Description;
			d3dInfo.driver        = adapter.Driver;

			CSystem::parseDriverVersion(d3dInfo.device, adapter.DriverVersion, d3dInfo.driverVersion);
		}

		GetVideoModes( d3dInfo.modes, driver );

		driver->release();
	}

	catch(const NLMISC::Exception &e)
	{
		nlwarning( e.what() );
	}
}
#endif

void CSystem::GatherOpenGLInfo()
{
	QGLWidget *gl = new QGLWidget();

	gl->makeCurrent();

	const char *s = NULL;
	s = reinterpret_cast< const char * >( glGetString( GL_VENDOR ) );
	if( s != NULL )
		openglInfo.vendor.assign( s );

	s = reinterpret_cast< const char * >( glGetString( GL_RENDERER ) );
	if( s != NULL )
		openglInfo.renderer.assign( s );

	s = reinterpret_cast< const char * >( glGetString( GL_VERSION ) );
	if( s != NULL )
		openglInfo.driverVersion.assign( s );

	s = reinterpret_cast< const char * >( glGetString( GL_EXTENSIONS ) );
	if( s != NULL )
	{
		openglInfo.extensions.assign( s );
		for( std::string::iterator itr = openglInfo.extensions.begin(); itr != openglInfo.extensions.end(); ++itr )
			if( *itr == ' ' )
				*itr = '\n';
	}

	delete gl;

	try
	{
		NL3D::IDriver *driver = NL3D::CDRU::createGlDriver();
		GetVideoModes( openglInfo.modes, driver );
		driver->release();
	}
	catch(const NLMISC::Exception &e)
	{
		nlwarning( e.what() );
	}
}

void CSystem::GetVideoModes( std::vector< CVideoMode > &dst, NL3D::IDriver *driver ) const
{
	std::vector< NL3D::GfxMode > modes;
	driver->getModes( modes );

	for( std::vector< NL3D::GfxMode >::iterator itr = modes.begin(); itr != modes.end(); ++itr )
	{
		if( ( itr->Width >= 800 ) && ( itr->Height >= 600 ) && ( itr->Depth == 32 ) && ( itr->Frequency >= 60 ) )
		{
			CVideoMode mode;
			mode.depth = itr->Depth;
			mode.width = itr->Width;
			mode.height = itr->Height;
			mode.frequency = itr->Frequency;

			dst.push_back( mode );
		}
	}
}