#include <iostream>
#include <iomanip>
#include <queue>
#include <vector>
#include <algorithm>
#include <functional>
#include "customer.h"
#include "teller.h"
#include "marketsim.h"

class Market_Simulation;
Customer_Arrives::Customer_Arrives(unsigned long t, Customer* j,Market_Simulation& ms)
:event(t),this_customer(j),market_simulation(ms){}

Customer_Arrives::~Customer_Arrives(){}

Teller_Becomes_Free::Teller_Becomes_Free(unsigned long t,Teller* tp,Market_Simulation& ms)
:event(t),this_Teller(tp),market_simulation(ms){}

Teller_Becomes_Free::~Teller_Becomes_Free(){}

void Customer_Arrives::processEvent()
{
	Teller* next_Teller=market_simulation.find_free_teller(this_customer);
	
	if(next_Teller)next_Teller->start_transaction(this_customer,time,market_simulation);
	else 
	{
		next_Teller=market_simulation.find_least_customer_teller(this_customer);
		next_Teller->Customer_queue.push(this_customer);
	}
	Customer* next_Customer=new Customer(time+Customer::next_arrival());
	event* next_event=
		new Customer_Arrives(next_Customer->get_arrival_time(),next_Customer,market_simulation);
	market_simulation.scheduleEvent(next_event);
}

void Teller_Becomes_Free::processEvent()
{
	this_Teller->end_transaction(time);
	if(!((*this_Teller).Customer_queue.empty()))
	{
		Customer* next_Customer;
		next_Customer=(*this_Teller).Customer_queue.front();
		(*this_Teller).Customer_queue.pop();
		(*this_Teller).start_transaction(next_Customer,time,market_simulation);
	}
}

Market_Simulation::Market_Simulation():simulation()
{
	std::cout<<"\n Enter the number of supper Tellers";
	std::cin>>number_of_super_tellers;
	for(int i=0;i<number_of_super_tellers;++i)
		super_teller_list.push_back(Teller());
		
	std::cout<<"\n Enter the number of express Tellers";
	std::cin>>number_of_express_tellers;
	for(i=0;i<number_of_express_tellers;++i)
		express_teller_list.push_back(Teller());
	
	std::cout<<"\n Enter the number of standard Tellers";
	std::cin>>number_of_standard_tellers;
	for(i=0;i<number_of_standard_tellers;++i)
		standard_teller_list.push_back(Teller());
	
	Customer::initialize_parameters(std::cout,std::cin);
	std::cout<<"\n Enter the maximum simulation time";
	std::cin>>max_run_time;
}

void Market_Simulation::print_statistics()
{
	std::cout<<'\n';
	std::cout<<std::setw(8)<<"ID";
	std::cout<<std::setw(12)<<"avg wait t";
	std::cout<<std::setw(12)<<"avg cus ph";
	std::cout<<std::setw(12)<<"avg itm ph";
	std::cout<<std::setw(12)<<"avg fr tim";
	std::cout<<std::setw(12)<<"tot fr tim";

	std::cout<<'\n';
	for(int i=0;i<number_of_super_tellers;i++)
		super_teller_list[i].print_local_stats(*this,"sup");

	for(i=0;i<number_of_express_tellers;i++)
		express_teller_list[i].print_local_stats(*this,"exp");

	for(i=0;i<number_of_standard_tellers;i++)
		standard_teller_list[i].print_local_stats(*this,"std");
	Teller::print_qlobal_stats(*this);

}

Teller* Market_Simulation::find_free_teller(Customer* c)
{
	std::vector<Teller>::iterator p;
	if(c->get_item_number()<=8)
	{
		for(p=super_teller_list.begin();p!=super_teller_list.end();++p)
		{
			if((*p).is_free())return &(*p);
		}
	}
	if(c->get_item_number()<=16)
	{
		for(p=express_teller_list.begin();p!=express_teller_list.end();++p)
		{
			if((*p).is_free())return &(*p);
		}

	}
	
	for(p=standard_teller_list.begin();p!=standard_teller_list.end();++p)
	{
		if((*p).is_free())return &(*p);
	}
	return 0;

}

Teller* Market_Simulation::find_least_customer_teller(Customer* c)
{
	std::vector<Teller>::iterator p,result;
	int least;
	int s= (int)c->get_item_number()/8;
	switch(s)
	{
	case 0:	
		result=super_teller_list.begin();p=result;
		least=(*(super_teller_list.begin())).Customer_queue.size();
		for(;p!=super_teller_list.end();++p)
		{
			if((*p).Customer_queue.size()<=least)
			{
				least=(*p).Customer_queue.size();
				result=p;
			}
		}
		return &(*result);
		break;
	case 1:
		result=express_teller_list.begin();p=result;
		least=(*(express_teller_list.begin())).Customer_queue.size();
		for(;p!=express_teller_list.end();++p)
		{
			if((*p).Customer_queue.size()<=least)
			{
				least=(*p).Customer_queue.size();
				result=p;
			}
		}
		return &(*result);	
		break;
	default:
		result=standard_teller_list.begin();p=result;
		least=(*(standard_teller_list.begin())).Customer_queue.size();
		for(;p!=standard_teller_list.end();++p)
		{
			if((*p).Customer_queue.size()<=least)
			{
				least=(*p).Customer_queue.size();
				result=p;
			}
		}
		return &(*result);
		break;
	}
}


int main()
{
	Market_Simulation market_simulation;
	Customer* first_Customer=new Customer(Customer::next_arrival());
	event* first_event=
		new Customer_Arrives(first_Customer->get_arrival_time(),first_Customer,market_simulation);
	market_simulation.scheduleEvent(first_event);
	market_simulation.run();
	market_simulation.print_statistics();
	return 0;
}


