/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * published by the Free Software Foundation; either version 2.1 of
 * the license, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite
 * 330, Boston, MA  02111-1307, USA.
 * 
 */
package com.k_int.zthes;

import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.k_int.xrtree.DataNode;
import com.k_int.xrtree.DataTypes;
import com.k_int.xrtree.GenericXRDbManager;
import com.k_int.xrtree.XRNode;
import com.k_int.xrtree.XRNodeRelationship;


/**
 * Title:		Zthes1_0Walker
 * @author: 	rob
 * @version:	$Id: v Exp $ 
 * @since			
 * Copyright:	1999-2007 Knowledge Integration Ltd
 * Company:		Knowledge Integration Ltd
 * Description:
 *
 *
 *
 * Created:		25 Feb 2008
 *
 *
 * History:
 *				$Log: v $
 *
 * 
 */

public class GenericZthes1_0Walker 
{
  private File                    				zthes_file;
  private Document                				doc;
  private DocumentBuilder         				doc_builder;
  private DocumentBuilderFactory  				d_factory;
  private HashMap<String,DataNode>				data_nodes;
  private HashMap<String,List<String>>          navigation;
  private HashMap<String,XRNodeRelationship>	term_rels;
  private HashMap<String,List<String>>          categories;
  private GenericXRDbManager      				manager;
  private String                  				xr_root;
  private String identifier;
  private boolean use_term_source = true;
  private int vocab_type          = DataTypes.VOCABULARY;
  private String guid             = null;
  private String id               = null;
  private String auth             = null;
 
  public void setZthesFilename(String file_name) throws AnalysisException {
      zthes_file = new File(file_name);
      
      if(zthes_file.exists()==false || zthes_file.isDirectory())
          throw new AnalysisException("File "+file_name+" doesn't exist");
     
      try
      {
          buildDocumentFactory();
      }
      catch(Exception e)
      {
          throw new AnalysisException(e);
      }
  }
     
  private void buildDocumentFactory() throws Exception 
  {        
      d_factory   = DocumentBuilderFactory.newInstance();
      d_factory.setNamespaceAware(true);
      d_factory.setValidating(false);
      doc_builder = d_factory.newDocumentBuilder();                   
  }
  
  
  public void walkZthes(GenericXRDbManager manager, boolean use_term_source, String identifier, int vocab_type) throws AnalysisException
  {
    this.use_term_source=use_term_source;
    this.vocab_type=vocab_type;
    walkZthes(manager,identifier);
  }
  
  
  public void walkZthes(GenericXRDbManager manager, boolean use_term_source, String identifier) throws AnalysisException
  {
    this.use_term_source=use_term_source;
    walkZthes(manager,identifier);
  }
  
 
  public void walkZthes(GenericXRDbManager manager, String identifier)throws AnalysisException 
  {
      this.manager        = manager;
      this.identifier     = identifier;
      DataNode vocab_root = null;
          
      try
      {
          xr_root           = "1."+manager.getNextXrRoot();
          System.out.println(xr_root);
         
          doc               = doc_builder.parse(zthes_file);          
          NodeList vocabs   = doc.getElementsByTagName("thes");
          if(vocabs.getLength()>0)
              vocab_root = processVocabHeader((Element)vocabs.item(0));
          else
              throw new AnalysisException("No vocabulary details are present");
                                        
          
          NodeList terms    = doc.getElementsByTagName("term");
          data_nodes        = new HashMap<String,DataNode>();
          categories        = new HashMap<String,List<String>>();
          navigation        = new HashMap<String,List<String>>();
          List<String> root_terms   = new ArrayList<String>();
          term_rels         = new HashMap<String,XRNodeRelationship>();
         
          for (int i=0 ; i<terms.getLength();i++) {

              Element term    = (Element) terms.item(i);               
              DataNode node   = buildDataNode(term);
             
              extractCategories(node.getId(),term);
           
              if(node.getSource()==null)
                node.setSource(vocab_root.getId());
              
              data_nodes.put(node.getId(), node);
                     
              NodeList relations = term.getElementsByTagName("relationType");
              if(relations.getLength()==0)
                  root_terms.add(node.getId());
              else 
              {
                  int rt=0;
                  int bt=0;
                  int use=0;
                 // int uf=0;
                  int nt=0;
                  List<String> children = new ArrayList<String>();
                 // List dest_rels= new ArrayList();

                  for(int j=0;j<relations.getLength();j++) {

                      Element relationType = (Element) relations.item(j);       
                      String type = relationType.getTextContent();
                      Element relation = (Element) relationType.getParentNode();
                      String id  =  ((Element) relation.getElementsByTagName("termId").item(0)).getTextContent();
                     
                      if(type.equals("BT"))                     
                          bt++;
                      else if(type.equals("USE"))                     
                          use++;                    
                      else {                      
                          children.add(id);  
                          
                          if(type.equals("RT"))
                              rt++;
                          else if(type.equals("NT"))
                              nt++;
                      }

                      XRNodeRelationship rel = new XRNodeRelationship();
                      rel.setSourceId(node.getId());
                      rel.setDestinationId(id); // this gets changed to the XR node id later...
                      if(type.equals("BT"))
                          rel.setType(ZthesRelTypes.BT);
                      else if (type.equals("NT"))
                          rel.setType(ZthesRelTypes.NT);
                      else if (type.equals("USE"))
                          rel.setType(ZthesRelTypes.USE);
                      else if (type.equals("UF"))
                          rel.setType(ZthesRelTypes.UF);
                      else if (type.equals("LE"))
                          rel.setType(ZthesRelTypes.LE);
                      else if (type.equals("RT"))
                          rel.setType(ZthesRelTypes.RT);
                      
                      // Make an entry for term relation
                      term_rels.put(rel.getSourceId()+rel.getDestinationId(),rel);
                  } 

                  if(children.size()>0) 
                      navigation.put(node.getId(), children);
                                
                  if(bt==0 && use==0) {
                      String browse = term.getAttribute("browseRoot");
                      if(browse!=null && browse.trim().equalsIgnoreCase("true"))
                          root_terms.add(node.getId());
                      else if(rt>0 && nt==0)
                      {;}
                      else {
                          root_terms.add(node.getId());                        
                      }
                  }
              }
          }
          //System.out.println("Root terms "+root_terms);
          navigation.put(vocab_root.getId(), root_terms);
         
          processData(vocab_root); 
          manager.addRevision(guid,id,"-1",auth);
      }
      catch(Exception e)
      {
          throw new AnalysisException(e);
      }        
  }
  
  private void extractCategories(String id,Element term)
  {    
    NodeList cat_nodes = term.getElementsByTagName("termCategory");
    List<String> cat_ids       = categories.get(id);
    if(cat_ids==null)
    {
      cat_ids = new ArrayList<String>();
      categories.put(id,cat_ids);
    }
    
    for(int i=0;i<cat_nodes.getLength();i++)
    {
      Element cat_elem = (Element)cat_nodes.item(i);
      String cat_id = cat_elem.getAttribute("identifier");
      if(cat_id!=null && cat_id.trim().length()>0)
      {
        cat_id=cat_id.trim();
        if(cat_ids.contains(cat_id)==false)
          cat_ids.add(cat_id);
      }
      
    }
  }
  
  
  private DataNode processVocabHeader(Element thes)
  {
      DataNode retval = new DataNode();
      retval.setType(vocab_type);
     
      String name = thes.getElementsByTagName("dc:title").item(0).getTextContent().trim();
      String id   = thes.getElementsByTagName("dc:identifier").item(0).getTextContent().trim();
      NodeList desc_nodes = thes.getElementsByTagName("dc:description");
      String description=null;
      if(desc_nodes.getLength()>0)
    	  description = desc_nodes.item(0).getTextContent().trim();
      this.id=id;
      String guid = extractGuid(thes)  ; 
      auth = extractAuthority(thes);
      this.guid=guid;
      if(guid==null)
        this.guid=id;
      if(identifier==null || identifier.equals("guid"))
      {
        if(guid!=null)
          id=guid;
      }
      
      retval.setName(name);
      retval.setId(id);
      retval.setSource(retval.getId());
      retval.setDescription(description);
   
      return retval;                
  }
  
  
  public String extractVocabId()
  {
    try
    {
      doc               = doc_builder.parse(zthes_file);          
      NodeList vocabs   = doc.getElementsByTagName("thes");
      if(vocabs.getLength()>0)
          processVocabHeader((Element)vocabs.item(0));
      else
          throw new AnalysisException("No vocabulary details are present");
                                      
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
    return id;
  }
  
  private String extractGuid(Element element) {
      String retval=null;
      NodeList list = element.getElementsByTagName("thesNote");
      
      for(int i=0;i<list.getLength();i++)
      {
          Element note = (Element) list.item(i);
          String label = note.getAttribute("label");
          if(label!=null && label.equals("globallyUniqueId"))
          {
              retval = note.getTextContent().trim();
              break;
          }
      }
      
      
      return retval;
  }
  
  private String extractAuthority(Element element) 
  {
    String retval=null;
    NodeList list = element.getElementsByTagName("thesNote");
    
    for(int i=0;i<list.getLength();i++)
    {
        Element note = (Element) list.item(i);
        String label = note.getAttribute("label");
        if(label!=null && label.equals("authority"))
        {
            retval = note.getTextContent().trim();
            break;
        }
    }
    
    
    return retval;
}
  
  private String extractSource(Element element)
  {
      String retval=null;
      NodeList list = element.getElementsByTagName("termNote");
      
      for(int i=0;i<list.getLength();i++)
      {
          Element note = (Element) list.item(i);
          String label = note.getAttribute("label");
          if(label.equals("source"))
          {
              retval = note.getTextContent();
              break;
          }
      } 
      if(retval==null || retval.trim().length()==0)
      {
        list = element.getElementsByTagName("termVocabulary");
        for(int j=0;j<list.getLength();j++)
        {
          Element vocab = (Element) list.item(j);
          retval=vocab.getTextContent();
        }
        
      }
      return retval;
  }
  
  
  private void processData(DataNode vocab_root) throws SQLException {  
      
      manager.addData(vocab_root);     
      XRNode root_node= new XRNode();
      root_node.setXRIndex(xr_root);
      
      root_node.setDataId(vocab_root.getId());
      root_node.setDataSource(vocab_root.getSource());
      manager.addXRNode(root_node);
      
      // rel between xr tree root and vocab root
      XRNodeRelationship node_rel = new XRNodeRelationship();
      node_rel.setSourceId("1");
      node_rel.setDestinationId(root_node.getXRIndex());       
      manager.addXRNodeRelationships(node_rel);
      
      node_rel = new XRNodeRelationship();
      node_rel.setSourceId(root_node.getXRIndex());
      node_rel.setDestinationId("1");       
      manager.addXRNodeRelationships(node_rel);
              
      List<String> root_terms = navigation.get(vocab_root.getId());
      Iterator<String> possible_top_level_terms = root_terms.iterator();
      
      int counter=1;
      
      while(possible_top_level_terms.hasNext())
      {
          String top_level= possible_top_level_terms.next();           
          List<String> children   = navigation.get(top_level);
          
         
          XRNode top_level_node = new XRNode();
          top_level_node.setXRIndex(root_node.getXRIndex()+"."+counter);

          DataNode top_level_data = (DataNode)data_nodes.get(top_level);
          top_level_node.setDataId(top_level_data.getId());
          top_level_node.setDataSource(top_level_data.getSource());
        
          manager.addXRNode(top_level_node); 
          
          node_rel = new XRNodeRelationship();
          node_rel.setSourceId(top_level_node.getXRIndex());
          node_rel.setDestinationId(root_node.getXRIndex());
          
          manager.addXRNodeRelationships(node_rel);
          node_rel = new XRNodeRelationship();
          node_rel.setSourceId(root_node.getXRIndex());
          node_rel.setDestinationId(top_level_node.getXRIndex());
          
          manager.addXRNodeRelationships(node_rel);
          counter++;
          if(children==null)
              continue;
          
          processNavigation(top_level, children, top_level_node.getXRIndex());        
         
      }
      Iterator<String> i = data_nodes.keySet().iterator();
      while(i.hasNext())
      {
          DataNode node = data_nodes.get(i.next());          
          manager.addData(node);
          String id = node.getId();
          List<String> cats = categories.remove(id);
          if(cats!=null && cats.size()>0)
          {
            Iterator<String> j = cats.iterator();
            while(j.hasNext())
            {
              String cat = j.next();
              manager.addCategory(id, cat);
            }
          }
      }
  }
  
  
  private void processNavigation(String parent, List<String> children, String parent_xr_id) throws SQLException
  {  
      if(children==null)
          return;
      
      Iterator<String> i = children.iterator(); 
      
      int modifier=1;
      
      // Iterate through all the children of node with ID parent
      while(i.hasNext()) 
      {            
          String child_id = i.next();            
          XRNode node = new XRNode();
          node.setXRIndex(parent_xr_id+"."+modifier);
          
          DataNode child = data_nodes.get(child_id);

          if(child == null) 
          {
            // Here we continue *But* this means we will never call manager.addXRNode(node) which means
            // we're missing a node in the final tree?
            continue;
          }

          // This relation points at node with childs id
          node.setDataId(child.getId());
          node.setDataSource(child.getSource());

          // Add this new node
          manager.addXRNode(node);

          List<String> next_level = navigation.get(child_id);
          
          XRNodeRelationship rel = (XRNodeRelationship) term_rels.get(parent+child_id);
          
          rel.setDestinationId(node.getXRIndex());
          rel.setSourceId(parent_xr_id);
          manager.addXRNodeRelationships(rel);
          
          rel = term_rels.get(child_id+parent);
          if ( rel != null ) 
          {
            rel.setDestinationId(parent_xr_id);
            rel.setSourceId(node.getXRIndex());
            manager.addXRNodeRelationships(rel);
          }
       
          if(next_level !=null && next_level.contains(parent)==false ) 
              processNavigation(child_id, next_level, node.getXRIndex());    
         
          modifier++;
      }      
  }
  
 private DataNode buildDataNode(Element term)
 {
      String name = ((Element) term.getElementsByTagName("termName").item(0)).getTextContent().trim();
      String id  =  ((Element) term.getElementsByTagName("termId").item(0)).getTextContent().trim();
      
      //System.out.println("Building data node for: "+name+": "+id);
      NodeList types = term.getElementsByTagName("termType");
      int type = 0;
      if(types.getLength()>0)
      {   
          String term_type=  ((Element) term.getElementsByTagName("termType").item(0)).getTextContent();
          //System.out.println(term_type);
          if(term_type!=null && term_type.trim().equals("")==false)
          {
              if(term_type.equals("PT"))
                 type=DataTypes.PT; 
              else if(term_type.equals("NL"))
                  type = DataTypes.NL;
              else if (term_type.equals("ND"))
                  type=DataTypes.NPT;          
          }
      } 
      
      NodeList notes = term.getElementsByTagName("termNote");
      String desc_attr_note=null;
      String no_attr_note = null;
      if(notes.getLength()>0)
      {
    	  for(int i=0;i<notes.getLength();i++)
    	  {
    		  
    		  Element note = (Element)notes.item(i);
    		  if(note.getAttributes().getLength()==0)
    			  no_attr_note=note.getTextContent().trim();
    		  else if (note.getAttribute("description").trim().length()>0)
    			  desc_attr_note=note.getAttribute("description").trim();
    		  else if (note.getAttribute("definition").trim().length()>0)
    			  desc_attr_note=note.getAttribute("definition").trim();
    		  
    	  }
      }
      
      
      DataNode result = new DataNode();
      if(use_term_source)
        result.setSource(extractSource(term));
     
      result.setType(type);
      result.setName(name);
      result.setId(id);
      if(desc_attr_note!=null)
    	  result.setDescription(desc_attr_note);
      else if (no_attr_note!=null)
    	  result.setDescription(no_attr_note);
      return result;
  }

}
