/*###############################################################################
# Linux Management Providers (LMP), System Memory provider package
# Copyright (C) 2008 Guillaume BOTTEX, ETRI <guillaumebottex@etri.re.kr, guillaumebottex@gmail.com>
#
# This program is being developed under the "OpenDRIM" project.
# The "OpenDRIM" project web page: http://opendrim.sourceforge.net
# The "OpenDRIM" project mailing list: opendrim@googlegroups.com
#
# This program 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.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#################################################################################

#################################################################################
# To contributors, please leave your contact information in this section
# AND comment your changes in the source code.
#
# Modified by <Author(s)>, <Affiliation>, <Year>
###############################################################################*/

#include "OpenDRIM_MemoryAccess.h"

const string systemCreationClassName = "OpenDRIM_ComputerSystem";
const string creationClassName = "OpenDRIM_Memory";
const string DeviceID = "Total Memory";
string systemName;

int SystemMemory_OpenDRIM_Memory_load(const CMPIBroker* broker, string& errorMessage) {
	_E_;
	CF_assert(CF_getSystemName(systemName, errorMessage));
	_L_;
	return OK;
}

int SystemMemory_OpenDRIM_Memory_unload(string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return OK;
}

int SystemMemory_OpenDRIM_Memory_retrieve(const CMPIBroker* broker, const CMPIContext* ctx, vector<OpenDRIM_Memory>& result, const char** properties, string& errorMessage, const string& discriminant) {
	_E_;
	//Initialize an instance
	OpenDRIM_Memory instance;
	instance.setSystemCreationClassName(systemCreationClassName);
	instance.setSystemName(systemName);
	instance.setCreationClassName(creationClassName);
	instance.setDeviceID(DeviceID);

	if (discriminant == "ei")
		CF_assert(SystemMemory_OpenDRIM_Memory_populate(instance, errorMessage));
	result.push_back(instance);
	_L_;
	return OK;
}

int SystemMemory_OpenDRIM_Memory_getInstance(const CMPIBroker* broker, const CMPIContext* ctx, OpenDRIM_Memory& instance, const char** properties, string& errorMessage) {
	_E_;
	if( instance.SystemCreationClassName != systemCreationClassName ||
			instance.CreationClassName != creationClassName ||
			instance.SystemName != systemName ||
			instance.DeviceID != DeviceID)
	{
		errorMessage = "Invalid path";
		return NOT_FOUND;
	}
	
	CF_assert(SystemMemory_OpenDRIM_Memory_populate(instance, errorMessage));
	_L_;
	return OK;
}

int SystemMemory_OpenDRIM_Memory_setInstance(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& newInstance, const OpenDRIM_Memory& oldInstance, const char** properties, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_createInstance(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_deleteInstance(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_RequestStateChange(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, const OpenDRIM_Memory_RequestStateChange_In& in, OpenDRIM_Memory_RequestStateChange_Out& out, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_SetPowerState(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, const OpenDRIM_Memory_SetPowerState_In& in, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_Reset(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_EnableDevice(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, const OpenDRIM_Memory_EnableDevice_In& in, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_OnlineDevice(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, const OpenDRIM_Memory_OnlineDevice_In& in, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_QuiesceDevice(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, const OpenDRIM_Memory_QuiesceDevice_In& in, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_SaveProperties(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_RestoreProperties(const CMPIBroker* broker, const CMPIContext* ctx, const OpenDRIM_Memory& instance, unsigned int& returnValue, string& errorMessage) {
	_E_;
	// TODO
	_L_;
	return NOT_SUPPORTED;
}

int SystemMemory_OpenDRIM_Memory_populate(OpenDRIM_Memory& instance, string& errorMessage) {
	_E_;

	/*
	 * Properties to fill from profile
	 * + Mandatory:
	 * [X] SystemCreationClassName  [KEY]
	 * [X] SystemName               [KEY]
	 * [X] CreationClassName        [KEY]
	 * [X] DeviceID                 [KEY]
	 * [X] Volatile
	 * [X] Access
	 * [X] BlockSize
	 * [X] NumberOfBlocks
	 * [X] ConsumableBlocks
	 * [X] EnabledState
	 * [X] RequestedState
	 * [X] OperationalStatus
	 * [X] HealthState
	 * [X] ElementName
	 */

	vector<void*> memory_infos;
	void *start_ptr;
	char *tableAddress;
	_smbios_entry_point *anchor;
	vector<unsigned short> operationalStatus;
	bool bVolatile=true;
	unsigned long long blockSize;
	unsigned long long consumableBlocks=0;
	unsigned long long numberOfBlocks=0;
	unsigned short access;
	unsigned short healthState;
	u32 start_address,end_address;

	if((start_ptr=SMBIOS_getRawData(_SMBIOS_ANCHOR_SEARCH_OFFSET,_SMBIOS_ANCHOR_SEARCH_RANGE,errorMessage))==NULL)
		return FAILED;

	anchor = SMBIOS_getEntryPoint(start_ptr);

	if((tableAddress=(char*)SMBIOS_getRawData(anchor->structure_table_address,anchor->structure_table_length,errorMessage))==NULL)
	{
		free(start_ptr);
		return FAILED;
	}

	SMBIOS_getStructure(memory_infos,tableAddress,anchor-> number_of_smbios_structure,_MEMORY_DEVICE);
	for(unsigned int i=0;i<memory_infos.size();i++)
	{
		u16 size=0;

		bVolatile = bVolatile && CF_isVolatileMemory(((memory_device*)memory_infos[i])->memory_type,((memory_device*)memory_infos[i])->type_detail);

		size=((memory_device*)memory_infos[i])->size;
		if(size!=0 && size!=0xFFFF)
		{
			if(size&0x8000)
				numberOfBlocks+=(size & 0x7FFF)*1024; // kB
			else
				numberOfBlocks+=size*1024*1024;	// MB
		}

		access=CF_getAccess(((memory_device*)memory_infos[i])->memory_type);
	}

	SMBIOS_getStructure(memory_infos,tableAddress,anchor-> number_of_smbios_structure,_MEMORY_ARRAY_MAPPED_ADDRESS);
	for(unsigned int i=0;i<memory_infos.size();i++)
	{
		start_address = ((memory_array_mapped_address*)memory_infos[i])->starting_address;
		end_address = ((memory_array_mapped_address*)memory_infos[i])->ending_address;
		consumableBlocks+=1024*(end_address-start_address+1);
	}

	SMBIOS_getStructure(memory_infos,tableAddress,anchor-> number_of_smbios_structure,_MEMORY_ERROR_INFORMATION);
	CF_getOperationalStatus(operationalStatus,memory_infos);
	healthState=CF_getHealthState(memory_infos);

	// see CIM Schema Documentation:
	// if a block concept is not valid (for example, for AggregateExtents, Memory or LogicalDisks), enter a 1
	blockSize=1;

	instance.setVolatile(bVolatile);
	instance.setAccess(access);
	instance.setBlockSize(blockSize);
	instance.setNumberOfBlocks(numberOfBlocks/blockSize);
	instance.setConsumableBlocks(consumableBlocks/blockSize);

	// Set to default values defined in the DMTF profile
	instance.setEnabledState(ES_ENABLED);
	instance.setRequestedState(RS_NOT_APPLICABLE);

	instance.setOperationalStatus(operationalStatus);
	instance.setHealthState(healthState);

	instance.setElementName(instance.DeviceID);

	free(tableAddress);
	free(start_ptr);

	_L_;
	return OK;
}

