package Juno2;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ResourceBundle;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.apache.xalan.serialize.Serializer;
import org.apache.xalan.serialize.SerializerFactory;
import org.apache.xalan.templates.OutputProperties;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import Juno2.DataLoad.AccountCVO;
import Juno2.DataLoad.CustomerCVO;
import Juno2.DataLoad.CustomerDOMParser;
import Juno2.DataLoad.CustomerInputSource;


public class CustomerXMLDAO implements CmdDAO, Serializable {

	static private HashMap customerDocList = null;

	public CustomerXMLDAO() {
		super();
	}

	public LoginVO getLogin(String userid, String password) throws Exception {

		LoginVO loginreq = new LoginVO();
		Document cdoc = (Document)customerDocList.get(userid);
		
		if(cdoc == null){
			System.err.println("Doc exception: " + userid + " not found in customerDocList");
			loginreq.setLoginState(false);
			loginreq.setUserId(null);
			loginreq.setUserPassword(null);
			loginreq.setFirstName(null);
			loginreq.setLastName(null);
			return loginreq;
		}
		
		String docUserid = extractStringFromCustomerDoc(cdoc, CmdConstants.CUSTOMERID);
		if(!(docUserid.equals(userid))){
			System.err.println("Doc exception: " + userid + " does not equal doc userid "+ docUserid);
			loginreq.setLoginState(false);
			loginreq.setUserId(null);
			loginreq.setUserPassword(null);
			loginreq.setFirstName(null);
			loginreq.setLastName(null);
			return loginreq;
		}

		String docPassword = extractStringFromCustomerDoc(cdoc, CmdConstants.PASSWORD);		
		if(!(docPassword.equals(password))){
			System.err.println("Doc exception: " + userid + " does not equal doc userid " + docUserid);
			loginreq.setLoginState(false);
			loginreq.setUserId(null);
			loginreq.setUserPassword(null);
			loginreq.setFirstName(null);
			loginreq.setLastName(null);
			return loginreq;
		}		
		
		String docFirstname = extractStringFromCustomerDoc(cdoc, CmdConstants.FIRSTNAME);
		String docLastname = extractStringFromCustomerDoc(cdoc, CmdConstants.LASTNAME);					
		
		loginreq.setUserId(docUserid);
		loginreq.setUserPassword(docPassword);
		loginreq.setFirstName(docFirstname);
		loginreq.setLastName(docLastname);
		loginreq.setLoginState(true);

		return loginreq;
	}

	public HashMap getAccountList(String userid) throws Exception {	

		//// need better and more explicit try / catch blocks
		
		HashMap hmap = new HashMap();

		try {

			DOMResult dr = null;
			DOMSource ds = null;			
			Document doc = null;
			
			doc = (Document)customerDocList.get(userid);
			ds = new DOMSource(doc);	
			
			TransformerFactory tfactory = TransformerFactory.newInstance();
			Transformer transformer = tfactory.newTransformer(new StreamSource(CmdConstants.ACCOUNTLIST_XSLT));
			
			////for get account list there is no parameter
			////for position query pass in accountid
			////transformer.setParameter("name", "value");

			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();				
			dr = new DOMResult(db.newDocument());

			transformer.transform(ds,dr);

			//// once obtain DOMResult 
			//// convert dr to Document 
			
			Document acctDocList = (Document) dr.getNode();
			//// trigger custom SAX Parser to generate HashMap of AccountVOs	
			
			
			////splat outDoc
			Serializer serializer = SerializerFactory.getSerializer
				(OutputProperties.getDefaultMethodProperties("xml"));
			serializer.setOutputStream(new FileOutputStream("junkAcctList.xml"));
			serializer.asDOMSerializer().serialize(dr.getNode());

			//// now have Document
			//// use getDocumentElement() and getFirstChild() and getNodeValue() to parse
			//// and populate VOs
			
			//// document root is acctList
			Element e = acctDocList.getDocumentElement();
			
			
			serializer.setOutputStream(new FileOutputStream("junkAcct.xml"));
			serializer.asDOMSerializer().serialize(e);
			
			
			Node acct = e.getFirstChild();			

			serializer.setOutputStream(new FileOutputStream("junkAcctFirstChild.xml"));
			serializer.asDOMSerializer().serialize(acct);
			
			while(acct != null) {
				if (acct.getNodeName().equals("account")) {
					AccountVO avo = processAccountNode(acct);
					if(avo != null)
						hmap.put(avo.getAccountId(), avo);					
				}
				acct = acct.getNextSibling();
			}		
		}

		catch(Exception exp)
		{
			System.err.println(
				"getAccountList Exception: " + exp + ":" + exp.getMessage());
			exp.printStackTrace();
			
		}
		
		return hmap;
	}

	public AccountVO processAccountNode(Node acct) {

		AccountVO avo = new AccountVO();

		Node aNode = acct.getFirstChild();
		while(aNode != null)
		{			
			if(aNode.getNodeName().equals(CmdConstants.ACCOUNTID)){
				avo.setAccountId(aNode.getFirstChild().getNodeValue());
			} else if(aNode.getNodeName().equals(CmdConstants.CUSTOMERID)){
				avo.setCustId(aNode.getFirstChild().getNodeValue());
			} else if(aNode.getNodeName().equals(CmdConstants.ACCOUNTBALANCE)){
				//float f = Float.parseFloat(accountBalance);
				avo.setAccountBalance(Float.parseFloat(aNode.getFirstChild().getNodeValue()));			
			} else if(aNode.getNodeName().equals(CmdConstants.ACCOUNTDESCRIPTION)){
				avo.setAccountDescription(aNode.getFirstChild().getNodeValue());
			}
	
			aNode = aNode.getNextSibling();
		}

		return avo;
	}
	
	
	public HashMap getPositionList(String userId, String accountId)
		throws Exception {	
			
		HashMap hmap = new HashMap();

		try {

			DOMResult dr = null;
			DOMSource ds = null;			
			Document doc = null;
			
			doc = (Document)customerDocList.get(userId);
			ds = new DOMSource(doc);	
			
			TransformerFactory tfactory = TransformerFactory.newInstance();
			Transformer transformer = tfactory.newTransformer(new StreamSource(CmdConstants.POSITIONLIST_XSLT));
			transformer.setParameter("accountId", accountId);
			
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();				
			dr = new DOMResult(db.newDocument());

			transformer.transform(ds,dr);

			Document posDocList = (Document) dr.getNode();		
			
			////splat outDoc
			Serializer serializer = SerializerFactory.getSerializer
				(OutputProperties.getDefaultMethodProperties("xml"));
			serializer.setOutputStream(new FileOutputStream("junkPosList.xml"));
			serializer.asDOMSerializer().serialize(dr.getNode());

			//// now have Document
			//// use getDocumentElement() and getFirstChild() and getNodeValue() to parse
			//// and populate VOs
			
			//// document root is acctList
			Element e = posDocList.getDocumentElement();
		
			serializer.setOutputStream(new FileOutputStream("junkPos.xml"));
			serializer.asDOMSerializer().serialize(e);
			
			Node pos = e.getFirstChild();			

			serializer.setOutputStream(new FileOutputStream("junkPosFirstChild.xml"));
			serializer.asDOMSerializer().serialize(pos);
			
			while(pos != null) {
				if (pos.getNodeName().equals("position")) {
					PositionVO pvo = processPositionNode(pos);
					if(pvo != null)
						hmap.put(pvo.getSymbol(), pvo);					
				}
				pos = pos.getNextSibling();
			}
		}

		catch(Exception exp)
		{
			System.err.println(
				"getAccountList Exception: " + exp + ":" + exp.getMessage());
			exp.printStackTrace();			
		}
		
		return hmap;
	}


	public PositionVO processPositionNode(Node pos) {

		PositionVO pvo = new PositionVO();

		Node pNode = pos.getFirstChild();
		while(pNode != null)
		{			
			if(pNode.getNodeName().equals(CmdConstants.ACCOUNTID)){
				pvo.setAccountId(pNode.getFirstChild().getNodeValue());
			} else if(pNode.getNodeName().equals(CmdConstants.SYMBOL)){
				pvo.setSymbol(pNode.getFirstChild().getNodeValue());
			} else if(pNode.getNodeName().equals(CmdConstants.NUMBEROFSHARES)){
				pvo.setNumberOfShares(Integer.parseInt(pNode.getFirstChild().getNodeValue()));
			} else if(pNode.getNodeName().equals(CmdConstants.PREVIOUSCLOSEPRICE)){
				pvo.setPreviousClosePrice(Float.parseFloat(pNode.getFirstChild().getNodeValue()));			
			} else if(pNode.getNodeName().equals(CmdConstants.COSTBASIS)){
				pvo.setCostBasis(Float.parseFloat(pNode.getFirstChild().getNodeValue()));
			}
	
			pNode = pNode.getNextSibling();
		}

		return pvo;
	}
	

	public boolean sellPosition(String userId, String accountId, EquityVO evo) 
	{
		try {

			Document doc = (Document)customerDocList.get(userId);
			DOMSource ds = new DOMSource(doc);	
			
			TransformerFactory tfactory = TransformerFactory.newInstance();
			Transformer transformer = tfactory.newTransformer(new StreamSource(CmdConstants.SELLPOSITION_XSLT));
			transformer.setParameter("aid", accountId);
			transformer.setParameter("sym", evo.getSymbol());			
			
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();				
			DOMResult dr = new DOMResult(db.newDocument());

			transformer.transform(ds,dr);

			Document sellResultDoc = (Document) dr.getNode();	
			
			////splat outDoc
			Serializer serializer = SerializerFactory.getSerializer
				(OutputProperties.getDefaultMethodProperties("xml"));
			serializer.setOutputStream(new FileOutputStream("junkSell.xml"));
			serializer.asDOMSerializer().serialize(dr.getNode());

			//// now put new transformed doc back into customerDocList
			customerDocList.put(userId, sellResultDoc);
		}

		catch(Exception exp)
		{
			System.err.println(
				"sellPosition Exception: " + exp + ":" + exp.getMessage());
			exp.printStackTrace();	
			//// return false; ???		
		}
		
		return true;
	}

	public void init(ResourceBundle rb) {
		if (customerDocList == null) {
			customerDocList = new HashMap();

			try {
				loadCustomerData(rb);
			} catch (Exception e) {

			}
		}
	}

	private void loadCustomerData(ResourceBundle rb) throws Exception {
		//// read files build CVO list then create DOM for each

		HashMap custList = new HashMap();
		HashMap acctList = new HashMap();

		String customerFileName = rb.getString("LoadCustomers");
		String accountFileName = rb.getString("LoadAccounts");
		String positionFileName = rb.getString("LoadPositions");

		loadCustomerData(custList, customerFileName);
		loadAccountData(acctList, accountFileName);
		loadPositionData(acctList, positionFileName);
		mergeCustomerAndAccount(custList, acctList);

		System.out.println("About to new CustomerInputSource");
		CustomerInputSource source = new CustomerInputSource(custList);

		//System.out.println("About to new JTSCustomerSAXParser");
		//JTSCustomerSAXParser CustomerParser = new JTSCustomerSAXParser();
		//CustomerParser.setContentHandler(new DefaultHandler());
		//CustomerParser.setContentHandler(new SAXParserHandlerXML());
		//CustomerParser.parse(source);

		CustomerDOMParser domParser = new CustomerDOMParser();

		int count = 0;
		for (int i=0; i < custList.size(); i++) {
			System.out.println("looping " + i + " on custList of " + custList.size());
			Document cdoc = domParser.createDocument(source);

			////NodeList nl = cdoc.getElementsByTagName("customerid");
			////System.out.println("NodeList length = " + nl.getLength());

			//// this needs to be cleaned up
			//// first child is Customers		
			String cid = extractCustomerIdString(cdoc);
			if(cid == null) {
				break;
			}
			customerDocList.put(cid, cdoc);			
		}
		int junk;
	}
	
	private static String extractCustomerIdString(Document cdoc) {
		////NodeList nl = cdoc.getElementsByTagName("customerid");
		////System.out.println("NodeList length = " + nl.getLength());

		//// this needs to be cleaned up
		//// first child is Customers
			
		Node n1 = cdoc.getFirstChild();
		System.out.println("first child name cdoc " + n1.getNodeName());
						
		if (n1.getNodeName() != CmdConstants.DOCUMENT_ROOT_ELEMENT) {
			System.out.println("Did not find n1 " + CmdConstants.DOCUMENT_ROOT_ELEMENT);
			return null;
		}

		//// second child is customer			
		Node n2 = n1.getFirstChild();
		System.out.println("first child name n1 " + n2.getNodeName());
					
		if (n2.getNodeName() != CmdConstants.CUSTOMER_ELEMENT) {
			System.out.println("Did not find n2 " + CmdConstants.CUSTOMER_ELEMENT);
			return null;
		}
			
		//// get the list of the customer's children
		//// look for the customerid
		NodeList childNodes = n2.getChildNodes();  //// get child list (account) from customer

		Node cn = null;
		System.out.println("customer childNode count = " + childNodes.getLength());
		
		String s = null;
		for (int x = 0; x < childNodes.getLength(); x++) {
			System.out.println("childNodes length: " + childNodes.getLength() + ", about to get child: " + x);
			cn = childNodes.item(x);
			
			System.out.println("childNode Name = " + cn.getNodeName());
			//// make "customerid" a define in CmdConstants
			if (cn != null) {
				if (cn.getNodeType() == Node.ELEMENT_NODE) {
					if (cn.getNodeName().equals(CmdConstants.CUSTOMERID)) {
						System.out.println("Found Customerid name : " + cn.getNodeName());
						System.out.println("Found Customerid value: " + cn.getNodeValue());
						System.out.println("Found Customerid data : " + cn.getFirstChild());
						//// should test for TEXT_NODE before cast
						Text cnc = (Text)cn.getFirstChild();
						s = cnc.getData();
						System.out.println("Found Customerid string : " + s);
						break;
					}
				}
			}
		}
		return s;		
	}
		
	private static String extractStringFromCustomerDoc(Document cdoc, String elementName) {
		////NodeList nl = cdoc.getElementsByTagName("customerid");
		////System.out.println("NodeList length = " + nl.getLength());
		
		Node n1 = cdoc.getFirstChild();
		System.out.println("first child name cdoc " + n1.getNodeName());
						
		if (n1.getNodeName() != "Customers") {
			System.out.println("Did not find n1 Customers\n");
			return null;
		}

		//// second child is customer			
		Node n2 = n1.getFirstChild();
		System.out.println("first child name n1 " + n2.getNodeName());
					
		if (n2.getNodeName() != "customer") {
			System.out.println("Did not find n2 customer\n");
			return null;
		}
			
		//// get the list of the customer's children
		//// look for the customerid
		NodeList childNodes = n2.getChildNodes();  //// get child list (account) from customer

		Node cn = null;
		System.out.println("customer childNode count = " + childNodes.getLength());
		
		for (int x = 0; x < childNodes.getLength(); x++) {
			System.out.println("childNodes length: " + childNodes.getLength() + ", about to get child: " + x);
			cn = childNodes.item(x);
			
			System.out.println("childNode Name = " + cn.getNodeName());
			//// make "customerid" a define in CmdConstants
			if (cn != null) {
				if (cn.getNodeType() == Node.ELEMENT_NODE) {
					if (cn.getNodeName().equals(elementName)) {
						System.out.println("Found " + elementName + " name : " + cn.getNodeName());
						System.out.println("Found " + elementName + " value: " + cn.getNodeValue());
						System.out.println("Found " + elementName + " data : " + cn.getFirstChild());
						//// should test for TEXT_NODE before cast
						Text cnc = (Text)cn.getFirstChild();
						String s = cnc.getData();
						System.out.println("Found " + elementName + " string : " + s);
						return s;
					}
				}
			}
		}
		return null;		
	}
	
	
	private static void loadCustomerData(HashMap custList, String fileName)
		throws Exception {

		try {
			BufferedReader br = new BufferedReader(new FileReader(fileName));

			do {
				CustomerCVO custObj = new CustomerCVO();

				String custId = br.readLine();
				if (custId == null)
					break;
				String password = br.readLine();
				String firstName = br.readLine();
				String lastName = br.readLine();

				custObj.setCustId(custId);
				custObj.setPassword(password);
				custObj.setFirstName(firstName);
				custObj.setLastName(lastName);

				custList.put(custId, custObj);
				////System.out.println(".");

			} while (br.readLine() != null);

			br.close();
		} catch (FileNotFoundException fex) {
			System.err.println(
				"loadCustomerData FileNotFoundEx: " + fex + ":" + fex.getMessage());
		} catch (Exception e) {
			System.err.println("loadCustomerData: " + e + ":" + e.getMessage());
		}
	}

	private static void loadAccountData(HashMap acctList, String fileName)
		throws Exception {
		BufferedReader br = new BufferedReader(new FileReader(fileName));

		try {
			do {
				String custId = br.readLine();
				if (custId == null)
					break;
				String accountId = br.readLine();
				String accountDescription = br.readLine();
				String accountBalance = br.readLine();

				AccountVO avo = new AccountVO();
				avo.setCustId(custId);
				avo.setAccountId(accountId);

				float f = Float.parseFloat(accountBalance);
				avo.setAccountBalance(f);

				avo.setAccountDescription(accountDescription);

				AccountCVO acvo = new AccountCVO(avo);

				acctList.put(accountId, acvo);

				////System.out.println("+");

			} while (br.readLine() != null);
		} catch (Exception ex) {
			System.err.println("loadAccountData: " + ex + ":" + ex.getMessage());
		}

		br.close();
	}

	private static void loadPositionData(HashMap acctList, String fileName)
		throws Exception {
		BufferedReader br = new BufferedReader(new FileReader(fileName));

		try {
			do {
				String accountId = br.readLine();
				if (accountId == null)
					break;
				String symbol = br.readLine();
				String numberOfShares = br.readLine();
				String previousClose = br.readLine();
				String costBasis = br.readLine();

				PositionVO pvo = new PositionVO();
				pvo.setAccountId(accountId);
				pvo.setSymbol(symbol);

				float f = (float) 0.0;
				f = Float.parseFloat(costBasis);
				pvo.setCostBasis(f);
				f = Float.parseFloat(previousClose);
				pvo.setPreviousClosePrice(f);

				int i = Integer.parseInt(numberOfShares);
				pvo.setNumberOfShares(i);

				AccountCVO acvo = (AccountCVO) acctList.get(accountId);
				if (acvo != null) {
					acvo.addPosition(pvo);
					acctList.put(accountId, acvo);
				} else
					System.err.println("LoadPositionData: AccountId not found: " + accountId);

				////System.out.println("-");

			} while (br.readLine() != null);
		} catch (Exception ex) {
			System.err.println("loadPositionData: " + ex + ":" + ex.getMessage());
		}

		br.close();
	}

	private static void mergeCustomerAndAccount(HashMap custList, HashMap acctList)
		throws Exception {
		try {
			Iterator keys = acctList.keySet().iterator();

			while (keys.hasNext()) {
				String accountId = (String) keys.next();
				AccountCVO acvo = (AccountCVO) acctList.get(accountId);

				CustomerCVO ccvo = (CustomerCVO) custList.get(acvo.getCustId());
				if (ccvo == null)
					break;
				ccvo.addAccount(acvo);
				custList.put(ccvo.getCustId(), ccvo);
			}
		} catch (Exception ex) {
			System.err.println("mergeAccountAndCustomer: " + ex + ":" + ex.getMessage());
		}
	}
}

