/*
 * AbstractAlgorithm.java
 *
 * Created on Ketvirtadienis, 2007, Sausio 25, 17.06
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package Main.algorithms;

import Main.util.*;
import Main.parser.*;
import Main.*;
import java.text.*;
import Main.data.*;

public abstract class AbstractAlgorithm
{
	
        public double u;
        public double v;
    
	public double U;
	public double V;
              
        public int indeksas;        // mano
        
        public int indeksas2 = 0; 
        public int indeksas3 = 5;
        public int indeksas5 = 0;
	
	public double U_deal;
        public double V_deal;
	
	public double penalty= 0;
	
	protected static Log mLog;
	
	/**
	*  G,P,Q - order
	*/
	protected double[][] matrix;
	/**
	*  X,Y - order
	*/
	protected double[][] result= null;
	protected double[][] matrixU= null;
	protected double[][] matrixV= null;
	
	private final String I= "i";
	private final String J= "j";
	
	
	public abstract void solve();
	
	public abstract void solve(boolean calculateUV);
	
	private boolean mSolved;
	
	public void setMatrix(double[][] m)
	{
		matrix= m;
	}
	public double[][] getMatrix()
	{
		return matrix;
	}
	
	public double[][] getResult()
	{
		return result;
	}
	
	public void calculateMatrixU()
	{
		matrixU= new double[matrix.length][matrix.length];
		for(int i= 0;i<matrix.length;i++)
		{
			for(int j= 0;j<matrix.length;j++)
			{
				matrixU[i][j]= getU(i,j);
			}
		}
		
	}
	
	public double[][] getMatrixU()
	{
		return matrixU;
	}
	
	protected void setMatrixU(double[][] m)
	{
		matrixU= m;
	}
	
	public void calculateMatrixV()
	{
		matrixV= new double[matrix.length][matrix.length];
		for(int i= 0;i<matrix.length;i++)
		{
			for(int j= 0;j<matrix.length;j++)
			{
				matrixV[i][j]= getV(i,j);
			}
		}
	}
	
	public double[][] getMatrixV()
	{
		return matrixV;
	}
	
	protected void setMatrixV(double[][] m)
	{
		matrixV= m;
	}
	
	private double getU(int i, int j)
	{
		for(int k= 0; k< InputDataSlot.DATA_NAMES.length; k++)
		{
			EquationsManager.getInstance().replaceValues(createVariableI(InputDataSlot.DATA_NAMES[k]), matrix[i][k], EquationsManager.U_EQUATION);
			EquationsManager.getInstance().replaceValues(createVariableJ(InputDataSlot.DATA_NAMES[k]), matrix[j][k], EquationsManager.U_EQUATION);
		}
		
		EquationsManager.getInstance().replaceValues(I, i, EquationsManager.U_EQUATION);
		EquationsManager.getInstance().replaceValues(J, j, EquationsManager.U_EQUATION);
		return EquationsManager.getInstance().calculate(EquationsManager.U_EQUATION);
		
		/*if(i == j)
		{
			return matrix[i][0]*matrix[i][1]*matrix[i][2];
		}
		else
			return 0;*/
	}
	
	private double getV(int i, int j)
	{
		for(int k= 0; k< InputDataSlot.DATA_NAMES.length; k++)
		{
			EquationsManager.getInstance().replaceValues(createVariableI(InputDataSlot.DATA_NAMES[k]), matrix[i][k], EquationsManager.V_EQUATION);
			EquationsManager.getInstance().replaceValues(createVariableJ(InputDataSlot.DATA_NAMES[k]), matrix[j][k], EquationsManager.V_EQUATION);
		}
		
		EquationsManager.getInstance().replaceValues(I, i, EquationsManager.V_EQUATION);
		EquationsManager.getInstance().replaceValues(J, j, EquationsManager.V_EQUATION);
		return EquationsManager.getInstance().calculate(EquationsManager.V_EQUATION);
		
		/*if(i == j)
		{
			return -matrix[j][0]*matrix[i][1]*matrix[j][2]+(1-matrix[i][1])*matrix[j][2]*matrix[j][0];
		}
		else
			return matrix[j][2]*matrix[j][0];*/
	}
	
     /*   if(indeksas == 10)      // Jeigu "reikia" sprendzia DSA algoritma 
                {
                    DSAAlgorithm mDSA = new DSAAlgorithm();
                    mDSA = new DSAAlgorithm();
                    mDSA.setMatrix((double[][]) matrix.clone());
                    mDSA.setMatrixU(getMatrixU());
                    mDSA.setMatrixV(getMatrixV());
                    mDSA.solve(isSolved());
                }*/
        
        
	public void solveFineProblem()
	{
           if (indeksas2 == 0 ){
            U = 0;
               // V = 0;
                for (int i=0;i < result.length; i++){
                  for (int j=0; j < result.length; j++){
                    U = U + result[i][1] * getMatrixU()[i][j] * result[j][0]; // x[i]*u[i][j]*y[j]
                   // V = V + result[i][1] * getMatrixV()[i][j] * result[j][0]; // x[i]*v[i][j]*y[j]
                  }
                }
           }
            

         /*   mLog.logn("U = " + Utility.Round(U));
            mLog.logn("V = " + Utility.Round(V));
            mLog.logn("u = " + Utility.Round(u));
            mLog.logn("v = " + Utility.Round(v));*/
           
           if (indeksas == 10){ 
                    //indeksas3 = 0;
   //              if ((u < U) || (v < V+0.01)) {
                     if ((Math.abs(u-U)>0.00000001) || (Math.abs(v-V)>0.00000001)) {
                            DSAAlgorithm mDSA = new DSAAlgorithm();
                            mDSA = new DSAAlgorithm();
                            mDSA.setMatrix((double[][]) matrix.clone());
                            mDSA.setMatrixU(getMatrixU());
                            mDSA.setMatrixV(getMatrixV());
                            mDSA.solve(isSolved());
                            /*if (isSolved() == true)
                              indeksas3 = 5; */
                            //indeksas = 0;
  //                      }
                }
           }

		
        if (indeksas3 == 5)    
        {
                mLog.logn("--------- Solving fine problem --------------");
		
		if(this instanceof QSEAlgorithm || this instanceof SEAlgorithm)
		{
			calculateMatrixU();
			calculateMatrixV();
		}

                U = 0;
               // V = 0;
                for (int i=0;i < result.length; i++){
                  for (int j=0; j < result.length; j++){
                    U = U + result[i][1] * getMatrixU()[i][j] * result[j][0]; // x[i]*u[i][j]*y[j]
                   // V = V + result[i][1] * getMatrixV()[i][j] * result[j][0]; // x[i]*v[i][j]*y[j]
                  }
                }
                
		double u_max_guarantee= Utility.Round(U);//calculateMaxUGuaranty();
		double v_max_guarantee= Utility.Round(V);//calculateMaxVGuaranty();
		double c= calculateC();
		
		U_deal= (c+ u_max_guarantee - v_max_guarantee)/2d - u_max_guarantee;
		V_deal = (c+ v_max_guarantee - u_max_guarantee)/2d - v_max_guarantee;
		
		double fine= MainWindow.getInstance().getInputDataPanel().getFine()*MainWindow.getInstance().getInputDataPanel().getFineProbability();
		
		mLog.logn("Max guarantee : Umaxmin = " + Utility.Round(u_max_guarantee));
		mLog.logn("Max guarantee : Vmaxmin = " + Utility.Round(v_max_guarantee));
		mLog.logn("C = " + Utility.Round(c));
		mLog.logn("fine = " + Utility.Round(fine));
		mLog.logn("deal U = " + Utility.Round(U_deal));
                mLog.logn("deal V = " + Utility.Round(V_deal));
		
		penalty= (c- u_max_guarantee-v_max_guarantee)/2d; 
		
		if(penalty>=fine)
		{
			mLog.logn("Unauthorized deal");
		}
		else
		{
			mLog.logn("Unauthorized deal prevented");
		}
		
		mLog.logn("Unauthorized deal will be prevented at value: "+Utility.Round(penalty));
                indeksas2 = 0;
        }
            indeksas2 = 0;
	}
	
	public double getPenalty()
	{
		return penalty;
	}
	
	public double calculateMaxUGuaranty()
	{
		if(matrixU == null)
		{
			return -1;
		}
		
		double[] array= new double[matrixU.length];
		
		for(int i= 0; i< matrixU[0].length; i++ )
		{
			double min= matrixU[0][i];
			for(int j= 0; j< matrixU.length; j++ )
			{
				if(matrixU[j][i] < min)
				{
					min= matrixU[j][i];
				}
			}
			array[i]= min;
		}
		return findMax(array);
	}
	
	private double findMax(double[] array)
	{
		double max= array[0];
		for(int i= 0; i< array.length; i++)
		{
			if(array[i] > max)
			{
				max= array[i];
			}
		}
		return max;
	}
	
	public double calculateMaxVGuaranty()
	{
		if(matrixV == null)
		{
			return -1;
		}
		
		double[] array= new double[matrixV.length];
		
		for(int i= 0; i< matrixV.length; i++ )
		{
			double min= matrixV[i][0];
			for(int j= 0; j< matrixV[0].length; j++ )
			{
				if(matrixV[i][j] < min)
				{
					min= matrixV[i][j];
				}
			}
			array[i]= min;
		}
		return findMax(array);
	}
	
	private double calculateC()
	{
		if(matrix == null || matrix.length == 0)
		{
			return 0;
		}
		
		int indxQ= InputDataSlot.getIndexOf("Q");
		int indxG= InputDataSlot.getIndexOf("G");
		if(indxQ == -1 || indxG == -1)
		{
			mLog.logn("THERE ARE NO Q OR G FIELDS - C will not be calculated");
			return -1d;
		}
		
		double max= -1;
		for(int i= 0; i< matrix.length;i++)
		{
			double tmp= matrix[i][indxQ]*matrix[i][indxG];
			if(tmp>max)
			{
				max= tmp;
			}
		}
		return max;
	}
	
	protected void setSolved(boolean b)
	{
		mSolved= b;
	}
	public boolean isSolved()
	{
		return mSolved;
	}
	
	public void reset()
	{
		result= null;
		matrixU= null;
		matrixV= null;
	}

	private String createVariableI(String variable)
	{
		return variable+"i";
	}
	
	private String createVariableJ(String variable)
	{
		return variable+"j";
	}
	
	public static void setLog(Log log)
	{
		mLog= log;
	}
	
	public double calculateU()
	{
		double tmp= 0;
		for(int i= 0; i< matrixU.length; i++)
		{
			for(int j= 0; j< matrixU[i].length; j++)
			{
				tmp+= matrixU[i][j]*result[i][0]*result[j][1];
			}
		}
		
		return tmp;
	}
	
	public double calculateV()
	{
		double tmp= 0;
		for(int i= 0; i< matrixV.length; i++)
		{
			for(int j= 0; j< matrixV[i].length; j++)
			{
				tmp+= matrixV[i][j]*result[i][0]*result[j][1];
			}
		}
		
		return tmp;
	}
	
	protected void outputMatrix(double[][] matrix)
	{
		for(int i= 0;i<matrix.length;i++)
		{
			for(int j= 0;j<matrix[i].length;j++)
			{
                            //mLog.log( NumberFormat.getInstance().format( matrix[i][j])+" ");
                            mLog.log(Utility.Round(matrix[i][j])+"  ");
			}
			mLog.logn();
		}
	}

}
