package com.k_int.webapp3.util;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.k_int.bank.service.Constants;
import com.k_int.bank.service.dto.ImportReportDTO;
import com.k_int.bank.service.dto.TermInfoDTO;
import com.k_int.bank.service.dto.TermRelationDTO;
import com.k_int.bank.service.dto.VocabInfoDTO;
import com.k_int.bank.service.dto.VocabReferenceDTO;
import com.k_int.commons.util.XMLUtil;
import com.k_int.svc.custprops.dto.IdentifierPropertyValueDTO;
import com.k_int.svc.custprops.dto.LocalisedStringPropertyValueDTO;
import com.k_int.svc.custprops.dto.PropertyValueDTO;
import com.k_int.svc.custprops.dto.SimplePropertyValueDTO;

public class DTOToDoc {
  private static final Log log = LogFactory.getLog(DTOToDoc.class);
  private static Map<String, String> namespaces = new HashMap<String, String>();
  
  static {
    namespaces.put("http://purl.org/dc/elements/1.1", "dc");
  }
  
  
  public static Element getZThesElement(Document doc) {
     Element zthes = doc.createElement("Zthes");
     zthes.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
     String dc_ns = "http://purl.org/dc/elements/1.1";
     zthes.setAttribute("xmlns:"+namespaces.get(dc_ns),  dc_ns);
     zthes.setAttribute("xsi:noNamespaceSchemaLocation", "http://www.k-int.com/schema/zthes-1.1.xsd");
     return zthes;
  }
  
 
  private static Element getThesNote(Document doc, String label, String value) {
    Element note = doc.createElement("thesNote");
    note.setAttribute("label", label);
    note.setTextContent(value);
    return note;
  }
  
  
  private static Element createElement(Document doc, String name) {
    Element ret = null;
    
    if (name.contains("#")) {
      String ns = name.substring(0, name.indexOf("#"));
      String tag_name = name.substring(name.indexOf("#")+1);
      if (namespaces.get(ns)!=null) {
        ret = doc.createElementNS(ns, namespaces.get(ns)+":"+tag_name);
      } else {
        ret = doc.createElement("termNote");
        ret.setAttribute("label", tag_name);
      }
    } else {
      ret = doc.createElement(name);
    }
    
    return ret;
  }
  
  
  private static Element getPropertyTypeElement(Document doc, String name, PropertyValueDTO prop) {
    if (prop==null) throw new NullPointerException("Property was null");
    Element ret = null;
    
    if (prop instanceof SimplePropertyValueDTO) {
      SimplePropertyValueDTO sprop = (SimplePropertyValueDTO)prop;
      ret = createElement(doc, name);
      ret.setTextContent(sprop.getValue());
      
    } else if (prop instanceof IdentifierPropertyValueDTO) {
      IdentifierPropertyValueDTO iprop = ((IdentifierPropertyValueDTO)prop);
      ret = doc.createElement("termNote");
      ret.setAttribute("label", name);
      ret.setTextContent(iprop.getValue());
      if (iprop.getAuthorty()!=null) {
        ret.setAttribute("vocab", iprop.getAuthorty());
      }

    } else if (prop instanceof LocalisedStringPropertyValueDTO) {
      LocalisedStringPropertyValueDTO lprop = (LocalisedStringPropertyValueDTO) prop;
      ret = createElement(doc, name);

      
      for (Map.Entry<Locale, String> ent : lprop.getValue().entrySet()) {
        Element lang  = doc.createElement("langstring");
        String locale = ent.getKey().getLanguage()+"-"+ent.getKey().getCountry();
        lang.setAttribute("xml:lang", locale);
        lang.setTextContent(ent.getValue());
        ret.appendChild(lang);
      }
      
    } else {
      log.error("Cannot map property with class "+prop.getClass()+" and value "+prop.getValue());
    }

    return ret;
  }
  
  
  public static Document getTermZThes(TermInfoDTO term) throws ParserConfigurationException {
    if (term==null) throw new NullPointerException("Term was null");
    
    Document doc  = XMLUtil.getEmptyDocument();
    Element zthes = getZThesElement(doc);
    Element thes  = getVocabThes(doc, term.getVocabReference());
    
    zthes.appendChild(thes);
    zthes.appendChild(getTermElement(doc, term));
    
    doc.appendChild(zthes);
    
    return doc;
  }
  
  
  private static Element getTermElement(Document doc, TermInfoDTO term) {
    if (term==null) throw new NullPointerException("Term was null");
    
    Element ret = doc.createElement("term");
    
    if ((term.getIsTopTerm()!=null && term.getIsTopTerm().equals(true)) || (term.getTags()!=null && term.getTags().contains(Constants.TERM_TAG_TOP_TERM))) {
      ret.setAttribute("browseRoot", "true");
    } else {
      ret.setAttribute("browseRoot", "false");
    }
     
    
    Element term_id = doc.createElement("termId");
    term_id.setTextContent(term.getIdentifier());
    ret.appendChild(term_id);
    
    ret.appendChild(getPropertyTypeElement(doc, "termName", term.getName()));
    
    Element term_type = doc.createElement("termType");
    term_type.setTextContent(term.getTermType());
    ret.appendChild(term_type);
    
    Element term_status = doc.createElement("termStatus");
    term_status.setTextContent(term.getStatus().toString());
    ret.appendChild(term_status);
    
    for (Map.Entry<String, PropertyValueDTO> ent : term.getProperties().entrySet()) {
      //term name already manually put in xml above
      if (!ent.getKey().equals("dc:title")  && !ent.getKey().equals("http://purl.org/dc/elements/1.1#title")) {
        Element prop = getPropertyTypeElement(doc, ent.getKey(), ent.getValue());
        ret.appendChild(prop);
      }
    }

    if (term.getSourceTermVocabulary()!=null) {
      Element source = doc.createElement("termNote");
      source.setAttribute("label", "source");
      source.setTextContent(term.getSourceTermVocabulary());
      ret.appendChild(source);
    }
    
    for (TermRelationDTO rel : term.getRelations()) {
      Element relation = doc.createElement("relation");
      
      Element type = doc.createElement("relationType");
      type.setTextContent(rel.getRelationType().getMnemonic());
      relation.appendChild(type);
      
      Element relId = doc.createElement("termId");
      relId.setTextContent(rel.getRelatedTerm().getIdentifier());
      relation.appendChild(relId);

      if (rel.getRelatedTerm().getDisplayName()!=null && !rel.getRelatedTerm().getDisplayName().equals("")) {
        Element relName = doc.createElement("termName");
        relName.setTextContent(rel.getRelatedTerm().getDisplayName());
        relation.appendChild(relName);
      }
      
      ret.appendChild(relation);
    }
    
    return ret;
  }

  
  public static Document getTermsByTagZThes(List<TermInfoDTO> terms, VocabInfoDTO vocab) throws ParserConfigurationException {
    Document doc  = XMLUtil.getEmptyDocument();
    Element zthes = getZThesElement(doc);
    Element thes  = getVocabThes(doc, vocab);
    
    zthes.appendChild(thes);
    
    for (TermInfoDTO term : terms) {
      zthes.appendChild(getTermElement(doc, term));
    }
    
    doc.appendChild(zthes);
    
    return doc;
  }
  
  
  public static Element getVocabThes(Document doc, VocabInfoDTO vocab) {
    if (vocab==null) throw new NullPointerException("VocabInfoDTO was null.");
    
    // thes element
    log.debug("Entering getVocabZThes");
    Element thes = createVocabThes(doc, vocab.getIdentifier(), vocab.getAuthority(), vocab.getProfile());
    
    for (Map.Entry<String, PropertyValueDTO> ent : vocab.getProperties().entrySet()) {
      try {
        thes.appendChild(getPropertyTypeElement(doc, ent.getKey(), ent.getValue()));
      } catch (DOMException dome) {
        // cannot create element - probably an xthes term note
      }
    }
    
    return thes;
  }
  
  
  public static Element getVocabThes(Document doc, VocabReferenceDTO vocab) {
    if (vocab==null) throw new NullPointerException("VocabReferenceDTO was null.");
    Element thes = createVocabThes(doc, vocab.getIdentifier(), vocab.getAuthority(), vocab.getProfile());
    
    return thes;
    
  }
  
  
  private static Element createVocabThes(Document doc, String vocab_id, String vocab_auth, String vocab_profile) {
    Element thes = doc.createElement("thes");
    
    thes.appendChild(getThesNote(doc, "authority", vocab_auth));
    thes.appendChild(getThesNote(doc, "profile", vocab_profile));
    thes.appendChild(getPragmaElement(doc));
    
    return thes;
  }
  
  
  private static Element getPragmaElement(Document doc) {
    Element pragma = doc.createElement("pragma");
    pragma.setAttribute("completion", "partial");
    
    return pragma;
  }
  
  public static Document createReportDocument(ImportReportDTO report) throws ParserConfigurationException {
    DocumentBuilderFactory factory  = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    factory.setValidating(false);
    DocumentBuilder builder = factory.newDocumentBuilder();
    
    Document doc = builder.newDocument();
    Element root = doc.createElement("submitReport");

    if (report.getStatus()==ImportReportDTO.SUCCESS) {
      Element voc_id = doc.createElement("vocabIdentifier");
      voc_id.setTextContent(report.getVocabIdentifier());
      root.appendChild(voc_id);
      
      Element voc_auth = doc.createElement("vocabAuthority");
      voc_auth.setTextContent(report.getVocabAuthority());
      root.appendChild(voc_auth);
      
      Element profile = doc.createElement("profile");
      profile.setTextContent(report.getProfile());
      root.appendChild(profile);
      
      Element cc = doc.createElement("changeCount");
      cc.setTextContent(report.getChangeCount()+"");
      root.appendChild(cc);
      
      Element added = doc.createElement("added");
      added.setTextContent(report.getAdded()+"");
      root.appendChild(added);
      
      Element deleted = doc.createElement("deleted");
      deleted.setTextContent(report.getDeleted()+"");
      root.appendChild(deleted);
      
      Element unchanged = doc.createElement("unchanged");
      unchanged.setTextContent(report.getUnchanged()+"");
      root.appendChild(deleted);
      
      Element updated = doc.createElement("updated");
      updated.setTextContent(report.getUpdated()+"");
      root.appendChild(updated);
      
      Element average = doc.createElement("averageTermImportTime");
      average.setTextContent(report.getAverageTermImportTime()+"");
      root.appendChild(average);
      
      Element longest = doc.createElement("longestTermImportTime");
      longest.setTextContent(report.getLongestTermImportTime()+"");
      root.appendChild(longest);
      
      Element shortest = doc.createElement("shortestTermImportTime");
      shortest.setTextContent(report.getShortestTermImportTime()+"");
      root.appendChild(shortest);
      
      Element total_time = doc.createElement("totalTime");
      total_time.setTextContent(report.getTotalTime()+"");
      root.appendChild(total_time);
      
      Element new_rev = doc.createElement("newRevision");
      new_rev.setTextContent(report.getNewRevision()+"");
      root.appendChild(new_rev);

      Element old_rev = doc.createElement("oldRevision");
      old_rev.setTextContent(report.getOldRevision()+"");
      root.appendChild(old_rev);

      
    } else {
      Element err = doc.createElement("error");
      err.setTextContent(report.getErrorMessage());
      root.appendChild(err);
    }

    doc.appendChild(root);
    try {
      System.out.println(XMLUtil.getIndentedText(doc));
    } catch (Exception e) {}
    
    return doc;
  }
}
