/*****************************************************************************
 *
 * Copyright (c) 2018-19, Zeeshan Islam, Josef Treus, Paula Herber, 
 * Westfälische Wilhelms-Universität Münster, AG Embedded Systems 
 * All rights reserved.
 *
 * This file is part of SysCSec
 *
 * SysCSec 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, either version 3 of the License, or (at your
 * option) any later version.
 *
 * SysCSec 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 SysCSec.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *  Please report any problems or bugs to: sysc.sec@uni-muenster.de
 *
 ****************************************************************************/

#define ADD 1
#define SUB 2
#define MUL 3
#define DIV 4
#define MOD 5
#define SHIFT 6
#define COMPARE 7
#define CALL 8
#define ASSIGN 9


#ifndef RessourceHandler_H
#define RessourceHandler_H

#define TIME_INTERVAL 20000
#define DEFAULT_POWER_CONSUMPTION 1
#define AMPLITUDE 100.0
#define NOISEDEGREE 5
#include <map>
#include <iostream>
#include <sstream>
#include "Utils.cpp"

using namespace std;

class RessourceHandler {
private:
	map<int,int> ressourceUsagesClient;
	map<int,int> ressourceUsagesServer;
	map<int, string> specialValues;

	RessourceHandler() {
	}

	RessourceHandler(const RessourceHandler&);
	RessourceHandler& operator=(const RessourceHandler&);

public:

	static RessourceHandler* getInstance() {
		static RessourceHandler instance;
		return &instance;
	}

	void logToFile() {
		// add noise to the PowerTraces 
		for(int i = 0; i < ressourceUsagesClient.rbegin()->first;i++){
			ressourceUsagesClient[i] += Utils::normalDistributedValue(ressourceUsagesClient[i] ,10*NOISEDEGREE);
		}
		for(int i = 0; i < ressourceUsagesServer.rbegin()->first;i++){
			ressourceUsagesServer[i] += Utils::normalDistributedValue(ressourceUsagesServer[i] , 10*NOISEDEGREE);
		}
		Utils::writeToJSObject(specialValues, ressourceUsagesClient, ressourceUsagesServer);
	}
	void logValue(int key, string val){
		specialValues[key] = (val);
	}
	void logRessourceUsage(int start, int duration, int user, int amount) {
		map<int,int> tempMap;

		/* initialize */
		for(int i = ressourceUsagesClient.size(); i < start; i++){
			ressourceUsagesClient[i] += ressourceUsagesClient[ressourceUsagesClient.size()-1];
		}
		for(int i = ressourceUsagesServer.size(); i < start; i++){
			ressourceUsagesServer[i] += ressourceUsagesServer[ressourceUsagesServer.size()-1];
		}
		
		/* select map of client or server */
		if(user == 1){
			tempMap = ressourceUsagesClient;
		}else if(user == 2){
			tempMap = ressourceUsagesServer;
		}else{
			return;
		}
		/** write to map **/
		for(int i = start; i < start+duration; i++){
			tempMap[i] += amount;
		}
		/* replace old map with new map */
		if(user == 1){
			ressourceUsagesClient = tempMap;
		}else{
			ressourceUsagesServer = tempMap;
		}
		/* reset maps */
		for(int i = ressourceUsagesClient.size(); i <= start+duration; i++){
			ressourceUsagesClient[i] += 0;
		}
		for(int i = ressourceUsagesServer.size(); i <= start+duration; i++){
			ressourceUsagesServer[i] += 0;
		}
	}

	void logOperationPowerExpense(int user, int operation){
		if(user == 1 || user == 2){
			stringstream ss;
			ss << sc_time_stamp().value();
			int time = (int)(stol(ss.str())/TIME_INTERVAL);
			switch(operation){
				case ADD:
					logRessourceUsage(time, 2, user, 2*AMPLITUDE);
					break;
				case SUB:
					logRessourceUsage(time, 2, user, 2*AMPLITUDE);
					break;
				case MUL:
					logRessourceUsage(time, 4, user, 2*AMPLITUDE);
					break;
				case DIV:
					logRessourceUsage(time, 4, user, 2*AMPLITUDE);
					break;
				case MOD:
					logRessourceUsage(time, 4, user, 2*AMPLITUDE);
					break;
				case SHIFT:
					logRessourceUsage(time, 1, user, 1*AMPLITUDE);
					break;
				case COMPARE:
					logRessourceUsage(time, 2, user, 1*AMPLITUDE);
					break;
				case CALL:
					logRessourceUsage(time, 1, user, 1*AMPLITUDE);
					break;
				case ASSIGN:
					logRessourceUsage(time, 1, user, 1*AMPLITUDE);
					break;
				
			}
			
		}

	}
};

#endif
