class  ExecuteTree
{
	public static Table generate(TreeNode tree)
	{
		Table res = new Table();

		// perform binary related operations
		if (tree instanceof BinaryNode)
		{
			BinaryNode bin = (BinaryNode) tree.getNode();
			
			if (bin.getOp().equals("TIMES"))
			{
				res=Product.execProduct( generate(bin.getLeftTree()) , generate(bin.getRightTree()), 0 );
			}
			else if (bin.getOp().equals("SINCE"))
			{
				res=Product.execProduct( generate(bin.getLeftTree()) , generate(bin.getRightTree()), 1 );
			}
			else if (bin.getOp().equals("UNTIL"))
			{
				res=Product.execProduct( generate(bin.getLeftTree()) , generate(bin.getRightTree()), 2 );
			}
			else if (bin.getOp().equals("UNION"))
			{
				res=Union.execUnion( generate(bin.getLeftTree()) , generate(bin.getRightTree()) );
			}
			else if (bin.getOp().equals("MINUS"))
			{
				res=Difference.execDiff( generate(bin.getLeftTree()) , generate(bin.getRightTree()) );
			}
			else if (bin.getOp().equals("JOIN"))
			{
				res=Join.execNatJoin( generate(bin.getLeftTree()) , generate(bin.getRightTree()), 0, null, null );
			}
			else if (bin.getOp().equals("SINCEJOIN"))
			{
				res=Join.execNatJoin( generate(bin.getLeftTree()) , generate(bin.getRightTree()), 1, null, null );
			}
			else if (bin.getOp().equals("UNTILJOIN"))
			{
				res=Join.execNatJoin( generate(bin.getLeftTree()) , generate(bin.getRightTree()), 2, null, null);
			}
			else if (bin.getOp().equals("WHERE"))
			{
				Table tab = new Table();
				tab=generate(bin.getLeftTree());
				// filter the table by keeping the rows that satisfy the constarint
				// the right of the WHERE node points to the constraint tree
				res=filterTable(tab, bin.getRightTree());
			}
			else
			{
				System.out.println("Error in executing Tree: Unkown binary node value=" + bin.getOp());
			}
		}

		else if (tree instanceof TableNode)
		{
			TableNode tab = (TableNode) tree.getNode();
			String qry = "Select * from ";
			qry=qry.concat(tab.getArg());
			res=Query.execQuery(qry);		
		}

		else if(tree instanceof ProjNode)
		{
			ProjNode proj = (ProjNode) tree.getNode();
			res=Project.execProject( generate(proj.getNext()) , proj.getArg() );		
		}

		else if(tree instanceof CoalesceNode)
		{
			CoalesceNode coal = (CoalesceNode) tree.getNode();
			res = AuxFuncs.coalesce( generate(coal.getNext()) );		
		}

		else if (tree instanceof RenameNode)
		{
			RenameNode rename = (RenameNode) tree.getNode();
			res = RenameField.change( generate(rename.getNext()), rename.getOldCol(), rename.getNewCol() ); 
		}

		/////////////////////////////////////////////////////////////////////
		else if (tree instanceof OnWithNode)
		{
			OnWithNode ow = ( OnWithNode) tree.getNode();

			String[] a = ow.getTabACol();
			String[] b = ow.getTabBCol();
			TreeNode nxt = ow.getNext();

			try
			{
				BinaryNode joinBin = (BinaryNode) nxt.getNode();
				if (joinBin.getOp().equals("JOIN"))
				{
					res=Join.execNatJoin( generate(joinBin.getLeftTree()) , generate(joinBin.getRightTree()), 0, a, b );
				}
				else if (joinBin.getOp().equals("SINCEJOIN"))
				{
					res=Join.execNatJoin( generate(joinBin.getLeftTree()) , generate(joinBin.getRightTree()), 1, a, b );
				}
				else if (joinBin.getOp().equals("UNTILJOIN"))
				{
					res=Join.execNatJoin( generate(joinBin.getLeftTree()) , generate(joinBin.getRightTree()), 2, a, b );
				}
				else
				{
					System.out.println("\nERROR: 'ON...WITH...' can only be used with 'JOIN', 'SINCEJOIN' or 'UNTILJOIN'");
					System.exit(0);
				}
			}
			catch (Exception e)
			{
				System.out.println("\nERROR: 'ON...WITH...' can only be used with 'JOIN', 'SINCEJOIN' or 'UNTILJOIN'");
				System.exit(0);
			}
			
		}
		/////////////////////////////////////////////////////////////////////

		else if (tree instanceof ModalNode)
		{
			ModalNode mod = (ModalNode) tree.getNode();

			if ( mod.getOp().equals("PAST") )
			{
				Past past = new Past();
				res = past.execute( generate(mod.getNext()) );
			}
			else if ( mod.getOp().equals("PREVIOUS") )
			{
				Previous prev = new Previous();
				res = prev.execute( generate(mod.getNext()) );
			}
			else if ( mod.getOp().equals("ALWAYSPAST") )
			{
				AlwaysPast allPast = new AlwaysPast();
				res = allPast.execute( generate(mod.getNext()) );
			}
			else if ( mod.getOp().equals("FUTURE") )
			{
				Future fut = new Future();
				res = fut.execute( generate(mod.getNext()) );
			}
			else if ( mod.getOp().equals("NEXT") )
			{
				Next nxt = new Next();
				res = nxt.execute( generate(mod.getNext()) );
			}
			else if ( mod.getOp().equals("ALWAYSFUTURE") )
			{
				AlwaysFuture allFut = new AlwaysFuture();
				res = allFut.execute( generate(mod.getNext()) );
			}
			else
			{
				System.out.println("Error in executing Tree: Unkown modal node value=" + mod.getOp());
			}	
		}

		else if (tree instanceof AtTimeNode)
		{
			AtTimeNode at = (AtTimeNode) tree.getNode();
			res = AtTime.execute(at.getArg(), generate(at.getNext()) ); 
		}


		else
		{
			System.out.println("Unkown Node:" +tree.getClass().getName()+" that cannot be executed");
		}

		return res;
		
	} // end of method



	public static Table filterTable(Table tab, TreeNode constraint)
	{
		Column col = tab.getCol();
		int i=0; // counter to go through each row in tab
		
		while(i<tab.getNoRowSet())
		{
			Row r = (Row) tab.getRow(i);
			boolean satisfied=false;

			satisfied=Condition.check(r, col, constraint);
			
			// if the condition is satisfied then keep the row
			if(satisfied)
			{
				// don't remove row from table
				i++;
			}
			else
			{
				// remove the row from the table
				Object o = tab.removeRow(i);
				// no need to add counter i as all the rows are shifted up
			}
		}

		return tab;
	}
}
