/*
 *  Draco Econometrics
 *  Copyright (C) 2006-2007 Approximatrix, LLC
 *  http://www.approximatrix.com
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.approximatrix.draco.io.XMLio;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.*;
import java.awt.Dimension;


import org.w3c.dom.*;
import org.xml.sax.SAXException;  
import org.xml.sax.SAXParseException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.approximatrix.draco.application.ChildFrame;
import com.approximatrix.draco.application.ChildFrameRegistrar;
import com.approximatrix.draco.general.ui.InfoWindow;


public class TextDatabase {
	
	private ChildFrameRegistrar master = null;
	private Document result = null;
	
	private NodeList texts = null;
	
	public TextDatabase(ChildFrameRegistrar parent) {
		master = parent;
	}
	
	public TextDatabase() {
		// Just a dummy
	}
	
	public Document getDOMrepresentation() {
    	
    	if(result != null) return result;
    	
        result = null;
        try {
            DocumentBuilder dbuild = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            result = dbuild.newDocument();
            Element collection = result.createElement(TextTags.TEXT_MASTER_TAG);
            Element body = result.createElement(TextTags.TEXT_BODY_TAG);
            
            // Get the text windows
            Object[] texts = master.getWindowsByClass(InfoWindow.class);
            if(texts != null) {
            	for(int i=0;i<texts.length;i++)
            		body.appendChild(saveText((InfoWindow)texts[i]));
            }
                        
            collection.appendChild(body);
            result.appendChild(collection);
            
        } catch (Exception e) {
            System.out.println("Exception caught..." + e);
            e.printStackTrace();
        }
        return result;
	}	
	
	/** Returns the number of texts currently loaded into memory
	 * 
	 * @return integer representing the number of texts loaded or stored
	 */
	public int getTextWindowCount() {
		if(texts == null) {
			if(!loadTexts()) return 0;
		}
		return texts.getLength();
	}
	
	/** Returns a requested text from the loaded list
	 * 
	 * @param index the index of the requested text
	 * @return an Element representing the text structure, or null if a problem occurred
	 */
	public Element getTextData(int index) {
		if(texts == null) {
			if(!loadTexts()) return null;
		}
		if(index < 0 || index >= getTextWindowCount()) return null;
		return (Element)texts.item(index);
	}
	
	
	/** Private function that extracts a NodeList of text elements from the
	 * DOM master.
	 * 
	 * @return true if successfully loaded, false otherwise
	 */
	private boolean loadTexts() {
		Element everything = result.getDocumentElement();
		if(everything == null) return false;
		
		NodeList bodies = everything.getElementsByTagName(TextTags.TEXT_BODY_TAG);

		if(bodies == null) return false;
		if(bodies.getLength() != 1) return false;
		Element body = (Element)bodies.item(0);

		// Get the analyses
		texts = body.getElementsByTagName(TextTags.TEXT_ELEMENT);
		return (texts != null);
	}
	
	public boolean loadDOMrepresentation(InputStream in) {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		
		try {
			DocumentBuilder builder = factory.newDocumentBuilder();
			result = builder.parse(in);
		} catch (SAXParseException spe) {
			return false;
		} catch (SAXException sxe) {
			// Error generated during parsing
			Exception  x = sxe;
			if (sxe.getException() != null)
				x = sxe.getException();
			x.printStackTrace();
			return false;
		} catch (ParserConfigurationException pce) {
			// Parser with specified options can't be built
			pce.printStackTrace();
			return false;
		} catch (IOException ioe) {
			ioe.printStackTrace();
			return false;
	    }
		return true;
	}
	
	/** Generates the node tree for a single text dialog, returning it for inclusion in
	 * the complete document.
	 * 
	 * @param dialog a valid InfoWindow dialog
	 * @return the node tree describing the window
	 */
	private Element saveText(InfoWindow dialog) {
		Element one = result.createElement(TextTags.TEXT_ELEMENT);
		one.setAttribute(TextTags.TEXT_ELEMENT_TYPE_SUBELEMENT, dialog.getContentType());
		
		if(dialog.isEditable())
			one.setAttribute(TextTags.TEXT_EDITABLE_SUBELEMENT, Boolean.toString(true));
		else
			one.setAttribute(TextTags.TEXT_EDITABLE_SUBELEMENT, Boolean.toString(false));
		
		Dimension xy = dialog.getSize();
		String contents = dialog.getText();
		String name = dialog.getTitle();
		
		Element temp = result.createElement(TextTags.TEXT_NAME_ELEMENT);
		Text text = result.createTextNode(name);
		temp.appendChild(text);
		one.appendChild(temp);
		
		temp = result.createElement(TextTags.TEXT_SIZE_ELEMENT);
		temp.setAttribute(TextTags.TEXT_SIZE_X_SUBELEMENT, Integer.toString(xy.width));
		temp.setAttribute(TextTags.TEXT_SIZE_Y_SUBELEMENT, Integer.toString(xy.height));
		one.appendChild(temp);
		
		temp = result.createElement(TextTags.TEXT_DATA_ELEMENT);
		//text = result.createTextNode(contents);
		text = result.createCDATASection(contents);
		temp.appendChild(text);
		one.appendChild(temp);
		
		return one;
	}
	
	/** Returns the stored size of the InfoWindow as a Dimension argument
	 * 
	 * @param index the window of interest
	 * @return a valid Dimension object, or null if not found
	 */
	public Dimension getSize(int index) {
		Dimension return_val = null;
		Element one = getTextData(index);
		if(one == null) return null;
		
		NodeList sizes = one.getElementsByTagName(TextTags.TEXT_SIZE_ELEMENT);
		if(sizes == null) return null;
		if(sizes.getLength() != 1) return null;
		try {
			String x = ((Element)sizes.item(0)).getAttribute(TextTags.TEXT_SIZE_X_SUBELEMENT);
			String y = ((Element)sizes.item(0)).getAttribute(TextTags.TEXT_SIZE_Y_SUBELEMENT);
			return_val = new Dimension(new Integer(x).intValue(),new Integer(y).intValue());
		} catch(NumberFormatException nfe) {
			return null;
		} catch(NullPointerException npe) {
			return null;
		}
		return return_val;
	}
	
	/** Returns the text contents of the text data of interest
	 * 
	 * @param index the window of interest
	 * @return a String of the text contents
	 */
	public String getContents(int index) {
		Element one = getTextData(index);
		if(one == null) return null;
		
		NodeList sizes = one.getElementsByTagName(TextTags.TEXT_DATA_ELEMENT);
		if(sizes == null) return null;
		if(sizes.getLength() != 1) return null;
		return ((Element)sizes.item(0)).getTextContent();
	}
	
	/** Returns the type of content stored in a text window
	 * 
	 * @param index the window of interest
	 * @return a String describing the content type (e.g. "text/plain"), or null if none is specified
	 */
	public String getContentType(int index) {
		Element one = getTextData(index);
		if(one == null) return null;
		
		String return_val = one.getAttribute(TextTags.TEXT_ELEMENT_TYPE_SUBELEMENT);
		if(return_val.length() == 0)
			return_val = null;
		
		return return_val;
	}
	
	/** Returns the name of the text data of interest
	 * 
	 * @param index the window of interest
	 * @return a String of the window name
	 */
	public String getName(int index) {
		Element one = getTextData(index);
		if(one == null) return null;
		
		NodeList sizes = one.getElementsByTagName(TextTags.TEXT_NAME_ELEMENT);
		if(sizes == null) return null;
		if(sizes.getLength() != 1) return null;
		return ((Element)sizes.item(0)).getTextContent();
	}
	
	/** Returns the editable state of this data
	 * 
	 * @param index the window of interest
	 * @return true if editing should be allowed, false otherwise
	 */
	public boolean getEditable(int index) {
		Element one = getTextData(index);
		if(one == null) return true;
		
		String truefalse = one.getAttribute(TextTags.TEXT_EDITABLE_SUBELEMENT);
		return new Boolean(truefalse).booleanValue();
	}
}
