#include "traffic_agent.cpp"
const string testMessage = "test_message";
const double measurement_cycle_time = 90; // s
const double saturated_stream = 0.5; // car/s
const int sample_cycles = 3;
const double min_profit = 30.0;






class TrafficAgentCycleTime : public BaseTrafficAgent {
public:
	int Tc;
	static const int minTc = 40;  // nepouziva se
	static const int maxTc = 200; // nepouziva se
	static const int stepTc = 2;	
	static const int d_Tc = 3; // pocet navrhovanych delek cyklu	
	int idealTc;
	int cycle_counter;
	double max_profit;
	unsigned int n_of_broadcast;
	Array <int> lane_sum;
	//Array <SignalGroup*> signal_groups;
	Array <double> received_Tcs;
	Array <double> received_profit_sum;
	Array <double> received_queue_diffs;
	// pomocne logovaci soubory
	ofstream Tcs;
	ofstream Ros;
	ofstream Qs;


// POMOCNE FUNKCE

	void printVector ( RV rv_vector, vec vector, string description ) {
		cout << endl << description << " " << name << endl;
		int k = 0;
		for ( int i = 0; i < rv_vector.length(); i ++ ) {
			cout << rv_vector.name(i) << " : ";
			for ( int j = 0; j < rv_vector.size(i); j ++ ) {				
				cout << vector(k) << " ";
				k ++;
			}
			cout << endl;
		}
		cout << endl;
	}

	void add2array( Array <double> &arr, double item  ) {
		arr.set_length( arr.length()+1, true );
		arr(arr.length()-1) = item;
		//cout << endl << "addin to array value " << item << endl;
	}

	void echo ( string message ) {
		cout << name << " hlasi: " << message << endl;		
	}

	unsigned int find_index ( RV rv_vector, string index_string ) {
		for ( unsigned int i = 0; i < rv_vector.length(); i ++) {
			if ( rv_vector.name(i) == index_string )
				return i;
		}
		return rv_vector.length();
	}

	unsigned int find_index ( Array <string> arr, string index_string ) {
		for ( unsigned int i = 0; i < arr.length(); i ++) {
			if ( arr(i) == index_string )
				return i;
		}
		return arr.length();
	}

// VYPOCETNI FUNKCE

	// soucet cekacich autocasu
	double getWT ( double time_cycle ) {
		double sum = 0;		
		for ( int i = 0; i <lanehs.length(); i ++ ) {
			sum += lanehs(i)->getWT( time_cycle );
		}		
		return sum;
	}

	double getProfit ( double tc ) {
		if ( tc != Tc )
			return (getWT(Tc) - getWT(tc))*( 1/ (Tc-tc)*(Tc-tc) );
		else
			return 0;
	}

	double getQueue () {
		double queue = 0;
		for ( int i = 0; i <lanehs.length(); i ++ ) {
			queue += lanehs(i)->getAverageQueueLength();
		}
		return queue;
	}

	double getAverageRo () {
		double Ro_sum = 0;
		for ( int i = 0; i <lanehs.length(); i ++ ) {
			Ro_sum += lanehs(i)->getRo();
		}
		return Ro_sum/lanehs.length();
	}

	
	

	void send2neighbour( Setting &set, int i, string messageName, double messageValue ) {		
		if ( i < neighbours.length() ) {
			Setting &msg =set.add(Setting::TypeGroup);
			UI::save( neighbours(i), msg, "to" );
			UI::save (name,msg,"from");
			UI::save( messageName, msg, "what" );
			UI::save( messageValue, msg, "value" );
		}
		else {
			//throw new Exception("soused "+((string)i)+" neexistuje");
			//cout << endl << endl << "soused " << i << mimo
			std::stringstream out;
			out << "soused " << i << " neexistuje";
			//throw out.str();
			cout << out.str();
		}
	}

	void send2allNeighbours ( Setting &set, string messageName, double messageValue ) {
		for ( int i = 0; i < neighbours.length(); i++ ) {
			send2neighbour( set, i, messageName, messageValue );
		}
	}

	

	
// FUNKCE VOLANE V main_loop NA ZACATKU
	
	void validate (){
		rv_action = RV("Tc",1);
		rv_action.add( RV( stage_names, ones_i(stage_names.length()) ) );
		Tc = 80;		
		idealTc = Tc;
		max_profit = 0;
		// prijate Tc a profity
		received_profit_sum.set_length( (maxTc - minTc)/stepTc +1 );
		received_Tcs.set_length( (maxTc - minTc)/stepTc +1 );
		cycle_counter = 0;

		BaseTrafficAgent::validate();

		for ( int i=0; i<lanehs.length(); i ++ ) {
			unsigned int index = find_index( green_names, name + "_" + lanehs(i)->getSG() );
				lanehs(i)->green_time_ratio = green_times( index );
		}

		// pomocne logovaci soubory
		Tcs.open("Tcs.dat");
		Tcs << "cyklus\tTc\tprofit\n";
		stringstream Ro_file_name;
		Ro_file_name << "Ro_" << name << ".dat";
		if ( name == "495" ) {
			Ros.open("Ros_495.dat");
			Qs.open("Qs_495.dat");
		}
		else { 
			Ros.open("Ros_601.dat");
			Qs.open("Qs_601.dat");
		}
		Ros << "Hustoty provozu ve frontach" << endl << "n";
		Qs << "Prumerna delka fronty" << endl << "n";
		for ( int i = 0; i < lanehs.length(); i++ ) {
			Ros << "\t" << lanehs(i)->getQueueName();
			Qs << "\t" << lanehs(i)->getAverageQueueLength();
		}
		Ros << endl;
	}

// FUNKCE VOLANE V main_loop V KAZDEM CYKLU

	void adapt (const vec &glob_dt) {
		// vynulovani pole prijatych rozdilu fronty
		received_queue_diffs.set_length(0);

		// inicializes vars of cycle of broadcast
		n_of_broadcast = 0;
		max_profit = 0;
		//cout << endl << name << ":" << endl << endl;
		// predani aktualnich dat do LaneHandler lanehs
		Ros << cycle_counter;
		Qs << cycle_counter;
		for ( int i=0; i<lanehs.length(); i ++ ) {
			lanehs(i)->inputs( 0 ) = inputs( 2*find_index(rv_inputs, lanehs(i)->rv_inputs.name(0) ) );
			lanehs(i)->inputs( 1 ) = inputs( 2*find_index(rv_inputs, lanehs(i)->rv_inputs.name(0) ) + 1 );
			lanehs(i)->Tc = Tc;
			lanehs(i)->addQueueLength( queues( find_index( rv_queues, lanehs(i)->getQueueName() ) ) );
			
			//lanehs(i)->echo();
			// pomocne logovaci soubory
			Ros << ";" << lanehs(i)->getRo();
			Qs << ";" << queues( find_index( rv_queues, lanehs(i)->getQueueName() ) )<<";"<< lanehs(i)->getAverageQueueLength()<< ";";
		}
		Ros <<";;"<<getAverageRo() << endl;
		Qs <<";;"<<getQueue() << endl;

		if ( cycle_counter % sample_cycles == 0 ) {
			//cout << name << " queue DIFF " << getRelativeQueueDiff() << endl;
			
			for ( int i=0; i<lanehs.length(); i ++ ) {
			
			
				//lanehs(i)->echo();
				
			
			//cout << name << " " << i << " "  << lanes(i).sg << " " << lanes(i).queue  << " input " << queues(i) << endl;
			}
		}

		for ( int i = 0; i < received_profit_sum.length(); i++ )
			received_profit_sum(i) = 0;


		BaseTrafficAgent::adapt(glob_dt);
	}

	void broadcast(Setting &set){		
		
		// 1. cycle of communication
		// sends all tcs in range +- d_tc and expected profits according to tc
		// format: tc_index, profit_index
		if ( n_of_broadcast == 0  ) {
			//cout << endl << name+" PROFITS:";
			int broadcast_width = (maxTc - minTc)/stepTc + 1;
			for ( int i = 0; i < broadcast_width; i++ ) {				
				int time_cycle = minTc + i*stepTc;
				double profit = getProfit(time_cycle);
				//cout << "\t" << time_cycle << " " <<profit;
				// send tc
				stringstream time_cycle_stream;
				time_cycle_stream << "tc_" << (i);
				send2allNeighbours( set, time_cycle_stream.str(), time_cycle);
				
				// send profit
				stringstream profit_stream;
				profit_stream << "profit_" << (i);
				send2allNeighbours( set, profit_stream.str(), profit );				
			}
			
			//cout << endl;
		}
		
		n_of_broadcast ++;
	}

	void receive(const Setting& msg){
		string what;
		string from;
		double val;		
		UI::get(what, msg, "what", UI::compulsory);
		UI::get(from, msg, "from", UI::compulsory);
		UI::get(val, msg, "value");

				
		//cout << name << " receiving from " << from << " " << what << " : " << val<<endl;
		
		if ( what.substr(0,2) == "tc" ) {			
			istringstream tc_i( what.substr(3, what.length()-3) );
			int index;
			tc_i >> index;
			received_Tcs( index ) = val;
			//cout << "tc " << val << endl;
		}
		if ( what.substr(0,6) == "profit" ) {				
			istringstream profit_i( what.substr(7, what.length()-7) );
			int index;
			profit_i >> index;
			received_profit_sum( index ) = received_profit_sum( index ) + val;
			//cout << "pridavam profit " << val << endl;			
		}
		
		
		if ( what == "new_stable_state" ) {
			//echo("new_stable_state");
			BaseTrafficAgent::receive(msg);		
		}	
		
	}

	void act (vec &glob_ut){
		
		if ( cycle_counter >= 10  && (cycle_counter %sample_cycles) == 0 ) {
			// choose tc with max profit		
			//cout << name << " soucet front " << getQueue()<< endl;
			//cout << endl << name << " received + my profits "<<endl;;
			idealTc = Tc;
			max_profit = 0;
			for ( int i = 0; i < received_profit_sum.length(); i ++ ) {
				int time_cycle = received_Tcs(i);
				double profit = received_profit_sum(i) + getProfit(time_cycle);
				//cout << time_cycle << " " << profit << endl;
				if ( profit > max_profit && profit > min_profit ) {
					max_profit = profit;
					idealTc = time_cycle;
				}
			}
			
			
			
			// NASTAVENI Tc
			//Tc = idealTc;
			//Tc = 40 + (cycle_counter%4)*80;
			int dTc = 0;
			if ( idealTc - Tc >= 2 )
				dTc = 2;
			if ( Tc - idealTc >=2 )
				dTc = -2;
			if ( idealTc - Tc >= 4 )
				dTc = 4;
			if ( Tc - idealTc >=4 )
				dTc = -4;
			Tc += dTc;
			//Tc = 84;
			cout << endl << name << "Tc: " << Tc << " idalni Tc " << idealTc << " se ziskem " << max_profit << endl;
			// nastaveni delky cyklu na Tc
			vec action;
			action.set_size(rv_action._dsize());	
			int st;
			int stage_time_sum = 0;  // soucet delky fazi
			action(find_index(rv_action, "Tc")) = Tc;		
			for ( int i =0; i < stage_names.length(); i ++) {			
				if ( (i+1) < stage_names.length() ) {
					st = round(((double)(stage_times(i)*Tc))/80.0);
					stage_time_sum += st;
					action(find_index(rv_action, stage_names(i))) = st;
				}
				else // dopocitani posledni faze - oprava zaokrouhlovaci chyby
					action(find_index(rv_action, stage_names(i))) = Tc - stage_time_sum;
			}
			action2ds.filldown(action,glob_ut);
		}
		Tcs << cycle_counter << ";" << Tc<< ";" << idealTc << ";" << max_profit <<  endl;

		cycle_counter ++;
	}
	
	// KONEC
	void finalize() {
		Tcs.close();
	}



};
UIREGISTER(TrafficAgentCycleTime);