#include "PduMap.h"


//PduData class methods

PduData::PduData(size_t outlets_num, string pdu_name, Tango::DeviceProxy *pdu_proxy)
{
	outlets_count = outlets_num; 
	name = pdu_name;
	proxy = pdu_proxy;
	errors = NULL;
}

void PduData::setOutletsCount(size_t count)
{
	outlets_count = count;
}

size_t PduData::getOutletsCount()
{
	return outlets_count;
}

string PduData::getDevName()
{
	return name;
}

void PduData::setDevName(string pdu_name)
{
	name = pdu_name;
}

Tango::DeviceProxy* PduData::getProxy()
{
	return proxy;
}

void PduData::setProxy(Tango::DeviceProxy *devPrx)
{
	proxy = devPrx;
}

void PduData::setErrors(Tango::DevFailed *errs)
{
	if(errors)	
		delete errors;
	errors = errs;
}
Tango::DevFailed* PduData::getErrors()
{
	return errors;
}

//ATTRIBUTE NAME
// --> Value
string PduData::getLabelByPos(int pos)
{
	validateOutletIdx(pos);
	return label_ls[pos].value;
}

string PduData::getLabelByAttr(string attr)
{
	validateLabelAttr(attr);
	int pos = atoi(attr.substr(4, 2).c_str()) - 1;
	return getLabelByPos(pos);
}

void PduData::setLabelByPos(int pos, string value)
{
	validateOutletIdx(pos);
	label_ls[pos].value = value;
}

void PduData::setLabelByAttr(string attr, string value)
{
	validateLabelAttr(attr);
	int pos = atoi(attr.substr(4, 2).c_str()) - 1;
	setLabelByPos(pos, value);
}

// --> Event
void PduData::setLabelEventId(int pos, int id)
{
	validateOutletIdx(pos);
	label_ls[pos].event = id;
}
void PduData::setLabelEventId(string attr, int id)
{
	validateLabelAttr(attr);
	int pos = atoi(attr.substr(4, 2).c_str()) - 1;
	setLabelEventId(pos, id);
}

//ATTRIBURTE STATE
// --> Value
string PduData::getStateByPos(int pos)
{
	validateOutletIdx(pos);
	return state_ls[pos].value;
}

/**
string PduData::getStateByAttr(string attr)
{
	validateStateAttr(attr);
	int pos = atoi(attr.substr(5, 2).c_str()) - 1;
	return getStateByPos(pos);
}
*/

void PduData::setStateByPos(int pos, string value)
{
	validateOutletIdx(pos);
	state_ls[pos].value = value;
}

void PduData::setStateByAttr(string attr, string value)
{
	validateStateAttr(attr);
	int pos = atoi(attr.substr(5, 2).c_str()) - 1;
	setStateByPos(pos, value);
}	
// --> Event
void PduData::setStateEventId(int pos, int id)
{
	validateOutletIdx(pos);
	state_ls[pos].event = id;
}

void PduData::setStateEventId(string attr, int id)
{
	validateStateAttr(attr);
	int pos = atoi(attr.substr(5, 2).c_str()) - 1;
	setStateEventId(pos, id);
}

vector<int> PduData::getAllEvents()
{
	vector<int> events;
	size_t len = getOutletsCount();
	for(unsigned int i = 0; i < len; i++ )
	{
		events.push_back(state_ls[i].event);
		events.push_back(label_ls[i].event);
	}
	return events;
}

bool PduData::matchLabelPattern(string label)
{
	regex label_pattern(label, regex_constants::icase);
	for(unsigned int i = 0; i < outlets_count; i++)
		if (regex_match(label_ls[i].value, label_pattern))
			return true;
	return false;
}

void PduData::addIfMatchLabelPattern(vector<string> &outlets_ls, string label)
{
	regex label_pattern(label, regex_constants::icase);
	for(unsigned int i = 0; i < outlets_count; i++)
		if (regex_match(label_ls[i].value, label_pattern))
			outlets_ls.push_back(label_ls[i].value);
}

void PduData::validateOutletIdx(unsigned int idx)
{
	if (idx >= outlets_count) 
	{
		stringstream idx_ss;
		string idx_s;
		idx_ss << idx;
		idx_ss >> idx_s;
		throw NotValidOutletIndex(name, idx_s);
	}
}

void PduData::validateStateAttr(string attr_name)
{
	/** Expected "stateXX" ('X' is a digit) in attr_name **/
	
	if (attr_name.size() == 7 && attr_name.substr(0, 5).compare("state") == 0) 
		if(isdigit(attr_name[5]) && isdigit(attr_name[6]))
			return;
	throw NotValidAttrName(attr_name);
}

void PduData::validateLabelAttr(string attr_name)
{
	/** Expected "nameXX" ('X' is a digit) in attr_name **/
	
	if (attr_name.size() == 6 && attr_name.substr(0, 4).compare("name") == 0) 
		if(isdigit(attr_name[4]) && isdigit(attr_name[5]))
			return;
	throw NotValidAttrName(attr_name);
}

// "PduMap" CLASS METHODS

PduMap* PduMap::instance = NULL;

PduMap::PduMap(){}

void PduMap::resetInstance()
{
	delete instance;
	instance = NULL;
}

PduMap* PduMap::getInstance()
{
	if (instance == 0)
	{
		instance = new PduMap();
	}
	return instance;
}

void PduMap::addPduData(size_t outletsNum, string pdu_name, Tango::DeviceProxy *pdu_proxy)
{
	if(isPduName(pdu_name))
		throw PduNameAlreadyExists(pdu_name);
	else
		pduData_ls.emplace_back(outletsNum, pdu_name, pdu_proxy);
}

void PduMap::deletePduData(string pdu_name)
{
	unsigned int idx = getIdxPduData(pdu_name);
	Tango::DeviceProxy *prxy = pduData_ls[idx].getProxy();
	Tango::DevFailed *errs = pduData_ls[idx].getErrors();
	if (prxy)
		delete prxy;
	if(errs)
		delete errs;
	pduData_ls.erase(pduData_ls.begin() + idx);
}

void PduMap::getPduInfoFromName(vector<string> &pdu_info, string pdu_name)
{	
	unsigned int idx = getIdxPduData(pdu_name);
	Tango::DevFailed *errors = pduData_ls[idx].getErrors();
	if(errors)
		throw *errors;
	size_t outlets_num = pduData_ls[idx].getOutletsCount();
	for(size_t j = 0; j < outlets_num; j++)
	{
		pdu_info.push_back(pduData_ls[idx].getLabelByPos(j));
		pdu_info.push_back(pduData_ls[idx].getStateByPos(j));
	}
}

void PduMap::getPduListFromLabel(vector<string> &pdu_ls, string label)
{
	size_t len = pduData_ls.size();
	for(unsigned int i = 0; i < len; i++)
		if(!pduData_ls[i].getErrors())
			if(pduData_ls[i].matchLabelPattern(label))
				pdu_ls.push_back(pduData_ls[i].getDevName());
}

void PduMap::getPduListFromName(vector<string> &pdu_ls, string pdu_name)
{
	regex name_pattern(pdu_name, regex_constants::icase);
	size_t len = pduData_ls.size();
	for(unsigned int i = 0; i < len; i++)
		if(regex_match(pduData_ls[i].getDevName(), name_pattern))
			pdu_ls.push_back(pduData_ls[i].getDevName());
}

void PduMap::getOutletsListFromLabel(vector<string> &outlets_ls, string label)
{
	size_t len = pduData_ls.size();
	for(unsigned int i = 0; i < len; i++)
		if(!pduData_ls[i].getErrors())
			pduData_ls[i].addIfMatchLabelPattern(outlets_ls, label);
}

void PduMap::getPduInErrorList(string &pdu_ls)
{
	size_t len = pduData_ls.size();
	pdu_ls = "";
	for(unsigned int i = 0; i < len; i++)
		if(pduData_ls[i].getErrors())
			pdu_ls += "- " + pduData_ls[i].getDevName() + "\n";
}

void PduMap::getAllPduEvents(vector<int> &events, string pdu_name)
{
	unsigned int idx = getIdxPduData(pdu_name);
	events = pduData_ls[idx].getAllEvents();
}

void PduMap::getAllPduEvents(vector<int> &events, unsigned int pos)
{
	validatePduIndex(pos);
	events = pduData_ls[pos].getAllEvents();
}

Tango::DeviceProxy* PduMap::getPduProxy(string pdu_name)
{
	unsigned int idx = getIdxPduData(pdu_name);
	return pduData_ls[idx].getProxy();
}

Tango::DeviceProxy* PduMap::getPduProxy(unsigned int pos)
{
	validatePduIndex(pos);
	return pduData_ls[pos].getProxy();
}

Tango::DevFailed* PduMap::getPduErrors(string pdu_name)
{
	unsigned int idx = getIdxPduData(pdu_name);
	return pduData_ls[idx].getErrors();
}

Tango::DevFailed* PduMap::getPduErrors(unsigned int pos)
{
	validatePduIndex(pos);
	return pduData_ls[pos].getErrors();
}

void PduMap::updateOutletState(string pdu_name, string attr_name, string new_value)
{
	unsigned int idx = getIdxPduData(pdu_name);
	pduData_ls[idx].setStateByAttr(attr_name, new_value);
}

void PduMap::updateOutletLabel(string pdu_name, string attr_name, string new_value)
{
	unsigned int idx = getIdxPduData(pdu_name);
	pduData_ls[idx].setLabelByAttr(attr_name, new_value);
}

void PduMap::updateAttrEventId(string pdu_name, string attr_name, int id)
{
	unsigned int idx = getIdxPduData(pdu_name);
	if (attr_name.substr(0, 5).compare("state") == 0)
	{	
		pduData_ls[idx].setStateEventId(attr_name, id);
		return;
	}
	else if (attr_name.substr(0, 4).compare("name") == 0)
	{
		pduData_ls[idx].setLabelEventId(attr_name, id);
		return;
	}
	else
	{
		throw NotValidAttrName(attr_name);
	}
}

void PduMap::updatePduError(string pdu_name, Tango::DevFailed *errs)
{
 	unsigned int idx = getIdxPduData(pdu_name);
	pduData_ls[idx].setErrors(errs);
}

string PduMap::toString()
{
	stringstream pdu_map_str;
	size_t pdu_num = pduData_ls.size();
	for(size_t i = 0; i < pdu_num; i++)
	{
		pdu_map_str << "PDU: " << pduData_ls[i].getDevName() << ": " << endl;
		size_t outlest_num = pduData_ls[i].getOutletsCount();
		for(size_t j = 0; j < outlest_num; j++)
		{
			pdu_map_str << "Outlet" << j + 1 << ": [ " 
						<< pduData_ls[i].getLabelByPos(j) << " | "
						<< pduData_ls[i].getStateByPos(j) << " ], " 
						<< endl << flush; 
		}
	}
	return pdu_map_str.str();
}

size_t PduMap::getPduNum()
{
	return pduData_ls.size();	
}

bool PduMap::inError()
{
	size_t len = pduData_ls.size();
	for(unsigned int i = 0; i < len; i++)		
		if(pduData_ls[i].getErrors())
			return true;
	return false;
}

void PduMap::getCompletePduList(vector<string> pdu_ls)
{
	size_t len = pduData_ls.size();
	for(unsigned int i = 0; i < len; i++)
		pdu_ls.push_back(pduData_ls[i].getDevName());
}

unsigned int PduMap::getIdxPduData(string pdu_name)
{
	size_t len = pduData_ls.size();
	for(unsigned int i = 0 ; i < len; i++)
		if(pdu_name.compare(pduData_ls[i].getDevName()) == 0)
			return i;
	throw NotValidPduName(pdu_name);
}

bool PduMap::isPduName(string pdu_name)
{
	size_t len = pduData_ls.size();
	for(unsigned int i = 0 ; i < len; i++)
		if(pdu_name.compare(pduData_ls[i].getDevName()) == 0)
			return true;
	return false;
}

void PduMap::validatePduIndex(unsigned int idx)
{
	size_t num = pduData_ls.size();
	if (idx >= num)
	{
		stringstream idx_ss;
		string idx_s;
		idx_ss << idx;
		idx_ss >> idx_s;
		throw NotValidPduIndex(idx_s);
	}
}