package com.appideas.base;

import android.util.Log;

import com.sharefaith.thesharefaithapp.base.SFApplication;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

/**
 * The APPideas XML Transformer
 *
 * @author		costmo
 * @version		20111226
 * @since		20111226
 */
public class AiXT extends DefaultHandler
{
	/**
	 * Database instance
	 */
	public AiDb mDb;

	/**
	 * A debugging tag
	 */
	public static String mTag = "AiXT";

	/**
	 * What type of element we are getting
	 */
	public String mGetting;

	/**
	 * A string buffer for parsing
	 */
	public StringBuffer mBuffer = null;

	/**
	 * Whether or not we are currently buffering
	 */
	public boolean mBuffering = false;

	/**
	 * A place to hold gathered values
	 */
	public HashMap<String,String> mContent;


	/**
	 * A place to hold gathered values
	 */
	public ArrayList<HashMap<String,String>> mArrayContent;

	/**
	 * The name of a node that will contain data for array grabs
	 */
	public String mContainerNode;

	/**
	 * Whether or not we are currently inside of a container
	 */
	public boolean mFillingContainer;

	/**
	 * The nodes to find
	 */
	public ArrayList<String> mFindNodes;

	/**
	 * Inner nodes to find
	 */
	public ArrayList<String> mInnerNodes;

	/**
	 * The current node name being written to the buffer
	 */
	public String mCurrentNode;

	public static String xmlHeader = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><root><request>";

	public static String xmlFooter = "</request></root>";

	/**
	 * Class constructor
	 *
	 * @since		20111226
	 */
	public AiXT()
	{
		this.mDb = SFApplication.getInstance().getDb();
	}

	/**
	 * Returns the String values for specified nodes.<p/>
	 *
	 * Use this for nodes that have only a single value.<p/>
	 *
	 * Use getArrayforNodes when you expect multiple values from the same node
	 *
	 * @since		20111226
	 * @return		String
	 * @param		node			The path to the node for which we are looking
	 * @param		input			The XML string to examine
	 */
	public HashMap<String,String> getValuesForNodes( String[] node, String input )
	{
		this.mContent = new HashMap<String,String>();
		this.mGetting = "value";
		this.mFindNodes = new ArrayList<String>( Arrays.asList( node ) );

		try
		{
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();

			/* Get the XMLReader of the SAXParser we created. */
			XMLReader xr = sp.getXMLReader();

			/* Create a new ContentHandler and apply it to the XML-Reader*/
			xr.setContentHandler( this );

			/* Parse the xml-data from our URL. */
			xr.parse( new InputSource( new StringReader( input ) ) );
			/* Parsing has finished. */

		}
		catch( Exception e )
		{
			this.mContent = new HashMap<String,String>();
			Log.d( mTag, "XML parsing exception in getValuesForNodes(): " + e.getMessage() );
			//Log.d( mTag, "Input: " + input );
			return this.mContent;
		}

		return this.mContent;
	}

	/**
	 * Returns the String values for specified nodes.<p/>
	 *
	 * Use this for nodes that have multiple values from the same node
	 *
	 * @since		20111226
	 * @return		String
	 * @param		containerNode	The node that will be the top-level of the data elements for the array
	 * @param		nodes			The path to the nodes for which we are looking.  The first element must be a top-level and the others must be subnodes
	 * @param		input			The XML string to examine
	 */
	public ArrayList<HashMap<String,String>> getArrayForNodes( String containerNode, String[] nodes, String input )
	{
		this.mContent = new HashMap<String,String>();
		this.mArrayContent = new  ArrayList<HashMap<String,String>>();
		this.mGetting = "array";
		this.mFindNodes = new ArrayList<String>( Arrays.asList( nodes ) );
		this.mContainerNode = containerNode;
		this.mFillingContainer = false;

		try
		{
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();

			/* Get the XMLReader of the SAXParser we created. */
			XMLReader xr = sp.getXMLReader();

			/* Create a new ContentHandler and apply it to the XML-Reader*/
			xr.setContentHandler( this );

			/* Parse the xml-data from our URL. */
			xr.parse( new InputSource( new StringReader( input ) ) );
			/* Parsing has finished. */

		}
		catch( Exception e )
		{
			this.mContent = new HashMap<String,String>();
			this.mArrayContent = new  ArrayList<HashMap<String,String>>();
			Log.d( mTag, "XML parsing exception in getArrayForNodes(): " + e.getMessage() );
			return this.mArrayContent;
		}

		return this.mArrayContent;
	}

	/**
	 * Tasks to perform at beginning of document read
	 *
	 * @since		20111226
	 */
	@Override
	public void startDocument() throws SAXException
	{
		this.mBuffer = new StringBuffer( "" );
		this.mBuffering = false;
		this.mCurrentNode = "";
	}

	/**
	 * Tasks to perform at end of document read
	 *
	 * @since		20111226
	 */
	@Override
	public void endDocument() throws SAXException
	{
		this.mBuffer = new StringBuffer( "" );
		this.mBuffering = false;
		this.mCurrentNode = "";
	}

	/**
	 * Tasks to perform at beginning of each node
	 *
	 * @since		20111226
	 * @param		namespaceUri			The URI of the namespace to search. Currently unused in implementation
	 * @param		localName				The name of the current node
	 * @param		qName					Currently unused in implementation
	 * @param		atts					Attributes to grab. Currently unused in implementation since we are reading only characters and not attributes
	 */
	@Override
	public void startElement( String namespaceUri, String localName, String qName, Attributes atts ) throws SAXException
	{
		if( this.mGetting.equals( "array" ) && localName.equals( this.mContainerNode ) )
		{
			this.mFillingContainer = true;
			//Log.d( "Tag", "Beginning a container" );
		}
		else if( this.mFindNodes.contains( localName ) )
		{
			//Log.d( "Tag", "Beginning an element: " + localName );
			this.mBuffer = new StringBuffer( "" );
			this.mBuffering = true;
			this.mCurrentNode = localName;
		}
	}

	/**
	 * Writes characters to the buffer when a desired node is being read
	 *
	 * @since		20111226
	 * @param		ch						Characters already written to the buffer
	 * @param		start					The beginning character position
	 * @param		length					The number of characters to add to the buffer
	 */
	@Override
	public void characters( char ch[], int start, int length )
	{
		if( mBuffering )
		{
			mBuffer.append( ch, start, length );
		}
	}

	/**
	 * Tasks to perform at end of each node
	 *
	 * @since		20111226
	 * @param		namespaceUri			The URI of the namespace to search. Currently unused in implementation
	 * @param		localName				The name of the current node
	 * @param		qName					Currently unused in implementation
	 */
	@Override
	public void endElement( String namespaceUri, String localName, String qName ) throws SAXException
	{

		if( this.mGetting.equals( "array" ) && localName.equals( this.mContainerNode ) )
		{
			HashMap<String,String> content = new HashMap<String,String>();

			for( int i = 0; i < this.mFindNodes.size(); i++ )
			{
				String key = this.mFindNodes.get( i );
				String currentItem = this.mContent.get( key );
				content.put( key, currentItem );
			}

			this.mFillingContainer = false;
			this.mArrayContent.add( content );
			this.mContent.clear();
		}

		if( localName.equals( this.mCurrentNode ) )
		{
			if(  this.mGetting.equals( "value" ) || this.mFillingContainer )
			{
				this.mContent.put( this.mCurrentNode, this.mBuffer.toString() );
			}
		}

		this.mBuffer = new StringBuffer( "" );
		this.mBuffering = false;
		this.mCurrentNode = "";

	}

	/**
	 * Strips all content outside of the given node for the purpose of isolating nodes<p/>
	 *
	 * Returns te input string if the node is not found
	 *
	 * @since		20111226
	 * @param		node			The node to fins
	 * @param		input			The input XML
	 */
	public static String stripTextOutsideOfNode( String node, String input )
	{
		String returnValue = input;
		String pattern = "<" + node + ">(.*)</" + node + ">";

		Pattern p = Pattern.compile( pattern, Pattern.DOTALL|Pattern.COMMENTS );
		Matcher matcher = p.matcher( input );

		if( matcher.find() )
		{
			returnValue = matcher.toMatchResult().group( 1 );
		}

		return returnValue;
	}

	/**
	 * Strips all content outside of the given node for the purpose of isolating nodes<p/>
	 *
	 * Returns te input string if the node is not found
	 *
	 * @since		20111226
	 * @param		node			The node to fins
	 * @param		input			The input XML
	 */
	public static ArrayList<String> stringArrayBetweenNode( String node, String input )
	{
		ArrayList<String> returnValue = new ArrayList<String>();

		String pattern = "<" + node + ">(.*?)</" + node + ">";

		Pattern p = Pattern.compile( pattern, Pattern.DOTALL|Pattern.COMMENTS );
		Matcher matcher = p.matcher( input );

		while( matcher.find() )
		{
			returnValue.add( matcher.group( 1 ) );
		}

		return returnValue;
	}



}
