/*
 * AresVChemicalMatrix.cpp
 *
 *  Created on: 11-Jul-08
 *      Author: indurks
 */

#include "AresV.h"

using namespace std;

ChemicalMatrix::ChemicalMatrix(GuardedTables &myGuardedTables)
{
	numReactionsRemoved = 0;

	for(int i = 0; i < (signed)myGuardedTables.getNumRxns(); i++) {
		Reaction r = myGuardedTables.getRxn(i);
		chemMatrix[r.r1].second[r.r2].push_back(i);

		if(r.r1 != r.r2)
			chemMatrix[r.r2].second[r.r1].push_back(i);
	}

	for(int i = 0; i < (signed)myGuardedTables.getNumRxns(); i++) {

		ComputeAffectedChemicalsForReaction(myGuardedTables.myRxns[i]);

		for(list<int>::iterator iter = myGuardedTables.myRxns[i].affectedChemicals.begin(); iter != myGuardedTables.myRxns[i].affectedChemicals.end(); iter++)
			chemMatrix[*iter].first += myGuardedTables.myRxns[i].sumPropensity;
	}
}

void ChemicalMatrix::ProcessReaction(Reaction &r, int z) {
	if(z == 0)
		return;

	int one = 0;
	if(r.r1 == z) one--;
	if(r.r2 == z) one--;
	if(r.p1 == z) one++;
	if(r.p2 == z) one++;

	if(one != 0) {
		bool alreadyInThere = false;

		for(list<int>::iterator iter = r.affectedChemicals.begin(); iter != r.affectedChemicals.end(); iter++)
			if(*iter == z)
				return;

		r.affectedChemicals.push_back(z);
	}
}

// Compute Affected Chemicals for a Reaction
void ChemicalMatrix::ComputeAffectedChemicalsForReaction(Reaction &r) {
	ProcessReaction(r, r.r1);
	ProcessReaction(r, r.r2);
	ProcessReaction(r, r.p1);
	ProcessReaction(r, r.p2);
}

bool ChemicalMatrix::hasChemical(int chem)
{
	return (chemMatrix.find(chem) != chemMatrix.end());
}

int ChemicalMatrix::getNumReactions(int chem)
{
	if(!hasChemical(chem))
	{
		cout << "Tried to Query Chemical# " << chem << " but no such chemical in ChemMatrix..." << std::endl;
		exit(0);
	}

	int sum = 0;

	for(map<int, list<int> >::iterator iter = chemMatrix[chem].second.begin(); iter != chemMatrix[chem].second.end(); iter++)
		sum += iter->second.size();

	return sum;
}

double ChemicalMatrix::getScore(int chem)
{
	if(!hasChemical(chem))
	{
		cout << "getScore - Tried to Query Chemical# " << chem << " but no such chemical in ChemMatrix..." << std::endl;
		exit(0);
	}

	return chemMatrix[chem].first;
}

bool ChemicalMatrix::listContainsInt(list<int> &theList, int n)
{
	for(list<int>::iterator iter = theList.begin(); iter != theList.end(); iter++)
		if(*iter == n)
			return true;
	return false;
}

// This is the only function in the program that can modify chemMatrix after
// its initial construction.
Reaction ChemicalMatrix::RemoveReaction(Reaction r)
{
	if(r.r1 == r.r2)
	{
		if(hasChemical(r.r1))
		{
			// First try to remove the reaction
			if(listContainsInt(chemMatrix[r.r1].second[r.r1], r.ID))
				chemMatrix[r.r1].second[r.r1].remove(r.ID);
			else
			{
				cout << "Could not find reaction with ID = " << r.ID << std::endl;
				exit(0);
			}

			// Now try to see if we need to remove the list it was in
			if(chemMatrix[r.r1].second[r.r1].size() == 0)
				chemMatrix[r.r1].second.erase(r.r1);

			// Now see if the entire chemical is empty
			if(chemMatrix[r.r1].second.size() == 0)
				chemMatrix.erase(r.r1);
		}
		else
		{
			cout << "ERROR in r.r1 == r.r2" << std::endl;
			exit(0);
		}
	}
	else
	{
		if(hasChemical(r.r1) && hasChemical(r.r2))
		{
			// First try to remove the reaction through first reactant
			if(listContainsInt(chemMatrix[r.r1].second[r.r2], r.ID))
				chemMatrix[r.r1].second[r.r2].remove(r.ID);
			else
			{
				cout << "Could not find reaction with ID = " << r.ID << std::endl;
				exit(0);
			}

			// Now try to see if we need to remove the list it was in
			if(chemMatrix[r.r1].second[r.r2].size() == 0)
				chemMatrix[r.r1].second.erase(r.r2);

			// Now see if the entire chemical is empty
			if(chemMatrix[r.r1].second.size() == 0)
				chemMatrix.erase(r.r1);

			// First try to remove the reaction through second reactant
			if(listContainsInt(chemMatrix[r.r2].second[r.r1], r.ID))
				chemMatrix[r.r2].second[r.r1].remove(r.ID);
			else
			{
				cout << "Could not find reaction with ID = " << r.ID << std::endl;
				exit(0);
			}

			// Now try to see if we need to remove the list it was in
			if(chemMatrix[r.r2].second[r.r1].size() == 0)
				chemMatrix[r.r2].second.erase(r.r1);

			// Now see if the entire chemical is empty
			if(chemMatrix[r.r2].second.size() == 0)
				chemMatrix.erase(r.r2);
		}
		else
		{
			cout << "ERROR in r.r1 != r.r2" << std::endl;
			exit(0);
		}
	}

	numReactionsRemoved += 1;

	return r;
}
