package com.k_int.openxsd.analysis;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.k_int.openxsd.analysis.iface.SchemaAnalysis;
import com.k_int.openxsd.analysis.iface.SimplePathAnalysis;
import com.k_int.openxsd.core.BasePathDescription;
import com.k_int.openxsd.core.ComplexPathDescription;
import com.k_int.openxsd.core.DocumentPath;
import com.k_int.openxsd.core.ElementDeclaration;
import com.k_int.openxsd.core.ElementRef;
import com.k_int.openxsd.core.NameRef;
import com.k_int.openxsd.core.SimplePathDescription;
import com.k_int.openxsd.core.TypeDefinition;

public class PathBuilder
{
    public static void populatePathAnalysis(SimplePathAnalysis path_analysis,SchemaAnalysis schema_analysis)
    {
        Iterator<NameRef> i = schema_analysis.getPossibleRoots();
        while(i.hasNext())
        {
            NameRef ref = i.next();
            buildPath(ref,path_analysis,schema_analysis);
        }
    }
    
    public static void populateFromRoot(NameRef root,SimplePathAnalysis path_analysis,SchemaAnalysis schema_analysis) throws AnalysisException
    {
        if(schema_analysis.isPossibleRoot(root))
            buildPath(root,path_analysis,schema_analysis);
        else
            throw new AnalysisException("Reference "+root+" is not a possible root element in the schema");
    }
    
    private static void buildPath(NameRef root,SimplePathAnalysis path_analysis,SchemaAnalysis schema_analysis)
    {
        ElementDeclaration root_element = schema_analysis.getElementDeclaration(root);
        DocumentPath root_path = new DocumentPath(root);
        path_analysis.addDocumentPath(root_path);
        TypeDefinition def = schema_analysis.getTypeDefinition(root_element.getType());
       
        if(def.getType()==TypeDefinition.SIMPLE)
        {
            SimplePathDescription desc = new SimplePathDescription(1,1);
            root_path.setDescription(desc);
            createSimplePath(root_path,def,schema_analysis,path_analysis);
        }
        else
        {
            ComplexPathDescription desc = new ComplexPathDescription(1,1);
            root_path.setDescription(desc);
            createComplexPath(root_path, def,schema_analysis,path_analysis);
        }
    }
      
    
    private static void createSimplePath(DocumentPath parent_path,TypeDefinition def,SchemaAnalysis schema_analysis,SimplePathAnalysis path_analysis)
    {        
        
        System.out.println("Create simple path "+parent_path.getSimplePath()+ ": "+parent_path.getDocumentPath());
        
        SimplePathDescription desc = (SimplePathDescription)parent_path.getDescription();
        desc.setMinLength(def.getMinLength());
        desc.setMaxLength(def.getMaxLength());
        desc.setAttributes(def.getAttributes());
        
        Iterator<String> i = def.getPatternIterator();
        while(i.hasNext())
        {
            String pattern = i.next();
            desc.addPattern(pattern);
        }
        
        i = def.getPossibleValuesIterator();
        while(i.hasNext())
        {
            String val = i.next();
            desc.addValue(val);
        }
        
       path_analysis.addDocumentPath(parent_path);
        
        /**path_context.attribute_map = type_definition.getAttributeMap();
        Iterator i = type_definition.getPatternIterator();

        while(i.hasNext()) {
          path_context.pattern = (String) i.next();
          //System.out.println("---- Pattern is "+path_context.pattern);
          break;      
        }
        
            
        ArrayList values = new ArrayList();
        i = type_definition.getPossibleValuesIterator();
        while(i.hasNext()) {
          values.add((String) i.next());
          //System.out.println("---- Adding value "+values.get(values.size()-1));
        }
        String[] possible_values = {};
        path_context.possible_values = (String[]) values.toArray(possible_values);
        //path_contexts.put(path_context.path, path_context);
        path_context_analysis.addPathContext(path_context.getPath(), path_context);  
        //System.out.println("Adding path context "+path_context.path);
        if(path_context.getPath().lastIndexOf("/")==0)
          return;
          
        Map the_map = type_definition.getValueSourceMap();
        
        if(the_map.size()>0)
          path_context.associated_value_map=the_map;
          
        Node the_node = schema_analysis.createNodeFromSchema(doc, path_context.getPath(),path_context.getgNamespaceUri());
        Node clone = the_node.cloneNode(true);
        node_context_analysis.addNode(path_context.getPath(), clone);  **/
    }
    
    private static void createComplexPath(DocumentPath parent_path,TypeDefinition def,SchemaAnalysis schema_analysis,SimplePathAnalysis path_analysis)
    {
        System.out.println("Create complex path "+parent_path.getSimplePath()+": "+parent_path.getDocumentPath());       
        // length facets probably not needed here but haven't time to check :)
        ComplexPathDescription desc = (ComplexPathDescription)parent_path.getDescription();

        desc.setMinLength(def.getMinLength());
        desc.setMaxLength(def.getMaxLength());
        desc.setAttributes(def.getAttributes());
       
        Iterator<ElementRef> i = def.getElementRefIterator();
        
        while(i.hasNext()) 
        {
          ElementRef element_ref    = i.next();
          ElementDeclaration dec    = element_ref.getElementDec();         
          NameRef name              =  new NameRef(dec.getName(),dec.getNamespace());
          
          if(parent_path.getDocumentPath().contains(name)) // ignore recursion
              continue;
          
          desc.addChildElement(name); // finished the stuff for the parent
          
          // now onto the 'child'
          
          int min_occurs = element_ref.getMinOccurs();
          int max_occurs = element_ref.getMaxOccurs();            
          
          
          /**if(min_occurs>0) 
          {
            if ( ! child_name.equals("grp.any") ) 
            {
              mandatory_child_elements.add(child_name);  
            }
          }**/
                  
          /**if ( ! child_name.equals("grp.any") ) {
            child_elements.add(child_name);  
          }
          else {
            child_context.allow_any = true;
          }**/
          DocumentPath child_path           = new DocumentPath(parent_path,name);
          TypeDefinition child_def          = schema_analysis.getTypeDefinition(dec.getType());
          BasePathDescription parent_desc   = parent_path.getDescription();
          BasePathDescription child_desc;
          
          if(child_def.getType()==TypeDefinition.SIMPLE)
              child_desc = new SimplePathDescription(min_occurs,max_occurs);
          else
              child_desc = new ComplexPathDescription(min_occurs,max_occurs);
         
          child_path.setDescription(child_desc);
         
          boolean optional_parent;
          
          if(parent_desc.getMinOccurs()==0 || parent_desc.hasOptionalParent()==true)
              optional_parent=true;
          else
              optional_parent=false;
          
          child_desc.setOptionalParent(optional_parent);
         
          if(child_def.getType()==TypeDefinition.SIMPLE)
              createSimplePath(child_path,child_def,schema_analysis,path_analysis);
          else
              createComplexPath(child_path,child_def,schema_analysis,path_analysis);
        }                
        path_analysis.addDocumentPath(parent_path);
       /** String[] children={};
        String[] mandatory_children = {};

        path_context.mandatory_child_elements=(String[]) mandatory_child_elements.toArray(mandatory_children);
        path_context.child_elements = (String[]) child_elements.toArray(children);
        
        path_context_analysis.addPathContext(path_context.getPath(), path_context);
        
        //System.out.println("Adding path context "+path_context.path);
        if(path_context.getPath().lastIndexOf("/")==0) // already have the root element as a document
          return;
        Node the_node = schema_analysis.createNodeFromSchema(doc, path_context.getPath(),path_context.getgNamespaceUri());
        Node clone = the_node.cloneNode(true);
        node_context_analysis.addNode(path_context.getPath(), clone);**/       
    }
    
    public static Map<String,String> createNsToPrefixMap(Element ns_node)
    {
        NamedNodeMap node_map       = ns_node.getAttributes();
        Map<String,String> retval   = new HashMap<String,String>();
        for(int i=0;i<node_map.getLength();i++)
        {
            Node attr = node_map.item(i);
            String name = attr.getNodeName();
            String ns_uri = attr.getNodeValue();
            int prefix_index = name.lastIndexOf(":");
            if(prefix_index==-1)
                name=null;
            else
                name=name.substring(prefix_index+1,name.length());
            
            retval.put(ns_uri, name);
            
        }
               
        return retval;
    }
}
