package com.k_int.npdb.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.SAXParserFactory;

import org.dom4j.dtd.InternalEntityDecl;
import org.xml.sax.Attributes;
import org.xml.sax.ext.DefaultHandler2;

public class FileSplitter extends DefaultHandler2 {

	private ArrayList<String> stack = new ArrayList<String>();
	private long maxElements = 5000;
	private int current;
	private int maxDepth = 2;
	private int docCount;
	private String outputPath;
	private String fileName;
	private BufferedWriter output;
	private StringBuffer internalDTD = new StringBuffer();
	private boolean lastTextData = false;
	private boolean firstElement = true;
	private boolean entityProcessed = false;
	private String namespace;

	public void characters(char[] ch, int start, int length) {
		writeText(ch, start, length);
	}

	public void startEntity(String name) {
		write("&" + name + ";");
		entityProcessed = true;
	}

	public void endEntity(String name) {
		entityProcessed = false;
	}

	public void startElement(java.lang.String uri, java.lang.String localName,
			java.lang.String qName, Attributes attributes) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("<");
		buffer.append(qName);
		for (int i = 0; i < attributes.getLength(); i++) {
			if (!attributes.getQName(i).contains("xmlns"))
				buffer.append(" " + attributes.getQName(i) + "=\""
						+ attributes.getValue(i) + "\"");
		}
		if (firstElement && namespace != null) {
			buffer.append(" xmlns=\"" + namespace + "\"");
		}
		buffer.append(">");
		stack.add(qName);
		writeStartTag(buffer.toString(), stack.size());
		current++;
		firstElement = false;
	}

	public void endElement(java.lang.String uri, java.lang.String localName,
			java.lang.String qName) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("</");
		buffer.append(qName);
		buffer.append(">");
		writeEndTag(buffer.toString(), stack.size());
		stack.remove(qName);
		if (current > maxElements) {
			if (stack.size() <= maxDepth) {
				current = 0;
				finishDocument();
				setNewWriter();
			}
		}
	}

	public void startDTD(String name, String publicId, String systemId) {
		writeln("");
		writeln("<!DOCTYPE DATA [");
		internalDTD.append("\n");
		internalDTD.append("<!DOCTYPE DATA [");
		internalDTD.append("\n");
	}

	public void endDocument() {
		finishDocument();
	}

	public void endDTD() {
		writeln("]>");
		internalDTD.append("]>");
		internalDTD.append("\n");
	}

	public void internalEntityDecl(String name, String value) {
		InternalEntityDecl decl = new InternalEntityDecl(name, value);
		internalDTD.append(decl.toString());
		internalDTD.append("\n");
		writeln(decl.toString());
	}

	private void writeText(char[] ch, int start, int length) {
		if (entityProcessed)
			return;
		try {
			output.write(ch, start, length);
			lastTextData = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void writeln(String s) {
		try {
			output.write(s);
			output.newLine();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void write(String s) {
		try {
			output.write(s);
	
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void writeStartTag(String s, int indent) {
		try {
			output.newLine();
			for (int i = 1; i < indent; i++)
				output.write(" ");
			output.write(s);
			lastTextData = false;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void writeEndTag(String s, int indent) {
		try {
			if (!lastTextData) {
				output.newLine();
				for (int i = 1; i < indent; i++) {
					output.write(" ");
				}
			}
			output.write(s);
			lastTextData = false;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void splitDocument(String path, String outputPath, String namespace) {

		File f = new File(path);
		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			javax.xml.parsers.SAXParser parser = factory.newSAXParser();

			this.outputPath = outputPath;
			this.fileName = f.getName();
			this.namespace = namespace;
			setNewWriter();
			parser.setProperty(
					"http://xml.org/sax/properties/declaration-handler", this);
			parser.setProperty("http://xml.org/sax/properties/lexical-handler",
					this);

			parser.parse(f, this);

			// parser.parse(new InputSource(new FileInputStream(f)));

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void finishDocument() {
		for (int i = stack.size() - 1; i >= 0; i--)
			writeEndTag("</" + stack.get(i) + ">", i + 1);
		try {

			if (output != null) {
				output.flush();
				output.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void setNewWriter() {
		try {
			docCount++;
			File out = new File(outputPath, fileName.substring(0, fileName
					.length() - 4)
					+ "_" + docCount + ".xml");
			output = new BufferedWriter(new FileWriter(out));
			output.write("<?xml version=\"1.0\"?>");
			if (internalDTD.length() > 0)
				write(internalDTD.toString());
			for (int i = 0; i < stack.size(); i++) {
				if (i == 0 && namespace != null)
					writeStartTag("<" + stack.get(i) + " xmlns=\"" + namespace
							+ "\" >", i + 1);
				else
					writeStartTag("<" + stack.get(i) + ">", i + 1);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public int getMaxDepth() {
		return maxDepth;
	}

	public void setMaxDepth(int maxDepth) {
		this.maxDepth = maxDepth;
	}

	public long getMaxElements() {
		return maxElements;
	}

	public void setMaxElements(long maxElements) {
		this.maxElements = maxElements;
	}

	public static void main(String[] args) {
		String path = "/home/ednis/Dokumenty/NPDB/tig/theatreweb/data_1.xml";
		String outputPath = "/home/ednis/Dokumenty/NPDB/split/";
		String namespace = "http://www.uktw.co.uk/";
		FileSplitter s = new FileSplitter();
		s.splitDocument(path, outputPath, namespace);
	}

}