package lt.ktu.gmj.tasks;

import java.util.Random;
import java.util.Vector;

import lt.ktu.gmj.Point;

public class WalrasMarket 
{
	public WalrasMarket (Point pt, ModelValues val, double X12, double X11, double X21, double X22)
	{
		values = new ModelValues(val);
		prices = new Point(pt);
                                
		this.X12 = X12;
		this.X11 = X11;
		this.X21 = X21;
		this.X22 = X22;
	}
	
	ModelValues values = null;
	Point prices = null;
	
	private final int N_Servers = 2;
	
	public double Seed = 0.0;
	private static Random rand;
	
	public double X12 = 0;
	public double X11 = 0;
	public double X21 = 0;
	public double X22 = 0;
	
	public int nClientsServed = 0;
	public int nClientsServed1 = 0;
	public int served_1=0;
	public int served_2=0;
	public int ClientGone = 0;
	
	public Vector<Double> TimesClientsWait = new Vector<Double>(0, 100);
	public Vector<Double> TimesClientGoAway = new Vector<Double>(0, 100);
	public Vector<Double> TimesClientsWait1 = new Vector<Double>(0, 100);
	public Vector<Double> TimesClientGoAway1 = new Vector<Double>(0, 100);
	
	public double dCurrentTime = 0.0;
	
	double[] minBrokenX = new double[N_Servers];
	double[] minUnbrokenX = new double[N_Servers];
	
	public void ServeClient(double dCurrentTime, int nServer) 
	{
            if (nServer == 0) 
            {
                if (this.TimesClientsWait.size() == 0) return;
                    
                    //if there is a client serve him
                  //  if (dCurrentTime >= TimesClientGoAway.elementAt(0))
                    if (dCurrentTime >= ((Double)TimesClientGoAway.elementAt(0)).doubleValue())
                    {
                            nClientsServed++;
                            //remove client from queue
                            TimesClientsWait.removeElementAt(0);
                            TimesClientGoAway.removeElementAt(0);
                    }
            }
	    if (nServer == 1) 
	    {
	    	if (TimesClientsWait1.size() == 0) return;
	    	//if there is a client serve him
	    	if (dCurrentTime >= ((Double)TimesClientGoAway1.elementAt(0)).doubleValue())
	    	{
	    		nClientsServed1++;
	    		//remove client from queue
	    		TimesClientsWait1.removeElementAt(0);
	    		TimesClientGoAway1.removeElementAt(0);
	    	}
	    }
	}
	
	public void AcceptClient(double dCurrentTime, double dServeTime, int nBestServer) 
	{
	//generate clients time depending on capacity
	    double dTimeToWait=0.0;
	    if ( values.Reg == 0.0 )
                dTimeToWait = ( -1.0 / (GetWi(nBestServer))) * Math.log(1.0 - dServeTime);
	    
	    else dTimeToWait = 1.0 / GetWi(nBestServer);
	    
	    //total time to wait
	    double dTotalTimeToWait = dTimeToWait;
	    if (nBestServer == 0) 
	    {
	    	if (TimesClientsWait.size() != 0) 
	    	{
	    		//Calculate when clients will be served
	    		for (int iClient = 0; iClient < TimesClientsWait.size(); iClient++) 
	    		{
	    			dTotalTimeToWait += ( (Double)TimesClientsWait.elementAt(iClient)).doubleValue();
	    		}
	    	}
	    	//save client's time of quit
	    	TimesClientGoAway.addElement(new Double(dTotalTimeToWait + dCurrentTime));
	    	//save client's wait time
	    	TimesClientsWait.addElement(new Double(dTimeToWait));
	    }
	    if (nBestServer == 1)
	    {
	    	if (TimesClientsWait1.size() != 0) 
	    	{
	    		//Calculate when clients will be served
	    		for (int iClient = 0; iClient < TimesClientsWait1.size(); iClient++) 
	    		{
	    			dTotalTimeToWait +=
	    				( (Double)TimesClientsWait1.elementAt(iClient)).
	    				doubleValue();
	    		}
	    	}
	    	//save client's time of quit
	    	TimesClientGoAway1.addElement(new Double(dTotalTimeToWait + dCurrentTime));
	    	//save client's wait time
	    	TimesClientsWait1.addElement(new Double(dTimeToWait));
	    }
	}
	
	public double GetWi(int nServer) 
	{
	    double Wi = 0.0;
	    if(nServer == 0)
	    {
	    	Wi = values.Z01 * (1.0 - Math.exp( - ( -values.Z11 * (values.B11 + X11)))) *
        	        		  (1.0 - Math.exp( - ( -values.Z12 * (values.B12 + X12))));

                /*
                 * values.Z01 * ( 1.0 - Math.exp( - Z11 * X11 - Z12 * X12 ) )
                 * values.Z02 * ( 1.0 - Math.exp( - Z22 * X22 - Z21 * X21 ) )
                 * */
	    }
	    else
	    {
	    	Wi = values.Z02 * (1.0 - Math.exp( - ( -values.Z22 * (values.B22 + X22)))) *
	        				  (1.0 - Math.exp( - ( -values.Z21 * (values.B21 + X21))));
	    }
	    return Wi;
	}
	
	public double GetProfit(int nServer) 
	{
		double zz = 0.0;
		if (nServer == 0)
		{
			zz = served_1 * prices.x[2] + prices.x[0] * X21  - prices.x[1] * X12;
		}
		else 
		{
			zz = served_2 * prices.x[3] + prices.x[1] * X12  - prices.x[0] * X21;
		}
		return zz;	
	}
	
	public void RunWalrasMarket() 
	{
	    served_1=0;
	    served_2=0;
	
	    rand = new Random();
	
	    for (int iClient = 0; iClient < values.A; iClient++) 
	    {
	    	if (values.Reg == 0)
	    		dCurrentTime += ( -1.0 / values.A) * (Math.log(1.0 - rand.nextDouble()));
	    	else
	    		dCurrentTime += 1.0 / values.A;
	    	//serve clients
	    	for (int iServer = 0; iServer < N_Servers; iServer++) 
	    	{
	    		ServeClient(dCurrentTime, iServer);
	    	}
	    	//chose best server
	    	int iBest = ClientChoseServer();
	    	if (iBest == -1) 
	    	{
	    		ClientGone++;
	    	}
	    	else
	    	{
	    		//Chosen server Accepts client
	    		double servetime = 0;
	    		if (values.Reg == 0)
	    			servetime = rand.nextDouble();
	    		else	
	    			servetime = 1.0 / values.A;
	    		
	    		AcceptClient(dCurrentTime, servetime, iBest);
	    	}
	    } //for iclient
	    served_1 = nClientsServed + TimesClientsWait.size();
	    served_2 = nClientsServed1 + TimesClientsWait1.size();
	}

	private int ClientChoseServer() 
	{
		int iBest = 0; //-1 indicates that client abandon servers
	    double dTotalPrice = GetTotalServicePrice(0);
	    double dTotalPrice2 = GetTotalServicePrice(1);
	    if (dTotalPrice2 < dTotalPrice) 
	    {
	    	iBest = 1;
	    }
	    // if prices equal then select server randomly
	    if (dTotalPrice == dTotalPrice2) 
	    {
	    	double random = rand.nextDouble();
	    	if ((random * 1000 )> 500)
	    		iBest = 0;
	    	else
	    		iBest = 1;
	    }
	    //minimal price
	    if ((dTotalPrice > values.C0) && (dTotalPrice2 > values.C0)) 
	    {
	    	return -1; //client goes way
	    }
	    return iBest;
	}
	
	public double GetTotalServicePrice(int nServer) {
            double Totalprice = 0.0;
	    if (nServer == 0)
	    	Totalprice = prices.x[2] + (TimesClientsWait.size() / GetWi(nServer));
	    else
	    	Totalprice = prices.x[3] + (TimesClientsWait1.size() / GetWi(nServer));
	    
	    return Totalprice;
	}
}
