package org.drools.repository;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Iterator;
import java.util.List;

import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.Session;
import javax.jcr.Workspace;

import org.apache.jackrabbit.core.TransientRepository;
import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader;
import org.apache.log4j.Logger;

/** 
 * This contains code to initialise the repository for jackrabbit.
 * This is mostly a collection of utilities. 
 * Any jackrabbit specific code needs to go in here.
 */
public class JackrabbitRepositoryConfigurator implements JCRRepositoryConfigurator {

    private static final Logger log = Logger.getLogger(JackrabbitRepositoryConfigurator.class);        
    
    /* (non-Javadoc)
     * @see org.drools.repository.RepositoryConfigurator#getJCRRepository()
     */
    public Repository getJCRRepository(String repoRootDir) {
        try {
            if (repoRootDir == null) {
                return new TransientRepository();
            } else { 
                return new TransientRepository(repoRootDir + "/repository.xml", repoRootDir);
            }
        } catch ( IOException e ) {
            throw new RulesRepositoryException("Unable to create a Repository instance.", e);
        }
    }
    
  
    
    /* (non-Javadoc)
     * @see org.drools.repository.RepositoryConfigurator#setupRulesRepository(javax.jcr.Session)
     */
    public void setupRulesRepository(Session session) throws RulesRepositoryException {
        System.out.println("Setting up the repository, registering node types etc.");
        try {
            Node root = session.getRootNode();
            Workspace ws = session.getWorkspace();

            //no need to set it up again, skip it if it has.
            boolean registered = RulesRepositoryAdministrator.isNamespaceRegistered( session );

            if (!registered) {
                ws.getNamespaceRegistry().registerNamespace("drools", RulesRepository.DROOLS_URI);
                
                //Note, the order in which they are registered actually does matter !
                this.registerNodeTypesFromCndFile("/node_type_definitions/tag_node_type.cnd", ws);
                this.registerNodeTypesFromCndFile("/node_type_definitions/state_node_type.cnd", ws);
                this.registerNodeTypesFromCndFile("/node_type_definitions/versionable_node_type.cnd", ws);
                this.registerNodeTypesFromCndFile("/node_type_definitions/versionable_asset_folder_node_type.cnd", ws);
                
                this.registerNodeTypesFromCndFile("/node_type_definitions/rule_node_type.cnd", ws);
                this.registerNodeTypesFromCndFile("/node_type_definitions/rulepackage_node_type.cnd", ws);
             
            }
            
            // Setup the rule repository node
            Node repositoryNode = RulesRepository.addNodeIfNew(root, RulesRepository.RULES_REPOSITORY_NAME, "nt:folder");
                    

            
            // Setup the RulePackageItem area        
            RulesRepository.addNodeIfNew(repositoryNode, RulesRepository.RULE_PACKAGE_AREA, "nt:folder");
            
            // Setup the Snapshot area        
            RulesRepository.addNodeIfNew(repositoryNode, RulesRepository.PACKAGE_SNAPSHOT_AREA, "nt:folder");
                        
            //Setup the Cateogry area                
            RulesRepository.addNodeIfNew(repositoryNode, RulesRepository.TAG_AREA, "nt:folder");
            
            //Setup the State area                
            RulesRepository.addNodeIfNew(repositoryNode, RulesRepository.STATE_AREA, "nt:folder");
            
            //and we need the "Draft" state
            RulesRepository.addNodeIfNew( repositoryNode.getNode( RulesRepository.STATE_AREA ), StateItem.DRAFT_STATE_NAME, StateItem.STATE_NODE_TYPE_NAME );
            
            session.save();                        
        }
        catch(Exception e) {
            log.error("Caught Exception", e);
            System.err.println(e.getMessage());
            throw new RulesRepositoryException(e);
        }
    }
    
    private void registerNodeTypesFromCndFile(String cndFileName, Workspace ws) throws RulesRepositoryException, InvalidNodeTypeDefException {
        try {
            //Read in the CND file
            Reader in = new InputStreamReader(this.getClass().getResourceAsStream( cndFileName ));
            
            // Create a CompactNodeTypeDefReader
            CompactNodeTypeDefReader cndReader = new CompactNodeTypeDefReader(in, cndFileName);
            
            // Get the List of NodeTypeDef objects
            List ntdList = cndReader.getNodeTypeDefs();
            
            // Get the NodeTypeManager from the Workspace.
            // Note that it must be cast from the generic JCR NodeTypeManager to the
            // Jackrabbit-specific implementation.
            NodeTypeManagerImpl ntmgr = (NodeTypeManagerImpl)ws.getNodeTypeManager();
            
            // Acquire the NodeTypeRegistry
            NodeTypeRegistry ntreg = ntmgr.getNodeTypeRegistry();
            
            // Loop through the prepared NodeTypeDefs
            for(Iterator i = ntdList.iterator(); i.hasNext();) {                               
                // Get the NodeTypeDef...
                NodeTypeDef ntd = (NodeTypeDef)i.next();                                        
                
                log.debug("Attempting to regsiter node type named: " + ntd.getName());
                
                // ...and register it            
                ntreg.registerNodeType(ntd);
            }
        }
        catch(InvalidNodeTypeDefException e) {
            log.warn("InvalidNodeTypeDefinitionException caught when trying to add node from CND file: " + cndFileName + ". This will happen if the node type was already registered. " + e);
            throw e;
        }
        catch(Exception e) {
            log.error("Caught Exception", e);
            throw new RulesRepositoryException(e);
        }
    }    
    
    
}
