/*
* Copyright 2003-2010 Tufts University  Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 * http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

/**
 *
 * @author akumar03
 */
package tufts.vue;


import java.security.interfaces.DSAKey;
import java.util.*;
import java.net.*;
import java.io.*;

import java.text.SimpleDateFormat;

import javax.activation.MimetypesFileTypeMap;


import edu.tufts.vue.metadata.VueMetadataElement;

//required for publishing to Fedora
import fedora.client.FedoraClient;
import fedora.client.utility.ingest.AutoIngestor;
import fedora.client.utility.AutoFinder;
import fedora.server.types.gen.Datastream;
import fedora.server.types.gen.FieldSearchResult;
import fedora.server.types.gen.FieldSearchQuery;
import fedora.client.Uploader;
import fedora.server.types.gen.Condition;
import fedora.server.types.gen.ComparisonOperator;

public class FedoraPublisher {
    public static final String HTTPS = "https";
    public static final String HTTP = "http";
    public static final String FEDORA_URL_PATH = "/fedora/";
    public static final String ENCODING = "UTF-8";
    public static final String COMMENT = "Object published through VUE";
    public static final boolean VERSIONABLE  = true;
    
    public static final String RESOURCE_DS = "RESOURCE";
    public static final String DC_DS = "DC";
    public static final String VUE_DS = "map.vue";
    
    
    public static final String FORMAT = "foxml1.0";
    public static final String VUE_FORMAT_URL = "http://vue.tufts.edu/docs/vueformat/";
    public static final String ONT_TYPE_METADATA = "http://vue.tufts.edu/ontology/vue.rdfs#ontoType";
    public static final String FEDORA_ONTOLOGY = "http://www.fedora.info/definitions/1/0/fedora-relsext-ontology.rdfs#";
    public static final String DC_URL = VueResources.getString("metadata.dublincore.url");
    public static final String RELS_URL ="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
    
    
    public static final String VUE_MIME_TYPE ="application/vue";
    public static final String XML_MIME_TYPE ="text/xml";
    
    public static final String DC_LABEL = "Dublin Core Metadata";
    public static final String RELS_LABEL ="Relationships to other objects";
    
    
    public static final String MAP_DS = "map.vue";
    public static final String RELS_DS = "RELS-EXT";
    
    public static final String VUE_CM =  "tufts/vue/map/generic";
    public static final String OTHER_CM = "tufts/vue/other";
    public static final String REMOTE_CM = "tufts/vue/remote";
    
    public static final String RESULT_FIELDS[] = {"pid"};
    
    public static final String FILE_PREFIX = "file://";
    
    static SimpleDateFormat  formatter =  new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'.000Z'");
    
    /** Creates a new instance of FedoraExporter */
    public FedoraPublisher() {
    }
    
    public static void uploadMap(edu.tufts.vue.dsm.DataSource ds, LWMap map) throws Exception {    
        addObjectToRepository(ds,VUE_CM,map.getFile(),map,map); 
    }
    
    public static void uploadArchive(edu.tufts.vue.dsm.DataSource ds, LWMap map) throws Exception {
        System.out.println("Saving archive to repository: "+map.getFile());
        //check if the file ends with vpk extension
        String  mapName = map.getFile().getName();
        if(mapName.endsWith(VueUtil.VueArchiveExtension)){
            addObjectToRepository(ds,VUE_CM,map.getFile(),map,map);
        } else {
            String archiveFilePath = map.getFile().getAbsolutePath();
            archiveFilePath = archiveFilePath.substring(0,archiveFilePath.length()-4)+VueUtil.VueArchiveExtension;
            File archiveFile = new File(archiveFilePath);
            tufts.vue.action.Archive.writeArchive(map,archiveFile);
            System.out.println("Writing Archival Object:"+archiveFile);
            addObjectToRepository(ds,VUE_CM,archiveFile,map,map);
        }
    }
    public static void uploadMapAll(edu.tufts.vue.dsm.DataSource ds, LWMap map) throws Exception{
        Properties properties = ds.getConfiguration();
        String mapLabel = map.getLabel();
        File origFile = map.getFile();
        
        File tempFile  = new File(VueUtil.getDefaultUserFolder()+File.separator+origFile.getName());
        tempFile.deleteOnExit();
        tufts.vue.action.ActionUtil.marshallMap(tempFile,map);
        
        LWMap cloneMap =   tufts.vue.action.OpenAction.loadMap(tempFile.getAbsolutePath());
        
        Iterator i = cloneMap.getAllDescendents(LWComponent.ChildKind.PROPER).iterator();
        while(i.hasNext()) {
            LWComponent component = (LWComponent) i.next();
            if(component.hasResource() && (component instanceof LWNode || component instanceof LWLink) && (component.getResource() instanceof URLResource)){
                URLResource resource = (URLResource) component.getResource();
                String pid = getFedoraPid(component);
                if(resource.isLocalFile()) {
                    File localFile = new File(resource.getSpec().replace(FILE_PREFIX,""));
                    addObjectToRepository(ds,OTHER_CM, localFile,  component, cloneMap);
                    String ingestUrl = HTTP+"://"+properties.getProperty("fedora22Address")+":"+properties.getProperty("fedora22Port")+FEDORA_URL_PATH+"get/"+pid+"/"+RESOURCE_DS;
                    component.setResource(URLResource.create(ingestUrl));
                } else if(!(resource instanceof Osid2AssetResource)) {
                    addObjectToRepository(ds,REMOTE_CM,null,component,cloneMap);
                    String ingestUrl = HTTP+"://"+properties.getProperty("fedora22Address")+":"+properties.getProperty("fedora22Port")+FEDORA_URL_PATH+"get/"+pid+"/"+RESOURCE_DS;
                    component.setResource(URLResource.create(ingestUrl));
                }
                //        System.out.println("Replacing resource: "+resource+ " with "+ingestUrl+" resource is "+resource.getClass());
                
                
            }
        }
        tufts.vue.action.ActionUtil.marshallMap(tempFile,cloneMap);
        uploadMap(ds,cloneMap);
        tufts.vue.action.ActionUtil.marshallMap(origFile, map);
        
        
    }
    
    private static void  addObjectToRepository(edu.tufts.vue.dsm.DataSource ds,String cModel,File file,LWComponent comp,LWMap map) throws Exception{
        Properties properties = ds.getConfiguration();
        System.setProperty("javax.net.ssl.trustStore", properties.getProperty("fedora22TrustStore"));
        System.setProperty("javax.net.ssl.trustStorePassword",properties.getProperty("fedora22TrustStorePassword"));
        FedoraClient fc = new FedoraClient(HTTPS+"://"+properties.getProperty("fedora22Address")+":"+properties.getProperty("fedora22SecurePort")+FEDORA_URL_PATH, properties.getProperty("fedora22UserName"), properties.getProperty("fedora22Password"));
        String pid = getFedoraPid(comp);
        AutoFinder af = new AutoFinder(fc.getAPIA());
        FieldSearchQuery query =  new FieldSearchQuery();
        Condition conds[] = new Condition[1];
        conds[0] = new  Condition(); //"pid",ComparisonOperator.eq,mapPid);
        conds[0].setProperty("pid");
        conds[0].setOperator(ComparisonOperator.eq);
        conds[0].setValue(pid);
        query.setConditions(conds);
        FieldSearchResult result = af.findObjects(RESULT_FIELDS,1,query);
        if(result.getResultList().length  >0 ) {
            modifyObject(fc,properties,cModel,file,comp,map);
        } else {
            addObject(fc,properties,cModel,file,comp,map);
        }
    }
    
    private static void addObject(FedoraClient fc,Properties p, String cModel,File file,LWComponent comp,LWMap map) throws Exception{
        String ingestFoxml =  getDigitalObjectXML(p,comp,map,cModel,file);
       BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\temp\\IngestTest.xml"));
        writer.write(ingestFoxml);
        writer.close();
        System.out.println("INGEST XML:\n"+ingestFoxml);
        StringBufferInputStream s = new StringBufferInputStream(ingestFoxml);
        AutoIngestor.ingestAndCommit(fc.getAPIA(), fc.getAPIM(), s,FORMAT, COMMENT);
    }
    private static void modifyObject(FedoraClient fc,Properties p, String cModel,File file,LWComponent comp,LWMap map) throws Exception{
        String pid = getFedoraPid(comp);
        String dsName = RESOURCE_DS;
        String mimeType =getMimeType(file,comp);
        if(cModel.equals(VUE_CM)) {
            dsName = VUE_DS;
            mimeType =VUE_MIME_TYPE;
        }
        String dcXML= getDC(comp,comp.getLabel(),pid);
        fc.getAPIM().modifyDatastreamByValue(pid,DC_DS,null,DC_LABEL,XML_MIME_TYPE,DC_URL,dcXML.getBytes(),null,null,COMMENT,true);
        // modifying rels-ext for non VUE_CM
        if(!cModel.equals(VUE_CM)){
            fc.getAPIM().modifyDatastreamByValue(pid,RELS_DS,null,RELS_LABEL,XML_MIME_TYPE,RELS_URL, getRDFDescriptionForLWComponent(comp,map).getBytes(),null,null,COMMENT,true);
        }
        if(cModel.equals(REMOTE_CM)){
            fc.getAPIM().modifyDatastreamByReference(pid, dsName,  null,comp.getLabel(), mimeType, VUE_FORMAT_URL, comp.getResource().getSpec(), null,null,  COMMENT,true);
        }else {
            dsName = file.getName();
            Uploader uploader = new Uploader(HTTPS, p.getProperty("fedora22Address"),Integer.parseInt(p.getProperty("fedora22SecurePort")),p.getProperty("fedora22UserName"), p.getProperty("fedora22Password"));
            String uploadId = uploader.upload(file);
            fc.getAPIM().modifyDatastreamByReference(pid, dsName,  null,comp.getLabel(), mimeType, VUE_FORMAT_URL, uploadId, null,null,  COMMENT,true);
            
        }
    }
    private static String getDigitalObjectXML(Properties p,LWComponent comp,LWMap map,String cModel,File file) throws Exception{
        String pid = getFedoraPid(comp);
        String label = comp.getLabel();
        StringBuffer xml = new StringBuffer();
        xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        xml.append("<foxml:digitalObject xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
        xml.append("           xmlns:foxml=\"info:fedora/fedora-system:def/foxml#\"\n");
        xml.append("           xsi:schemaLocation=\"info:fedora/fedora-system:def/foxml# http://www.fedora.info/definitions/1/0/foxml1-0.xsd\"");
        xml.append("\n           PID=\""+ pid + "\">\n");
        xml.append("  <foxml:objectProperties>\n");
        xml.append("    <foxml:property NAME=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\" VALUE=\"FedoraObject\"/>\n");
        xml.append("    <foxml:property NAME=\"info:fedora/fedora-system:def/model#label\" VALUE=\""
                + label + "\"/>\n");
        xml.append("    <foxml:property NAME=\"info:fedora/fedora-system:def/model#contentModel\" VALUE=\""
                + cModel + "\"/>\n");
        xml.append("    <foxml:property NAME=\"info:fedora/fedora-system:def/model#ownerId\" VALUE=\""+p.getProperty("fedora22UserName")+"\"/>");
        xml.append("  </foxml:objectProperties>\n");
        xml.append(getDCXML(comp));
        if(!cModel.equals(VUE_CM)) {
            xml.append( getRelsXML(comp,map));
        }
        xml.append(getOjectDSXML(p,comp,cModel,file));
        xml.append("</foxml:digitalObject>");
        String objXML = xml.toString();
        return objXML;
    }
    
    private static String getOjectDSXML(Properties p,LWComponent comp,String cModel, File file) throws Exception {
        System.out.println("Getting object XML for "+comp.getLabel()+" file: "+file);
        String r = "";
        String dateString = getDateString();
        String uploadId = new String();
        String pid = getFedoraPid(comp);
        String dsName = RESOURCE_DS;
        String mimeType = getMimeType(file,comp);
        String controlGroup = "M";
        String contentLocationType = "INTERNAL_ID";
        if(cModel.equals(VUE_CM)) {
            dsName = file.getName();
            mimeType =VUE_MIME_TYPE;
            r += getDSXML("R",MAP_DS,dateString,comp.getLabel(),"http://local.fedora.server/fedora/get/"+pid+"/"+dsName,"URL",mimeType);
        }
        if(!cModel.equals(REMOTE_CM)){
            //dsName = URLEncoder.encode(file.getName(), ENCODING);
            dsName = file.getName();
            //Uploader uploader = new Uploader(HTTPS, p.getProperty("fedora22Address"),Integer.parseInt(p.getProperty("fedora22SecurePort")),p.getProperty("fedora22UserName"), edu.tufts.vue.util.Encryption.decrypt(p.getProperty("fedora22Password")));
            Uploader uploader = new Uploader(HTTPS, p.getProperty("fedora22Address"),Integer.parseInt(p.getProperty("fedora22SecurePort")),p.getProperty("fedora22UserName"), p.getProperty("fedora22Password"));
            uploadId = uploader.upload(file);
            r+= getDSXML("R",RESOURCE_DS,dateString,comp.getLabel(), "http://local.fedora.server/fedora/get/"+pid+"/"+dsName,"URL",mimeType);
        } else {
            controlGroup = "E";
            contentLocationType = "URL";
            uploadId = comp.getResource().getSpec();
            
        }
        
        return r+ getDSXML(controlGroup,dsName,dateString,comp.getLabel(), uploadId,contentLocationType,mimeType);
    }
    
    public static String getDSXML(String controlGroup,String dsName,String dateString,String label, String uploadId,String contentLocationType, String mimeType) {
        StringBuffer xml = new StringBuffer();
        xml.append("<foxml:datastream CONTROL_GROUP=\""+controlGroup+"\" ID=\""+dsName+"\" STATE=\"A\" VERSIONABLE=\"true\">\n");
        xml.append("<foxml:datastreamVersion CREATED=\""+dateString+"\" FORMAT_URI=\"http://vue.tufts.edu/docs/vueformat/\"");
        xml.append(" ID=\""+dsName+".0\" LABEL=\""+label+"\" MIMETYPE=\""+mimeType+"\">\n");
        xml.append("<foxml:contentDigest DIGEST=\"none\" TYPE=\"DISABLED\"/>\n");
        xml.append("<foxml:contentLocation  REF=\""+uploadId+"\" TYPE=\""+contentLocationType+"\"/>\n");
        xml.append("</foxml:datastreamVersion></foxml:datastream>\n");
        return xml.toString();
    }
    
    
    
    private static String getDCXML(LWComponent comp) {
        String dateString = getDateString();
        StringBuffer xml = new StringBuffer();
        xml.append("<foxml:datastream CONTROL_GROUP=\"X\" ID=\"DC\" STATE=\"A\" VERSIONABLE=\"true\">\n");
        xml.append("<foxml:datastreamVersion CREATED=\""+dateString+"\" ID=\"DC1.0\" LABEL=\"Dublin Core Metadata\" MIMETYPE=\"text/xml\">\n");
        xml.append("<foxml:contentDigest DIGEST=\"none\" TYPE=\"DISABLED\"/>\n");
        xml.append(" <foxml:xmlContent>\n");
        xml.append(getDC(comp,comp.getLabel(),getFedoraPid(comp)));
        xml.append("</foxml:xmlContent></foxml:datastreamVersion></foxml:datastream>\n");
        return xml.toString();
    }
    
    private static String getDC(LWComponent c,String title,String identifier) {
        String dc = new String();
//      System.out.println("getDC: LWComponent: "+c+ " metadata elements: "+  c.getMetadataList().getMetadata().size());
        dc +="<oai_dc:dc xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:oai_dc=\"http://www.openarchives.org/OAI/2.0/oai_dc/\">";
        dc += "<dc:title>"+title+"</dc:title>";
        dc += "<dc:identifier>"+identifier+"</dc:identifier>";
        if(c.getNotes() != null) {
            dc += "<dc:description>"+c.getNotes()+"</dc:description>";
        }
        for(VueMetadataElement element: c.getMetadataList().getMetadata()) {
            //          System.out.println("Publishing Metadata: key:"+element.getKey()+ " value: "+ element.getValue()+"  for "+c.getLabel());
            if(element.getKey().contains(DC_URL)) {
                String key = "dc:"+element.getKey().substring(DC_URL.length()+1).toLowerCase();
                dc += "<"+key+">"+element.getValue()+"</"+key+">";
            }
        }
        dc += "</oai_dc:dc>";
        return dc;
    }
    
    private static String getRelsXML(LWComponent comp,LWMap map) throws Exception  {
        String dateString = getDateString();
        StringBuffer xml = new StringBuffer();
        xml.append("<foxml:datastream CONTROL_GROUP=\"X\" ID=\"RELS-EXT\" STATE=\"A\" VERSIONABLE=\"true\">\n");
        xml.append("<foxml:datastreamVersion CREATED=\""+dateString+"\" ID=\"RELS-EXT.0\" LABEL=\"Relationships to other objects\" MIMETYPE=\"text/xml\">\n");
        xml.append("<foxml:xmlContent>\n");
        xml.append(getRDFDescriptionForLWComponent(comp,map));
        xml.append("</foxml:xmlContent></foxml:datastreamVersion></foxml:datastream>");
        return xml.toString();
        
    }
    
    
    private static String getRDFDescriptionForLWComponent(LWComponent comp,LWMap map) {
        String rdfDescription = new String();
        rdfDescription = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:rel=\"info:fedora/fedora-system:def/relations-external#\">";
        rdfDescription += "<rdf:Description rdf:about=\"info:fedora/"+getFedoraPid(comp)+"\">";
        for(LWComponent c: map.getAllDescendents(LWComponent.ChildKind.PROPER)) {
            for(VueMetadataElement element: c.getMetadataList().getMetadata()) {
                if(DEBUG.RDF) System.out.println("METADATA: "+element.getValue()+" key:  "+element.getKey()+" LWComponent: "+comp.getLabel() );
                if(element.getKey().equals(ONT_TYPE_METADATA) && element.getValue().startsWith(FEDORA_ONTOLOGY) && c instanceof LWLink){
                    LWLink link = (LWLink)c;
                    LWComponent head = link.getHead();
                    if(getFedoraPid(head).equals(getFedoraPid(comp))) {
                        if(DEBUG.RDF) System.out.println("METADATA MATCH COMPONENT: "+element.getValue()+" key:  "+element.getKey()+" LWComponent: "+comp.getLabel() +" Link:"+link);
                        LWComponent tail = link.getTail();
                        rdfDescription +=  "<rel:"+getFedoraOntologyTerm(element.getValue())+" rdf:resource=\"info:fedora/"+getFedoraPid(tail)+"\" />\n";
                    }
                }
            }
        }
        rdfDescription +=  "<rel:IsPartOf rdf:resource=\"info:fedora/"+getFedoraPid(map)+"\" />";
        rdfDescription += "</rdf:Description>";
        rdfDescription +=      "</rdf:RDF>";
        return rdfDescription;
    }
    
    private static String getFedoraPid(LWComponent component) {
        return "vue:"+component.getURIString().substring(component.getURIString().lastIndexOf("/")+1);
    }
    
    private static String getFedoraOntologyTerm(String value) {
        String term = new String();
        term = value.substring(FEDORA_ONTOLOGY.length());
        term = term.replaceAll(" ","");
        return term;
    }
    
    private static String getDateString() {
        Date currentTime = new Date();
        return formatter.format(currentTime);
    }
    
    private static String getMimeType(File file, LWComponent comp) {
        String mimeType = "text/html";
        if(file!= null) {
            mimeType =  new MimetypesFileTypeMap().getContentType(file) ;
        } else{
            mimeType =  new MimetypesFileTypeMap().getContentType(comp.getResource().getSpec());
        }
        return mimeType;
    }

}
