/* ************************************************************************
#
#  Chronologica 
#
#  http://etimeline.com/
#
#  Copyright:
#    Copyright 2012 eTimeline, LLC. All rights reserved.
#
#  License:
#    See the license.txt file in the project's top-level directory for details.
#
#  Authors:
#    * Andy White
#
************************************************************************ */
package etime.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;

import divconq.db.CustomResult;
import divconq.db.DatabaseResult;
import divconq.db.IDatabaseCallback;
import divconq.db.IDatabaseRequest;
import divconq.db.ObjectCallback;
import divconq.db.ObjectResult;
import divconq.db.QueryRequest;
import divconq.db.query.ISelectField;
import divconq.db.query.ListDirectRequest;
import divconq.db.query.ListRequest;
import divconq.db.query.LoadRecordRequest;
import divconq.db.query.OrderAs;
import divconq.db.query.OrderField;
import divconq.db.query.OrderFields;
import divconq.db.query.OrderLinked;
import divconq.db.query.SelectComposer;
import divconq.db.query.SelectDirectRequest;
import divconq.db.query.SelectField;
import divconq.db.query.SelectFields;
import divconq.db.query.SelectForeignField;
import divconq.db.query.SelectRequest;
import divconq.db.query.SelectSubquery;
import divconq.db.query.WhereAnd;
import divconq.db.query.WhereAny;
import divconq.db.query.WhereComposer;
import divconq.db.query.WhereEqual;
import divconq.db.query.WhereExpression;
import divconq.db.query.WhereField;
import divconq.db.query.WhereIs;
import divconq.db.query.WhereLessThan;
import divconq.db.query.WhereOr;
import divconq.db.update.DynamicListField;
import divconq.db.update.DynamicScalarField;
import divconq.db.update.InsertRecordRequest;
import divconq.db.update.RetireRecordRequest;
import divconq.db.update.ScalarField;
import divconq.db.update.UpdateBigFieldRequest;
import divconq.db.update.UpdateRecordRequest;
import divconq.lang.BigDateTime;
import divconq.lang.FuncResult;
import divconq.lang.Hub;
import divconq.lang.HubResources;
import divconq.lang.OperationCallback;
import divconq.lang.OperationResult;
import divconq.lang.TaskContext;
import divconq.lang.chars.Utf8Encoder;
import divconq.log.Logger;
import divconq.struct.FieldStruct;
import divconq.struct.RecordStruct;
import divconq.struct.builder.XmlStreamBuilder;
import divconq.struct.builder.YamlStreamBuilder;
import divconq.test.TestDb;
import divconq.util.IOUtil;
import divconq.util.StringUtil;

public class Main {
	public static void main(String[] args) {
		HubResources resources = new HubResources("00101", true);
		OperationResult or = resources.init();
		
		if (or.hasErrors()) {
			Logger.error("Unable to continue, hub resources not properly configured");
			return;
		}
	
		Hub.instance.start(resources);
		
		TaskContext.useNewRoot();
		
		Scanner scan = new Scanner(System.in);
		boolean running = true;
		
		while(running) {
			try {
				System.out.println();
				System.out.println("-----------------------------------------------");
				System.out.println("   Chronologica Database Menu");
				System.out.println("-----------------------------------------------");
				System.out.println("0)  Main Menu");
				System.out.println("1)  Sample Tests");
				System.out.println("2)  List all people ");
				System.out.println("3)  List all people (all data) ");
				System.out.println("4)  List people directly to file (Xml)");
				System.out.println("5)  List people directly to file (Yaml)");
				System.out.println("6)  List people born in date range");
				System.out.println("7)  List spouses for given person");
				System.out.println("8)  List descendants for given person");
				System.out.println("9)  Retire person record");
				System.out.println("10) Load person record");
				System.out.println("11) Advanced Query Tests");

				String opt = scan.nextLine();
				
				Long mopt = StringUtil.parseInt(opt);
				
				if (mopt == null)
					continue;
				
				switch (mopt.intValue()) {
				case 0:
					running = false;
					break;
					
				case 1:
					Main.sample(scan);
					break;
					
				case 2: {
					QueryRequest req = new QueryRequest("etlListAll", new RecordStruct(new FieldStruct("Compact", true)));
					
					Hub.instance.getDatabase().submit(req, new ResultCallback("List All Compact"));						
				}
				break;
				
				case 3: {
					QueryRequest req = new QueryRequest("etlListAll");
					
					Hub.instance.getDatabase().submit(req, new ResultCallback("List All Full"));						
				}
				break;
				
				case 4:	{
					new File("./files").mkdirs(); 
					
					QueryRequest gpr3 = new QueryRequest("etlListAll");
					String file = "./files/etlpeople.xml";
					final FileOutputStream fos3 = new FileOutputStream(file);
					
					CustomResult xml = new CustomResult(new XmlStreamBuilder(new PrintStream(fos3), true));
					
					Hub.instance.getDatabase().submit(gpr3, xml, new FileStreamCallback("People to Xml", file, fos3));
					
					break;
				}
				case 5:	{					
					new File("./files").mkdirs(); 
					
					QueryRequest gpr3 = new QueryRequest("etlListAll");
					String file = "./files/etlpeople.yaml";
					final FileOutputStream fos3 = new FileOutputStream(file);
					
					CustomResult yaml = new CustomResult(new YamlStreamBuilder(new PrintStream(fos3)));
					
					Hub.instance.getDatabase().submit(gpr3, yaml, new FileStreamCallback("People to Yaml", file, fos3));
					
					break;
				}
				case 6: {
					RecordStruct params = new RecordStruct();
					
					System.out.print("From Year (blank for all): ");
					String dt = scan.nextLine();
					
					if (StringUtil.isNotEmpty(dt))
						params.setField("From", new BigDateTime(StringUtil.parseInt(dt, 0)));
					
					System.out.print("To Year (blank for all): ");
					dt = scan.nextLine();
					
					if (StringUtil.isNotEmpty(dt))
						params.setField("To", new BigDateTime(StringUtil.parseInt(dt, 0)));
					
					QueryRequest req = new QueryRequest("etlSearchBDay", params);
					
					Hub.instance.getDatabase().submit(req, new ResultCallback("B-day Search"));						
				}
				break;
					
				case 7: {
					RecordStruct params = new RecordStruct();
					
					System.out.print("Person Id: ");
					String prsn = scan.nextLine();
					
					if (StringUtil.isNotEmpty(prsn))
						params.setField("PersonId", prsn);
					
					QueryRequest req = new QueryRequest("etlListSpouses", params);
					
					Hub.instance.getDatabase().submit(req, new ResultCallback("Spouse info Search"));						
				}
				break;
				
				case 8: {
					RecordStruct params = new RecordStruct();
					
					System.out.print("Person Id: ");
					String prsn = scan.nextLine();
					
					if (StringUtil.isNotEmpty(prsn))
						params.setField("PersonId", prsn);
					
					QueryRequest req = new QueryRequest("etlListDescendants", params);
					
					Hub.instance.getDatabase().submit(req, new ResultCallback("Descendant info Search"));						
				}
				break;
				
				case 9: {
					System.out.print("Person Id: ");
					String prsn = scan.nextLine();
					
					if (StringUtil.isEmpty(prsn))
						break;
					
					RetireRecordRequest req = new RetireRecordRequest("etlPerson", prsn);
					
					Hub.instance.getDatabase().submit(req, new ResultCallback("Retire Person Record"));						
				}
				break;
					
				case 10: {
						System.out.print("Person Id: ");
						String prsn = scan.nextLine();
						
						if (StringUtil.isEmpty(prsn))
							break;
						
						System.out.print("Compact (yes|no): ");
						String cmpct = scan.nextLine();
						
						boolean full = (StringUtil.isEmpty(cmpct) || !"yes".equals(cmpct.toLowerCase()));
						
						System.out.print("Year (blank for all): ");
						String dt = scan.nextLine();
						
						BigDateTime bdt = StringUtil.isNotEmpty(dt) ? new BigDateTime(StringUtil.parseInt(dt, 0)) : null;
						
						LoadRecordRequest req = new LoadRecordRequest("etlPerson", prsn, bdt, 
								new SelectFields(
										new SelectField("Id"),
										new SelectSubquery("etlBioFather","Father",
												new SelectField("Id"),
												new SelectField("etlName", "Name"),
												new SelectField("etlSex", "Sex"),
												new SelectField("etlOccupation", "Occupations"),
												new SelectComposer("etlSpouses", "Spouses"),
												new SelectField("etlName", "Name"),
												new SelectSubquery("etlBioFather","GrandFather",
														new SelectField("Id"),
														new SelectField("etlName", "Name"),
														new SelectField("etlSex", "Sex")
												)
										),
										//new SelectForeignField("etlBioFather", "etlName", "Father"),
										new SelectForeignField("etlBioMother", "etlName", "Mother"),
										new SelectField("From", "DOB"),
										new SelectField("To", "DOD"),
										new SelectField("etlGlobalId", "GlobalId"),
										new SelectField("etlName", "Name"),
										new SelectField("etlOccupation", "Occupations"),
										new SelectField("etlSex", "Sex"),
										new SelectComposer("etlSpouses", "Spouses"),
										new SelectComposer("etlSpouses2", "Spouses2")
								)
						);
						
						req.setCompact(!full);
						req.setHistorical(bdt == null);
						
						Hub.instance.getDatabase().submit(req, new ResultCallback("Load person record"));						
					}
					break;
					
				case 11:
					Main.advQuery(scan);
					break;
				}						
			}
			catch(Exception x) {
				System.out.println("CLI error: " + x);
			}
		}
		
		Hub.instance.stop();
	}	
	
	public static void sample(Scanner scan) {
		System.out.println("-----------------------------------------------");
		System.out.println("   Sample Instructions");
		System.out.println("-----------------------------------------------");
		System.out.println("To run this example please run Import (option 1) first.");
		System.out.println("If you have done any other imports or updates this.");
		System.out.println("sample will not work.");
		System.out.println();
		
		boolean running = true;
		
		while(running) {
			try {
				System.out.println();
				System.out.println("-----------------------------------------------");
				System.out.println("   Sample Menu");
				System.out.println("-----------------------------------------------");
				System.out.println("0)  Main Menu");
				System.out.println("1)  Import People");
				System.out.println("2)  Alter Data Part 1");
				System.out.println("3)  Alter Data Part 2");
				System.out.println("4)  Big Update test");
				System.out.println("5)  Big Read test");

				String opt = scan.nextLine();
				
				Long mopt = StringUtil.parseInt(opt);
				
				if (mopt == null)
					continue;
				
				switch (mopt.intValue()) {
				case 0:
					running = false;
					break;
					
				case 1:	
					FuncResult<File> rres = Hub.instance.getResources().getPackageResource("etlCore", "sample/sample1.tlml");
					
					if (rres.hasErrors()) {
						System.out.println("Error loading file: " + rres.getMessage());
						break;
					}
					
					Import imp = new Import();
					imp.runImport(rres.getResult(), new OperationCallback(true) {
						@Override
						public void completed() {
							if (this.hasErrors())
								System.out.println("Import Enountered Errors");
							else
								System.out.println("Success!");
						}
					});
					
					break;					
				case 2:	{
						System.out.println("Alter Data Part 1 Starting");						
						
						final CountDownLatch latch = new CountDownLatch(10);

						// 1 - add spouse for Gary
						InsertRecordRequest insMelinda = new InsertRecordRequest("etlPerson",
								new ScalarField("etlGlobalId", "68f2b8f837a4469ab279b9abb6d1efc1"),
								new DynamicScalarField("etlName", null, "Melinda Harrier"),
								new DynamicScalarField("etlSex", null, "Female"),
								new ScalarField("From", new BigDateTime(1939, 5, 3, 16, 29)),
								new DynamicListField("etlSpouse", null, "00100_000000000000006", new BigDateTime(1989, 12, 21, 14, 30))
						);
						
						Hub.instance.getDatabase().submit(insMelinda, new SampleCallback(insMelinda, latch));

						// 2 - Change Jared b-day away 
						UpdateRecordRequest upJared = new UpdateRecordRequest("etlPerson", "00100_000000000000004",
								new ScalarField("From", new BigDateTime(1937, 2, 2, 22, 11))
						);
						
						Hub.instance.getDatabase().submit(upJared, new SampleCallback(upJared, latch));
						
						// 3 - Add Jared culmination 
						UpdateRecordRequest upJared2 = new UpdateRecordRequest("etlPerson", "00100_000000000000004",
								new ScalarField("To", new BigDateTime(1999, 7, 1, 3, 55))
						);
						
						Hub.instance.getDatabase().submit(upJared2, new SampleCallback(upJared2, latch));
						
						// 4 - Add 3 Kelly occupations, 2 overlap
						UpdateRecordRequest upKelly1 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
								new DynamicListField("etlOccupation", null, "Waitress", new BigDateTime(1955, 6, 12, 7, 30), new BigDateTime(1958, 1, 20)),
								new DynamicListField("etlOccupation", null, "Plumber", new BigDateTime(1957, 10, 29, 9), new BigDateTime(1962, 8, 1, 17)),
								new DynamicListField("etlOccupation", null, "Architect", new BigDateTime(1962, 9, 17, 11, 30))
						);
						
						Hub.instance.getDatabase().submit(upKelly1, new SampleCallback(upKelly1, latch));
						
						// 5 - Add 1 Ronald occupation, overlap with Kelly
						UpdateRecordRequest upRonald = new UpdateRecordRequest("etlPerson", "00100_000000000000002",
								new DynamicListField("etlOccupation", null, "Plumber", new BigDateTime(1936, 3, 23, 6, 30), new BigDateTime(1977, 3, 1))
						);
						
						Hub.instance.getDatabase().submit(upRonald, new SampleCallback(upRonald, latch));
						
						// 6 - change date on kelly spouse 1
						UpdateRecordRequest upKelly2 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
								new DynamicListField("etlSpouse", "6A486A1E7BA042489DB14A2558C17A0D", "00100_000000000000006", 
										new BigDateTime(1960, 1, 11), new BigDateTime(1986, 9, 21))
						);		
						
						Hub.instance.getDatabase().submit(upKelly2, new SampleCallback(upKelly2, latch));
						
						// 7 - remove Kelly mother
						UpdateRecordRequest upKelly3 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
								new ScalarField("etlBioMother")
						);			
						
						Hub.instance.getDatabase().submit(upKelly3, new SampleCallback(upKelly3, latch));
						
						// 8 & 9 - change Kelly father
						InsertRecordRequest insStan = new InsertRecordRequest("etlPerson",
								new DynamicScalarField("etlName", null, "Stan Barrier"),
								new DynamicScalarField("etlSex", null, "Male")
						);
						
						Hub.instance.getDatabase().submit(insStan, new ObjectCallback() {							
							@Override
							public void process(ObjectResult res) {
								latch.countDown();
								
								if (res.hasErrors()) {
									System.out.println("Add Stan Error:");
									TestDb.printPretty(res.getMessages());
									latch.countDown();
									return;
								}
								
								String fid = ((RecordStruct) res.getResult()).getFieldAsString("Id");
								
								UpdateRecordRequest upKelly4 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
										new ScalarField("etlBioFather", fid)
								);			
								
								Hub.instance.getDatabase().submit(upKelly4, new SampleCallback(upKelly4, latch));
							}
						});
						
						// 10 - remove Kelly's 86 name
						UpdateRecordRequest upKelly5 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
								new DynamicScalarField("etlName", "6861936751874630A3FB7486D7843333")
						);		
						
						Hub.instance.getDatabase().submit(upKelly5, new SampleCallback(upKelly5, latch));
						
						// wait until all db tasks done
						
						latch.await();
						System.out.println("Alter Data Part 1 Complete");						
					}
				
					break;
					
				case 3:	{
					System.out.println("Alter Data Part 2 Starting");						
					
					final CountDownLatch latch = new CountDownLatch(7);
					
					// 1 - Change Jared b-day back to correct 
					UpdateRecordRequest upJared = new UpdateRecordRequest("etlPerson", "00100_000000000000004",
							new ScalarField("From", new BigDateTime(1935, 11, 10))
					);
					
					Hub.instance.getDatabase().submit(upJared, new SampleCallback(upJared, latch));
					
					// 2 - Remove Jared culmination 
					UpdateRecordRequest upJared2 = new UpdateRecordRequest("etlPerson", "00100_000000000000004",
							new ScalarField("To")
					);
					
					Hub.instance.getDatabase().submit(upJared2, new SampleCallback(upJared2, latch));
					
					// 3 - change date on kelly spouse 1 back
					UpdateRecordRequest upKelly2 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
							new DynamicListField("etlSpouse", "6A486A1E7BA042489DB14A2558C17A0D", "00100_000000000000006", 
									new BigDateTime(1960, 1, 11), new BigDateTime(1986, 9, 27))
					);		
					
					Hub.instance.getDatabase().submit(upKelly2, new SampleCallback(upKelly2, latch));
					
					// 4 - restore Kelly mother
					UpdateRecordRequest upKelly3 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
							new ScalarField("etlBioMother", "00100_000000000000001")
					);			
					
					Hub.instance.getDatabase().submit(upKelly3, new SampleCallback(upKelly3, latch));
					
					// 5 - restore Kelly father
					UpdateRecordRequest upKelly4 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
							new ScalarField("etlBioFather", "00100_000000000000002")
					);			
					
					Hub.instance.getDatabase().submit(upKelly4, new SampleCallback(upKelly4, latch));
					
					// 6 - add Ronald father
					UpdateRecordRequest upKelly4b = new UpdateRecordRequest("etlPerson", "00100_000000000000002",
							new ScalarField("etlBioFather", "00100_000000000000009")
					);			
					
					Hub.instance.getDatabase().submit(upKelly4b, new SampleCallback(upKelly4b, latch));
					
					// 7 - restore Kelly's 86 name
					UpdateRecordRequest upKelly5 = new UpdateRecordRequest("etlPerson", "00100_000000000000007",
							new DynamicScalarField("etlName", "6861936751874630A3FB7486D7843333", "Kelly Murphy", new BigDateTime(1986, 9, 27))
					);		

					Hub.instance.getDatabase().submit(upKelly5, new SampleCallback(upKelly5, latch));
					
					// wait until all db tasks done
					
					latch.await();
					System.out.println("Alter Data Part 2 Complete");						
				}
			
				break;
				
				case 4:	
					FuncResult<File> bres = Hub.instance.getResources().getPackageResource("etlCore", "sample/biography.txt");
					
					if (bres.hasErrors()) {
						System.out.println("Error loading file: " + bres.getMessage());
						break;
					}
					
					String bio = IOUtil.readEntireFile(bres.getResult());
					
					if (bio == null) {
						System.out.println("Error read biography file");
						break;						
					}
					
					UpdateBigFieldRequest bfreq = new UpdateBigFieldRequest("etlPerson", "00100_000000000000007",
							new ScalarField("etlBiography", bio)
					);

					Hub.instance.getDatabase().submit(bfreq, new ResultCallback("Biography Update"));
					
					break;
				case 5:
					LoadRecordRequest req = new LoadRecordRequest("etlPerson", "00100_000000000000007",  
							new SelectFields(
									new SelectField("etlBiography")
							)
					);
					
					req.setCompact(true);
					
					Hub.instance.getDatabase().submit(req, new ObjectCallback() {
						protected String name = "Biography Read";
						
						@Override
						public void process(ObjectResult res) {
							if (res.hasErrors()) {
								System.out.println(this.name + " Error:");
								TestDb.printPretty(res.getMessages());
							}
							else {
								RecordStruct rec = (RecordStruct) res.getResult();
								
								if (rec == null) {
									System.out.println(this.name + " Empty Result");
									return;
								}								
										
								System.out.println(this.name + " Result:");
								
								String bio = rec.getFieldAsString("etlBiography");
								
								if (StringUtil.isEmpty(bio)) {
									System.out.println(this.name + " Empty Result");
									return;
								}								
								
								System.out.println("Size: " + bio.length());
								
								new File("./files").mkdirs(); 
								
								try {
									FileOutputStream fos = new FileOutputStream("./files/biography.txt");
	
									fos.write(Utf8Encoder.encode(bio));
								
									fos.flush();
									fos.close();
									
									System.out.println("Written to: ./files/biography.txt");
								} 
								catch (IOException x) {
									System.out.println("Error writing biography file: " + x);
								}
							}
						}
					});						
					
					break;
				}
						
			}
			catch(Exception x) {
				System.out.println("CLI error: " + x);
			}
		}		
	}
	
	public static void advQuery(Scanner scan) {
		System.out.println();
		
		boolean running = true;
		
		while(running) {
			try {
				System.out.println();
				System.out.println("-----------------------------------------------");
				System.out.println("   Advanced Query Tests Menu");
				System.out.println("-----------------------------------------------");
				System.out.println("0)  Main Menu");
				System.out.println("1)  Select Test");
				System.out.println("2)  Select Test w/ Paging");
				System.out.println("3)  List Test");
				System.out.println("4)  List Test w/ Paging");
				System.out.println("5)  Select Direct Test");
				System.out.println("6)  List Direct Test");

				String opt = scan.nextLine();
				
				Long mopt = StringUtil.parseInt(opt);
				
				if (mopt == null)
					continue;
				
				switch (mopt.intValue()) {
				case 0:
					running = false;
					break;
					
				case 1:	{
						SelectFields selt = new SelectFields(
								new SelectField("Id"),
								new SelectField("etlName", "Name"),
								new SelectField("etlSex", "Sex", "Tr"),
								new SelectField("etlGlobalId", "GlobalId"),
								new SelectField("From", "DOB"),
								new SelectField("To", "DOD"),
								new SelectComposer("etlAge", "Age"),
								new SelectForeignField("etlBioFather", "etlName", "Father"),
								new SelectForeignField("etlBioMother", "etlName", "Mother")
						);
						
						OrderFields ordr = new OrderFields(
								new OrderLinked("DOB"),
								new OrderField("etlSex", OrderAs.Descending, "Tr")
						);
						
						WhereExpression whr = new WhereOr(
								new WhereEqual(new WhereField("From"), new BigDateTime(1935, 11, 10)),
								new WhereAny(new WhereField("etlOccupation"), "Plumber")
								/*,
								new WhereAnd(
										new WhereLessThan(new WhereComposer("etlAge"), 70),
										new WhereIs(new WhereComposer("etlAge"))
								)
								*/
						);
						
						//SelectRequest req = new SelectRequest("etlPerson", selt, ordr, whr, new CollectorScript("etlAllMen"), "19580000T");
						SelectRequest req = new SelectRequest("etlPerson", selt, ordr, whr, new BigDateTime(1958, 1, 1));
						
						req.setCacheEnabled(true);
						
						Hub.instance.getDatabase().submit(req, new ResultCallback("Select Test"));						
						
						break;
					}
					
				case 2:	{
						SelectFields selt = new SelectFields(
								new SelectField("Id"),
								new SelectField("etlName", "Name"),
								new SelectField("etlSex", "Sex", "Tr"),
								new SelectComposer("etlAge", "Age"),
								new SelectForeignField("etlBioFather", "etlName", "Father"),
								new SelectForeignField("etlBioMother", "etlName", "Mother")
						);
						
						OrderFields ordr = new OrderFields(
								new OrderLinked("Name", OrderAs.Descending)
						);
						
						WhereExpression whr = new WhereAnd(
								new WhereLessThan(new WhereComposer("etlAge"), 50),
								new WhereIs(new WhereComposer("etlAge"))
						);
						
						// who is under 50 in 1958?
						final SelectRequest req = new SelectRequest("etlPerson", selt, ordr, whr, new BigDateTime(1958, 1, 1));
						
						req.setCacheEnabled(true);
						req.setPageSize(3);
						
						Hub.instance.getDatabase().submit(req, new ResultCallback("Select Test w/ Paging 1") {
							@Override
							public void process(ObjectResult res) {
								super.process(res);
								
								req.nextPage();
								Hub.instance.getDatabase().submit(req, new ResultCallback("Select Test w/ Paging 2"));
							}
						});
						
						break;
					}
					
				case 4:	{
					ISelectField selt = new SelectField("etlName");
						
						OrderFields ordr = new OrderFields(
								new OrderField("etlName")
						);
						
						WhereExpression whr = new WhereAnd(
								new WhereLessThan(new WhereComposer("etlAge"), 75),
								new WhereIs(new WhereComposer("etlAge"))
						);
						
						final ListRequest req = new ListRequest("etlPerson", selt, ordr, whr, new BigDateTime(1958));
						
						req.setCacheEnabled(true);
						req.setPageSize(3);
						
						Hub.instance.getDatabase().submit(req, new ResultCallback("List Test w/ Paging 1") {
							@Override
							public void process(ObjectResult res) {
								super.process(res);
								
								req.nextPage();
								Hub.instance.getDatabase().submit(req, new ResultCallback("List Test w/ Paging 2"));
							}
						});
						
						break;
					}
					
				case 5:	{
						SelectFields selt = new SelectFields(
								new SelectField("Id"),
								new SelectField("etlName", "Name"),
								new SelectField("etlSex", "Sex", "Tr"),
								new SelectField("etlGlobalId", "GlobalId"),
								new SelectField("From", "DOB"),
								new SelectField("To", "DOD"),
								new SelectComposer("etlAge", "Age"),
								new SelectForeignField("etlBioFather", "etlName", "Father"),
								new SelectForeignField("etlBioMother", "etlName", "Mother")
						);
						
						WhereExpression whr = new WhereOr(
								new WhereEqual(new WhereField("From"), new BigDateTime(1935, 11, 10)),
								new WhereAny(new WhereField("etlOccupation"), "Plumber")
						);
						
						SelectDirectRequest req = new SelectDirectRequest("etlPerson", selt, whr, new BigDateTime(1958));
						
						Hub.instance.getDatabase().submit(req, new ResultCallback("Select Direct Test"));						
						
						break;
					}
				
					case 3:	{
						ISelectField selt = new SelectSubquery("etlBioFather",
								new SelectField("Id"),
								new SelectField("etlName", "Name"),
								new SelectComposer("etlAge", "Age")
						);
						
						OrderFields ordr = new OrderFields(
								new OrderField("etlSex")
						);
						
						ListRequest req = new ListRequest("etlPerson", selt, ordr, null, new BigDateTime(1958));
						
						req.setCacheEnabled(true);
						
						Hub.instance.getDatabase().submit(req, new ResultCallback("List Test"));						
						
						break;
					}
				
					case 6:	{
						ISelectField selt = new SelectSubquery("etlBioFather",
								new SelectField("Id"),
								new SelectField("etlName", "Name"),
								new SelectComposer("etlAge", "Age")
						);
						
						//selt = new SelectField("etlName");
						
						ListDirectRequest req = new ListDirectRequest("etlPerson", selt);  //, null, new BigDateTime(1958));
						req.setHistorical(true);
						
						Hub.instance.getDatabase().submit(req, new ResultCallback("List Direct Test"));						
						
						break;
					}
				}
						
			}
			catch(Exception x) {
				System.out.println("CLI error: " + x);
			}
		}		
	}
	
	static public class SampleCallback extends ObjectCallback {
		protected String name = null;
		protected CountDownLatch latch = null;

		public SampleCallback(IDatabaseRequest req, CountDownLatch latch) {
			this.name = req.getProcedure();
			this.latch = latch;
		}
		
		@Override
		public void process(ObjectResult res) {
			if (res.hasErrors()) {
				System.out.println(this.name + " Error:");
				TestDb.printPretty(res.getMessages());
			}
			
			latch.countDown();
		}
	}
	
	static public class ResultCallback extends ObjectCallback {
		protected String name = null;

		public ResultCallback(String name) {
			this.name = name;
		}
		
		@Override
		public void process(ObjectResult res) {
			if (res.hasErrors()) {
				System.out.println(this.name + " Error:");
				TestDb.printPretty(res.getMessages());
			}
			else {
				System.out.println(this.name + " Result:");
				TestDb.printPretty(res.getResult());
			}
		}
	}
	
	static public class FileStreamCallback implements IDatabaseCallback {
		protected FileOutputStream fos = null;
		protected String file = null;
		protected String feature = null;
		
		public FileStreamCallback(String feature, String file, FileOutputStream stream) {
			this.feature = feature;
			this.file = file;
			this.fos = stream;
		}
		
		@Override
		public void process(DatabaseResult res) {
			if (res.hasErrors()) {
				System.out.println(this.feature + " error:");
				TestDb.printPretty(res.getMessages());
			}
			else  {
				System.out.println(this.feature + " saved to file: " + this.file);
			}
			
			try {
				this.fos.flush();
				this.fos.close();
			} 
			catch (IOException x) {
			}
		}
	}
}
