/* The class contains a set of auxiliary functions that are used by methods
 * defined in other classes which manipulate and/or perform operations over
 * tables.
 * Auxiliary Functions in class:
 * - overlap: verifies if two rows overlap by comparing their time values.
 * - coalesce: merges rows that have the same set of column values and 
 *             time values that relate.
 */
import java.util.*;

class  AuxFuncs
{

	/********************************************************************/
	/*								OVERLAP								*/
	/********************************************************************/

	/* Returns true if the set of two time components overlap */
	public static boolean overlapOld(int st1, int et1, int st2, int et2)
	{
		if ( (st1<=et2+1) && (et1>=st2-1) )
		{
			return true;
		}
		else
			return false;
	}

	//NEW OVERLAP
	/* Returns true if the set of two time components overlap */
	public static boolean overlap(int st1, int et1, int st2, int et2)
	{
		if ( (st1<=et2) && (et1>=st2) )
		{
			return true;
		}
		else
			return false;
	}

	/********************************************************************/
	/*					COALESCE THE ROWS IN A TABLE					*/
	/********************************************************************/

	public static Table coalesce(Table R) /* Calls method to perform coalesce */
	{
		if (R.getNoRowSet()!=0) /* if the table is not empty i.e contains 1 or more rows */
		{
			int[] colIndex=givenIndex(R.getNoCols());
			quicksort( R, 0, R.getNoRowSet()-1, colIndex ); /* sort the rows in table first */
			R.rowSet=AuxFuncs.coalesceRows(R.getRowSet(), R.getNoCols(), colIndex);
		}
		
		return R;
	}

	/* Method to perform and return the coalescing of a set of rows */
	public static ArrayList coalesceRows(ArrayList R, int noCols, int[] colIndex)
	{		
		ArrayList Rc = new ArrayList(); /* holds the coalesced rows */
		int i = 0; /* counter to go through each row in the set R */
		Row Ri; /* current row */
		Row Rj; /* next row (j=i+1) */

		/* flag to indicate keep fetching next row while it matches the ith row */
		boolean next = true; 

		do
		{
			Ri = (Row) R.get(i);
			next=true;

			while (next==true && i<R.size()-1)
			{
				Rj = (Row) R.get(i+1); /* get next row (next to the ith row) */

				/* when comparing Rows, do not compare the time components */
				if ( compareRows(Ri, Rj, false, colIndex)==0 && Rj.st<=(Ri.et+1) )
				{
					/* coalesce the two rows by updating the ith row's end time value */
					Ri.et=StrictMath.max(Ri.et, Rj.et);
					i=i+1;
				}
				else /* no more rows that match the ith row */
				{
					next=false;
				}

			}

			Rc.add(Ri); /* add the coalesced row to the output set */
			i=i+1;
		}
		while (i<R.size());
		
		return Rc;
	}

	/********************************************************************/
	/*			QUICKSORT ALGORITHM TO SORT ROWS IN A TABLE				*/
	/********************************************************************/
	public static void quicksort(Table list, int p, int r, int[] colIndex)
	{
		if ( p < r ) {
			int q = partition(list, p, r, colIndex);
			if ( q == r )
				q--;
			quicksort(list, p, q, colIndex);
			quicksort(list, q+1, r, colIndex);
		}
	}
	
	private static int partition(Table list, int lo, int hi, int[] colIndex)
	{
		Row pivot = (Row) list.getRow(lo);
		while ( true ) 
		{
			while ( compareRows( (Row) list.getRow(hi), pivot, true, colIndex) >= 0 && lo < hi )
				hi--;
			 while ( compareRows( (Row) list.getRow(lo), pivot, true, colIndex) < 0 && lo < hi )
				lo++;
			if (lo < hi) 
			{
				list.placeRow(lo, (Row) list.removeRow(hi));
				list.placeRow(hi, (Row) list.removeRow(lo+1));
			}
		else return hi;
		}
	}

	/********************************************************************/
	/*							COMPARE TWO ROWS						*/
	/********************************************************************/
	public static int compareRows(Row x, Row y, boolean cmpTime, int[] colIndex)
	{
		/* compare the row values, each non-time column object */
		int res = compareRowVals( x.getRowValues(), y.getRowValues(), colIndex);

		/* compare time component value of rows if requested */
		if(cmpTime)
		{
			/* if the two rows are identical in all column values then order/compare by time */
			if(res==0) /* compare start time */
			{
				Integer st1 = new Integer( x.getRow_st() );
				Integer st2 = new Integer( y.getRow_st() );
				res=initCompare(st1, st2);
			}
			if(res==0) /* compare end time if the start time of both rows is the same */ 
			{
				Integer et1 = new Integer( x.getRow_et() );
				Integer et2 = new Integer( y.getRow_et() );
				res=initCompare(et1, et2);
			}
		}

		/* catching an error */
		if(res==99)
		{
			System.out.println("Error: initCompare returned 99");
		}

		return res;
	}

	public static int compareRowVals(Object[] a, Object[] b, int[] colIndex)
	{
		int res=0; /* assume at the start that a and b have same objects */
		int i=0; /* counter to go through each object */

		while(i<a.length && res==0) /* go through each object value */
		{
			int pos=colIndex[i];
			/* test if both or either a[i] and b[i] are null values */
			int nullVal = nullHandling(a[pos], b[pos]);

			if( nullVal != 99 ) /* found both or either a[i] and b[i] to be 'null' */
			{
				res=nullVal;
			}
			else /* a[i] amd b[i] are not a 'null' value */
			{
				int compVal = initCompare(a[pos], b[pos]); /* compare a[i] and b[i] */

				if (compVal!=0) /* objects compared are not the same */
				{
					res=compVal;
				}
			}
			i++;
		}

		return res;
	}


	/********************************************************************/
	/*				CHECK FOR NULL ROW OBJECT VALUES					*/
	/********************************************************************/
	public static int nullHandling(Object a, Object b) /* handling 'null' object values for row comparision */
	{
		int nullcomp = 99; /* defualt setting: a and b are not null */

		/* test to find if a or/and b are null values */
		try
		{
			if( a.equals("null") )
			{
				nullcomp=-1;
			}
		}
		catch (NullPointerException e)
		{
			/* continue to the next 'try' statement */
		}
		try
		{
			if( b.equals("null") )
			{
				nullcomp=1;
			}
		}
		catch (NullPointerException e)
		{
			/* continue to the next 'try' statement */
		}
		try
		{
			if( a.equals("null") && b.equals("null") )
			{
				nullcomp=0;
			}
		}
		catch (NullPointerException e)
		{
			/* neither value a or b are null, return default nullcomp value assignment */
		}

		return nullcomp;
	}


	/********************************************************************/
	/*						COMPARE TWO OBJECTS							*/
	/********************************************************************/
    public static int initCompare(Object a1, Object b1) 
	{
		try	
		{
			Integer a2 = (Integer) a1;
			Integer b2 = (Integer) b1;
			return compare(a2, b2);
		}
		catch (Exception e)
		{
			/* continue to the next 'try' statement */
		}
		try	
		{
			String a2 = (String) a1;
			String b2 = (String) b1;
			return compare(a2, b2);			
		}
		catch (Exception e)
		{
			/* continue to the next 'try' statement */
		}
		try	
		{
			Double a2 = (Double) a1;
			Double b2 = (Double) b1;
			return compare(a2, b2);			
		}
		catch (Exception e)
		{
			/* continue to the next 'try' statement */
		}
		try	
		{
			Float a2 = (Float) a1;
			Float b2 = (Float) b1;
			return compare(a2, b2);			
		}
		catch (Exception e)
		{   
			System.out.println("Error: Unkown objects in method initCompare" + a1 +"-" + b1);
			return 99;
		}
	}

	public static int compare(Integer a, Integer b) 
	{
		return a.compareTo(b);
	}

	public static int compare(String a, String b) 
	{
		return a.compareTo(b);

	}

	public static int compare(Double a, Double b) 
	{
		return a.compareTo(b);
	}

	public static int compare(Float a, Float b) 
	{
		return a.compareTo(b);
	}

	/********************************************************************/
	/*				ORDER COLUMNS FOR INDEXING TABLE ROWS				*/
	/********************************************************************/

	public static int[] orderCols(String[] indexby, Column c)
	{
		int[] colposIndex = new int[c.getColNumber()];

		if(indexby.length==0) /* keep same ordering of columns */
		{
			for (int i=0; i<c.getColNumber(); i++)
			{
				colposIndex[i]=i;
			}
		}
		else /* order by given index */
		{
			for (int i=0; i<indexby.length; i++)
			{
				colposIndex[i]=c.getColPos(indexby[i]);
			}

			int dest=indexby.length;
			for (int j=0; j<c.getColNumber(); j++)
			{
				if( !checkName(indexby, c.getPosName(j)) )
				{
					colposIndex[dest]=j;
					dest++;
				}
			}
		}

		return colposIndex;

	}

	private static boolean checkName(String[] list, String name)
	{
		boolean res=false;
		int i=0;

		while(i<list.length && !res)
		{
			if( name.equals(list[i]) )
			{
				res=true;
			}
			i++;
		}

		return res;
	}

	public static int[] givenIndex(int num)
	{
		int[] colpos = new int[num];

		for (int i=0; i<num; i++)
		{
			colpos[i]=i;
		}

		return colpos;
	}
	

	/********************************************************************/
	/*	CHECKS IF TWO SETS COLUMN INOFRMATION ARE OF THE SAME FORMAT	*/
	/*			i.e if two tables have the same format					*/
	/********************************************************************/
	public static boolean sameFormat(Column c1, Column c2)
	{
		boolean res=true;
		
		if ( c1.getColNumber()==c2.getColNumber() ) // same number of columns
		{
			/* check column types */
			int index=0;
			while ( index<c1.getColNumber() && res)
			{
				if( c1.getPosType(index) != c2.getPosType(index) )
					res=false;
				index++;
			}
		}
		else
			res=false;
		
		return res;
	}

}
