package org.dhappy.mimis.cocoon;

import java.io.IOException;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 

import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.SAXException;

import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Traverser;
import org.neo4j.graphdb.Traverser.Order;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.StopEvaluator;
import org.neo4j.graphdb.ReturnableEvaluator;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.NotFoundException;

import org.apache.cocoon.pipeline.ProcessingException;
import org.apache.cocoon.sax.AbstractSAXGenerator;
import org.apache.cocoon.sax.SAXConsumer;
import org.apache.cocoon.xml.sax.ImmutableAttributesImpl;
import org.xml.sax.SAXException;

import org.apache.commons.logging.LogFactory;

public class GraphWalkGenerator extends AbstractSAXGenerator {
    //implements CacheableProcessingComponent { 
    AttributesImpl emptyAttr = new AttributesImpl();
    static String dbPath = "var/neo4j";
    private final static GraphDatabaseService graphDb = new EmbeddedGraphDatabase(dbPath);
    private final Log logger = LogFactory.getLog(this.getClass());

    public enum TagRelationshipTypes implements RelationshipType {
        CHILD, DOCUMENT
    }

    public GraphWalkGenerator() {
    }

    /*
    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters params)
        throws ProcessingException, SAXException, IOException {
        super.setup(resolver, objectModel, src, params);
    }

    public void recycle() {
        super.recycle();
        graphDb.shutdown();
    }
    */

    public void execute() {
        SAXConsumer consumer = this.getSAXConsumer();
        Node doc;
        Transaction tx = graphDb.beginTx();
        
        try {
            Node root = graphDb.getReferenceNode();
            if( root.hasRelationship(TagRelationshipTypes.DOCUMENT) ) {
                doc = root.getRelationships(TagRelationshipTypes.DOCUMENT).iterator().next().getEndNode();
            } else {
                doc = graphDb.createNode();
                root.createRelationshipTo(doc, TagRelationshipTypes.DOCUMENT);
                Node child = graphDb.createNode();
                Relationship relationship = doc.createRelationshipTo(child, TagRelationshipTypes.CHILD);
            
                doc.setProperty("tag", "example");
                child.setProperty("data", "world");
                relationship.setProperty("owner", "will");
            }
            tx.success();
        } catch(NotFoundException nfe) {
            logger.error("Could not retrieve reference node for " + dbPath);
            throw new IllegalStateException(nfe);
        } finally {
            tx.finish();
        }

        try {
            consumer.startDocument();
            tx = graphDb.beginTx();
            Stack<String> pathToRoot = new Stack<String>();
            try {
                Traverser tree = doc.traverse(Order.DEPTH_FIRST,
                                              StopEvaluator.END_OF_GRAPH, ReturnableEvaluator.ALL,
                                              TagRelationshipTypes.CHILD, Direction.OUTGOING);
                for(Node child : tree) {
                    if(child.hasProperty("tag")) {
                        String tagName = (String)child.getProperty("tag");
                        consumer.startElement("", tagName, tagName, emptyAttr);
                        pathToRoot.push(tagName);
                    } else if(child.hasProperty("data")) {
                        String data = (String)child.getProperty("data");
                        consumer.characters(data.toCharArray(), 0, data.length());
                    }
                }
                tx.success();
                for(String tagName : pathToRoot) {
                    consumer.endElement("", tagName, tagName);
                }
            } finally {
                tx.finish();
            }
            consumer.endDocument();
        } catch( SAXException e ) {
            throw new ProcessingException(e);
        }
    }
}