/***************************************************************************
 *   Copyright (C) 2008 by Oliver Bock                                     *
 *   oliver.bock[AT]aei.mpg.de                                             *
 *                                                                         *
 *   This file is part of Einstein@Home.                                   *
 *                                                                         *
 *   Einstein@Home is free software: you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published     *
 *   by the Free Software Foundation, version 2 of the License.            *
 *                                                                         *
 *   Einstein@Home 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with Einstein@Home. If not, see <http://www.gnu.org/licenses/>. *
 *                                                                         *
 ***************************************************************************/

#include "BOINCClientAdapter.h"
#include "Libxml2Adapter.h"

#include <sstream>

BOINCClientAdapter::BOINCClientAdapter(string sharedMemoryIdentifier)
{
	m_Initialized = false;
	m_SharedMemoryAreaIdentifier = sharedMemoryIdentifier;
	m_SharedMemoryAreaAvailable = false;

	m_xmlIFace = new Libxml2Adapter();

	m_GraphicsFrameRate = 20;
	m_GraphicsQualitySetting = BOINCClientAdapter::LowGraphicsQualitySetting;
	m_GraphicsWindowWidth = 800;
	m_GraphicsWindowHeight = 600;
}

BOINCClientAdapter::~BOINCClientAdapter()
{
	if(m_xmlIFace) delete m_xmlIFace;
}

void BOINCClientAdapter::initialize()
{
	if(!m_Initialized) {
		readUserInfo();
		readSharedMemoryArea();
		readProjectPreferences();

		m_Initialized = true;
	}
}

void BOINCClientAdapter::refresh()
{
	if(m_Initialized) {
		readUserInfo();
		readSharedMemoryArea();

		/// \todo Check that we're still watching our own WU (or science app)!
	}
	else {
		cerr << "The BOINC Client Adapter has not yet been initialized! Doing so now..." << endl;
		initialize();
	}
}

void BOINCClientAdapter::readUserInfo()
{
	boinc_parse_init_data_file();
	boinc_get_init_data(m_UserData);
}

void BOINCClientAdapter::readSharedMemoryArea()
{
	// check if we already have a pointer
	if(m_SharedMemoryAreaAvailable) {
		// load contents
		m_SharedMemoryAreaContents = string(m_SharedMemoryArea);
	}
	// the shared memory area's not available, try to get a pointer to it
	else {
	   m_SharedMemoryArea = (char*) boinc_graphics_get_shmem((char*)m_SharedMemoryAreaIdentifier.c_str());

	    if(m_SharedMemoryArea) {
	    	// fine, get the contents recursively
	        m_SharedMemoryAreaAvailable = true;
	        readSharedMemoryArea();
	    }
	    else {
	    	// bad luck
	    	m_SharedMemoryAreaContents = "";
	        m_SharedMemoryAreaAvailable = false;
	    }
	}
}

void BOINCClientAdapter::readProjectPreferences()
{
	string temp;
	istringstream converter;
	converter.exceptions(ios_base::badbit | ios_base::failbit);

	// prepare xml document
	m_xmlIFace->setXmlDocument(projectInformation(), "http://einstein.phys.uwm.edu");

	// use XPath queries to get attributes
	try {
		temp = m_xmlIFace->getSingleNodeContentByXPath("/project_preferences/graphics/@fps");
		if(temp.length() > 0) {
			converter.clear();
			converter.str(temp);
			converter >> dec >> m_GraphicsFrameRate;
		}

		temp = m_xmlIFace->getSingleNodeContentByXPath("/project_preferences/graphics/@quality");
		if(temp.length() > 0) {
			if(temp == "high") {
				m_GraphicsQualitySetting = BOINCClientAdapter::HighGraphicsQualitySetting;
			}
			else if(temp == "medium") {
				m_GraphicsQualitySetting = BOINCClientAdapter::MediumGraphicsQualitySetting;
			}
			else {
				m_GraphicsQualitySetting = BOINCClientAdapter::LowGraphicsQualitySetting;
			}
		}

		temp = m_xmlIFace->getSingleNodeContentByXPath("/project_preferences/graphics/@width");
		if(temp.length() > 0) {
			converter.clear();
			converter.str(temp);
			converter >> dec >> m_GraphicsWindowWidth;
		}

		temp = m_xmlIFace->getSingleNodeContentByXPath("/project_preferences/graphics/@height");
		if(temp.length() > 0) {
			converter.clear();
			converter.str(temp);
			converter >> dec >> m_GraphicsWindowHeight;
		}
	}
	catch(ios_base::failure) {
		cerr << "Error parsing project preferences! Using defaults..." << endl;
	}
}

string BOINCClientAdapter::applicationInformation() const
{
	return m_SharedMemoryAreaContents;
}

string BOINCClientAdapter::projectInformation() const
{
	string temp("<project_preferences />\n");
	
	// preferences available? (BOINC initializes them with 0)
	if(m_UserData.project_preferences != 0) {
		// ugly workaround for incomplete XML fragment returned by BOINC!
		temp = "<project_preferences>\n";
		temp += string(m_UserData.project_preferences);
		temp += "</project_preferences>\n";
	}

	return temp;
}

int BOINCClientAdapter::graphicsFrameRate() const
{
	return m_GraphicsFrameRate;
}

BOINCClientAdapter::GraphicsQualitySetting BOINCClientAdapter::graphicsQualitySetting() const
{
	return m_GraphicsQualitySetting;
}

int BOINCClientAdapter::graphicsWindowWidth() const
{
	return m_GraphicsWindowWidth;
}

int BOINCClientAdapter::graphicsWindowHeight() const
{
	return m_GraphicsWindowHeight;
}

string BOINCClientAdapter::coreVersion() const
{
	stringstream buffer;

	// build common version string
	buffer	<< m_UserData.major_version << "."
			<< m_UserData.minor_version << "."
			<< m_UserData.release;

	return string(buffer.str());
}

string BOINCClientAdapter::applicationName() const
{
	return string(m_UserData.app_name);
}

string BOINCClientAdapter::applicationVersion() const
{
	stringstream buffer;
	buffer << m_UserData.app_version;

	return string(buffer.str());
}

string BOINCClientAdapter::userName() const
{
	return string(m_UserData.user_name);
}

string BOINCClientAdapter::teamName() const
{
	return string(m_UserData.team_name);
}

double BOINCClientAdapter::userCredit() const
{
	return m_UserData.user_total_credit;
}

double BOINCClientAdapter::userRACredit() const
{
	return m_UserData.user_expavg_credit;
}

double BOINCClientAdapter::hostCredit() const
{
	return m_UserData.host_total_credit;
}

double BOINCClientAdapter::hostRACredit() const
{
	return m_UserData.host_expavg_credit;
}

string BOINCClientAdapter::wuName() const
{
	return string(m_UserData.wu_name);
}

double BOINCClientAdapter::wuFPOpsEstimated() const
{
	return m_UserData.rsc_fpops_est;
}


double BOINCClientAdapter::wuFPOpsBound() const
{
	return m_UserData.rsc_fpops_bound;
}


double BOINCClientAdapter::wuMemoryBound() const
{
	return m_UserData.rsc_memory_bound;
}

double BOINCClientAdapter::wuDiskBound() const
{
	return m_UserData.rsc_disk_bound;
}

double BOINCClientAdapter::wuCPUTimeSpent() const
{
	return m_UserData.wu_cpu_time;
}