/* ************************************************************************
#
#  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.util.HashMap;
import java.util.List;
import java.util.concurrent.Semaphore;

import divconq.db.ObjectCallback;
import divconq.db.ObjectResult;
import divconq.db.update.DynamicListField;
import divconq.db.update.DynamicScalarField;
import divconq.db.update.InsertRecordRequest;
import divconq.db.update.ScalarField;
import divconq.lang.BigDateTime;
import divconq.lang.Hub;
import divconq.lang.OperationCallback;
import divconq.struct.RecordStruct;
import divconq.util.StringUtil;
import divconq.xml.XElement;
import divconq.xml.XmlReader;

public class Import {
	// TODO parameters
	
	// must call with a task context set
	// this 
	public void runImport(File file, final OperationCallback or) {
		final Semaphore done = new Semaphore(0);
		
		// null callback not supported
		if (or == null)
			return;
		
		XElement tlml = XmlReader.loadFile(file);
		
		if (tlml == null) {
			or.errorTr(100000, file);
			or.completed();
		}
		
		// TODO look up the database id for mapping, if we don't have the database then error out
		
		// build entity cross-reference hash
		final HashMap<String, String> idxref = new HashMap<String, String>();
		
		List<XElement> entities = tlml.selectAll("entities/*");
		
		// reverse input means older data last and referenced data must come before unreferenced
		for (int i = entities.size() - 1; i >= 0; i--) {
			XElement entel = entities.get(i);
			
			String table = entel.getName();
			final String eid = entel.getAttribute("id");
			
			if ("person".equals(table)) 
				table = "etlPerson";
			else if ("government".equals(table)) 
				table = "etlGovernment";
			else if ("community".equals(table)) 
				table = "etlCommunity";
			else if ("structure".equals(table)) 
				table = "etlStructure";
			else {
				or.warnTr(100001, table, eid);
				continue;
			}
			
			InsertRecordRequest ins = new InsertRecordRequest(table);
			ins.addField(new ScalarField("etlGlobalId", eid));
			
			for (XElement pel : entel.selectAll("properties/*")) {
				String prop = pel.getName();
				
				String id = pel.getAttribute("id");
				
				if (StringUtil.isEmpty(id)) {
					or.warnTr(100003, prop);
					continue;
				}
				
				BigDateTime from = this.parseDate(pel.find("date"));
				
				if ("name".equals(prop)) { 
					String value = null;					
					XElement vel = pel.find("value");
					
					if (vel != null)
						value = vel.getText();
					
					ins.addField(new DynamicScalarField("etlName", id, value, from));
				}
				else if ("sex".equals(prop)) { 
					String value = null;					
					XElement vel = pel.find("value");
					
					if (vel != null)
						value = vel.getText();
					
					if ("male".equals(value))
						value = "Male";
					else if ("female".equals(value))
						value = "Female";
					else
						value = null;
					
					ins.addField(new DynamicScalarField("etlSex", id, value, from));
				}
				else if ("occupation".equals(prop)) { 
					String value = null;					
					XElement vel = pel.find("value");
					
					if (vel == null)
						continue;
					
					value = vel.getText();
					
					if (StringUtil.isEmpty(value))
						continue;
					
					// TODO support "to" if available (how did we do it in Chronologica?)
					BigDateTime to = this.parseDate(pel.find("to"));
					
					ins.addField(new DynamicListField("etlOccupation", id, value, from, to));
				}
				else if ("inception".equals(prop)) { 
					ins.addField(new ScalarField("From", from));
				}
				else if ("culmination".equals(prop)) { 
					ins.addField(new ScalarField("To", from));
				}
				else if ("parents".equals(prop)) { 
					XElement vel = pel.find("mother");
					
					if ((vel != null) && (vel.hasAttribute("refid"))) 
						ins.addField(new ScalarField("etlBioMother", idxref.get(vel.getAttribute("refid"))));
					
					vel = pel.find("father");
					
					if ((vel != null) && (vel.hasAttribute("refid"))) 
						ins.addField(new ScalarField("etlBioFather", idxref.get(vel.getAttribute("refid"))));
				}
				else if ("spouse".equals(prop)) { 
					XElement vel = pel.find("spouse");
					
					if ((vel != null) && (vel.hasAttribute("refid"))) {
						BigDateTime to = this.parseDate(pel.find("to"));

						ins.addField(new DynamicListField("etlSpouse", id, idxref.get(vel.getAttribute("refid")), from, to));
					}
				}
				else {
					or.warnTr(100002, prop, pel.getAttribute("id"));
					continue;
				}
			}
			
			Hub.instance.getDatabase().submit(ins, new ObjectCallback() {
				@Override
				public void process(ObjectResult res) {
					or.copyMessages(res);
					
					if (!or.hasErrors())
						idxref.put(eid, ((RecordStruct) res.getResult()).getFieldAsString("Id"));
					
					done.release();
				}
			});
			
			try {
				done.acquire();
			} 
			catch (InterruptedException x) {
				or.errorTr(100004, eid, x);
				break;
			}
		}

		or.completed();
	}
	
	public BigDateTime parseDate(XElement el) {
		if (el != null)
			return this.parseDate(el.getText());
		
		return null;
	}
	
	public BigDateTime parseDate(String date) {
		if (date != null) {
			String[] dp = date.split("\\/");
			
			if (dp.length == 3) {
				int y = (int) StringUtil.parseInt(dp[0], 0);
				int m = (int) StringUtil.parseInt(dp[1], 0);
				int d = (int) StringUtil.parseInt(dp[2], 0);
				
				if ((y != 0) && (m != 0) && (d != 0))
					return new BigDateTime(y, m, d);
			}
			else if (dp.length == 2) {
				int y = (int) StringUtil.parseInt(dp[0], 0);
				int m = (int) StringUtil.parseInt(dp[1], 0);
				
				if ((y != 0) && (m != 0))
					return new BigDateTime(y, m);
			}
			else if (dp.length == 1) {
				int y = (int) StringUtil.parseInt(dp[0], 0);
				
				if (y != 0) 
					return new BigDateTime(y);
			}
		}
		
		return null;
	}
}
