import java.util.*;

class  Join
{

	public static Table execNatJoin(Table A, Table B, int type, String[] a, String[] b)
	{
		/* Assume that column names in a table are different from each other */

		Table R = new Table();
		Column colA = A.getCol();
		Column colB = B.getCol();
		ArrayList commonCols = new ArrayList();

		if (a==null && b==null)
		{
			/* find the column names that match in both tables */

			for (int i=0; i<colA.getColNumber(); i++)
			{
				int index=0;
				boolean found=false;

				while ( found==false && index<colB.getColNumber() )
				{
					/* the column name and their types match */
					if ( colA.getPosName(i).equals(colB.getPosName(index))
						&& colA.getPosType(i)==colB.getPosType(index) )
					{
						/* place the common column name in list */
						commonCols.add(colA.getPosName(i));
						found=true;
					}
					index++;
				}
			}

			/* convert all object in commonCols to string */
			String[] colNames = new String[commonCols.size()];
			for (int i=0; i<commonCols.size(); i++)
			{
				colNames[i]= (String) commonCols.get(i);
			}

			/* catching errors */
			if (colNames.length>0)
			{
				R = execjoin(A, B, colNames, colNames, type);
			}
			else
			{
				String func = "";
				if (type==0)
					func = "JOIN";
				else if (type==1)
					func = "SINCE-JOIN";
				else if (type==2)
					func = "UNTIL-JOIN";
				else
					func="";

				CatchErr.joinNoComColsErr(func);
				R=A;
				//PRODUCE A PRODUCT OF THE TWO TABLES
			}
		} /* end of outter 'if' */
		
		else
		{
			R = execjoin(A, B, a, b, type);
		}

		boolean coalFlag=UserInterface.getCoal();
		if ( (type==1 || type==2) && coalFlag )
		{
			AuxFuncs.coalesce(R);
		}

		
		return R;
	}

	public static Table execjoin(Table A, Table B, String[] a, String[] b, int type) 
	{
		/* We assume that the number of column names of table A and B that are used to
		 * perform the Join and compare rows are of the same size (a.length == b.length)
		 * and have matching types for comparison.
		 */

		Table R = new Table(); /* output table */

		/* variables to hold the min and max time known to the temporal database system */
		int min_time = UserInterface.getMinTime();
		int max_time = UserInterface.getMaxTime();

		boolean perform = true;

		/* sort the the two tables */
		int[] colIndex_A = AuxFuncs.orderCols( a, A.getCol() );
		int[] colIndex_B = AuxFuncs.orderCols( b, B.getCol() );
		AuxFuncs.quicksort( A, 0, A.getNoRowSet()-1, colIndex_A );
		AuxFuncs.quicksort( B, 0, B.getNoRowSet()-1, colIndex_B );
		//PrintTable.print(A);
		//PrintTable.print(B);

		/* When merging column information and rows from Table A and B, join all 
		 * column/row information from Table A with column/row information of 
		 * Table B excluding the common columns that Table B shares with Table A.
		 */

		Column colA = A.getCol();
		Column colB = B.getCol();
		
		/* get column postions from Table B that are used when merging info */
		int[] colPosB = mergeColPos(b, colB);

		/* merge by joining the column information of table A and B */
		
		/* Column information for Table R */
		int noCols = colA.getColNumber() + colB.getColNumber() - b.length;
		String[] colNames = new String[noCols];
		int[] colTypes = new int[noCols];

		/* copy all column information from Table A:
		 * append Table A's column names and their types to column info of Table R 
		 */
		System.arraycopy( colA.getColNames(), 0, colNames, 0, colA.getColNumber() );
		System.arraycopy( colA.getColTypes(), 0, colTypes, 0, colA.getColNumber() );

		/* postion to copy array info from Table B to Table R */
		int destIndex = colA.getColNumber();

		/* copy the column information from Table B:
		 * the columns to copy are given by the column positions held in colposB
		 */
		for (int i=0; i<colPosB.length; i++)
		{
			colNames[destIndex] = colB.getPosName(colPosB[i]);
			colTypes[destIndex] = colB.getPosType(colPosB[i]);
			destIndex++;
		}

		/* Place merged Column Information in Table R */
		R.putCol(noCols, colNames, colTypes);

		/* Join the Row information of table A and B */

		/* go through all rows in Table A */
		for (int i=0; i<A.getNoRowSet(); i++)
		{
			perform = true;

			Row Ai = (Row) A.getRow(i);
			Object[] AiJoinVals = getJoinVals(Ai, a, colA);
			int[] joinIndex = AuxFuncs.givenIndex(AiJoinVals.length);
			
			
			///
			switch (type)
			{
			case 1: /* Since */
				if (Ai.getRow_st()==max_time && Ai.getRow_et()==max_time)
					perform=false;
				break;

			case 2: /* Until */
				if (Ai.getRow_st()==min_time && Ai.getRow_et()==min_time)
					perform=false;
				break;

			default:
				perform=true;
				break;
			}
			///
			if (perform)
			{

				int j=0; /* counter to go through rows in table B */
				boolean stop=false; /* flag to indicate if row from B is greater than row from A */
				
				while (j<B.getNoRowSet() && !stop)
				{
					perform=true;

					Row Bj = (Row) B.getRow(j);
					Object[] BjJoinVals = getJoinVals(Bj, b, colB);
					
					/* Compare the common column values of the rows */
					int cmpRows=AuxFuncs.compareRowVals( AiJoinVals, BjJoinVals, joinIndex );
					//System.out.println("matched rows A"+i+" and B"+j+" with outcome="+cmpRows);
					
					/* the row in A is greater than the row in B, move and compare next row in B */ 
					if (cmpRows > 0)
						j++;

					/* found matching rows */
					else if (cmpRows == 0)
					{
						/* if two rows have overlap then join them */
						if ( AuxFuncs.overlap(Ai.getRow_st(), Ai.getRow_et(), Bj.getRow_st(), Bj.getRow_et()) )
						{
							/* join the two rows */
							Object[] rowVals = new Object[noCols];

							/* copy all row values from row in table A */
							System.arraycopy( Ai.getRowValues(), 0, rowVals, 0, colA.getColNumber() );

							/* copy all appropriate row values from row in Table B */
							Object[] BjVals = getVals(Bj, colPosB);
							int rowIndex=colA.getColNumber();
							System.arraycopy( BjVals, 0, rowVals, rowIndex, BjVals.length );
						
							/* integrate the overlaping time of the two rows */
							int st_R=99; /* start time of the resulting joined row */
							int et_R=99; /* end time of the resulting joined row */
							switch (type)
							{
							case 0: /* calculate the new time values for performing: A Join B */
								st_R=StrictMath.max(Ai.getRow_st(), Bj.getRow_st());
								et_R=StrictMath.min(Ai.getRow_et(), Bj.getRow_et());
								break;

							case 1: /* calculate the new time values for performing: A SinceJoin B */
								if (Bj.getRow_st()==max_time && Bj.getRow_et()==max_time)
									perform=false;
								else
								{
									st_R=StrictMath.max(Ai.getRow_st(), Bj.getRow_st())+1;
									et_R=Ai.getRow_et()+1;

									if (st_R>max_time)
										st_R=max_time;
									if (et_R>max_time)
										et_R=max_time;
								}
								break;

							case 2: /* calculate the new time values for performing: A UntilJoin B */
								if (Bj.getRow_st()==min_time && Bj.getRow_et()==min_time)
									perform=false;
								else
								{
									st_R=Ai.getRow_st()-1;
									et_R=StrictMath.min(Ai.getRow_et(), Bj.getRow_et())-1;

									if (st_R<min_time)
										st_R=min_time;
									if (et_R<min_time)
										et_R=min_time;
								}
								break;

							default:
								System.out.println("Unknown \"type\" in class \"Join\" ");
								break;
							}

							if (perform)
							{
								/* place merged Row in Table R */
								R.putRow(rowVals, st_R, et_R);
							}
						}
						/* increment table B row counter */
						j++;
					}

					/* found the row in A to be less than the row in B, so stop comparing further rows in B */
					else
					{
						//System.out.println("Ai="+i+"   Bj="+j);
						stop=true;
						j++;
					}
					
				} /* while loop */

			} /* end of IF 'PERFORM' */

		} /* for loop */

		return R;
	}
	

	private static Object[] getVals(Row r, int[] colPos)
	{
		Object[] output = new Object[colPos.length];

		for (int i=0; i<colPos.length; i++)
		{
			output[i]=r.getRowVal( colPos[i] );
		}

		return output;
	}
	
	private static Object[] getJoinVals(Row r, String[] names, Column c)
	{
		Object[] output = new Object[names.length];

		for (int i=0; i<names.length; i++)
		{
			output[i]=r.getRowVal( c.getColPos(names[i]) );
		}

		return output;
	}

	/* returns all the column positions that are not used to compare data 
	 * when performing the join function between two tables - returns all
	 * the columns that are not shared with another table
	 * input parameters:
	 * names => the column names which are shared with the other table
	 * c => column information of a table need to extract the column positions
	 */
	// SHOULD ALSO COMPARE TYPE OF COLUMNS THAT ARE TO BE NEGLECTED
	private static int[] mergeColPos(String[] names, Column c)
	{
		int[] output = new int[c.getColNumber() - names.length];

		int outIndex=0;
		//int namesIndex=0;
		
		//if(output.length>0)
		//{
		for (int i=0; i<c.getColNumber(); i++) /* go through all the column names */
		{
			//if (namesIndex<names.length) /* need to ignore common column names */
			//{
			//	System.out.print("<" + c.getColPos(names[namesIndex]) + ">");
				//if ( names[namesIndex].equals(c.getPosName(i)) )
			//	if ( c.getColPos(names[namesIndex])!=99 )
			//	{
					/* don't copy the column position */
			//		namesIndex++;
			//	}
			//	else
			//	{
					/* copy the column position */
			//		output[outIndex]=i;
			//		outIndex++;
			//	}
			//}
			//else
			//{
				/* copy the column position */
			//	output[outIndex]=i;
			//	outIndex++;
			//}
			String current = c.getPosName(i);
			boolean found = false; /* indictes if cuurent column name is in the 'names' list */ 
			int namesIndex=0;

			while (namesIndex<names.length && !found)
			{
				if (current.equals(names[namesIndex]))
				{
					found=true;
				}
				namesIndex++;
			}

			if (!found)
			{
				/* copy the column position */
				output[outIndex]=i;
				outIndex++;
			}
		}
		//}

		return output;
	}
	
}
