package org.biomoby.registry.meta;

import org.biomoby.client.CentralImpl;
import org.biomoby.shared.Central;
import org.biomoby.shared.MobyException;

import java.io.*;
import java.net.*;

/**
 * A class for storing MOBY RDFs and Central calls to disk for reuse in other JVMs.
 *
 * @author Paul Gordon
 */

public class RegistryCache{
    public static final String REG_CACHE_FILE_PREFIX = "mobyCtrRDF";
    public static final String CALL_CACHE_FILE_PREFIX = "mobyCtrCall";

    private static Registry defaultRegistry = null;
    private static File tempDir = new File(System.getProperty("java.io.tmpdir"));

    /**
     * Returns where a resource should be cached locally, whether it current exists or not.
     *
     * @param resourceName one of the resources listed in org.biomoby.shared.Central
     */
    public static File getOntologyFile(Registry reg, String resourceName){
	File fileLocation = null;
	if(Central.DATA_TYPES_RESOURCE_NAME.equals(resourceName)){
	    return calcDataTypeOntologyFile(reg);
	}
	else if(Central.SERVICE_TYPES_RESOURCE_NAME.equals(resourceName)){
	    return calcServiceTypeOntologyFile(reg);
	}
	else if(Central.NAMESPACES_RESOURCE_NAME.equals(resourceName)){
	    return calcNamespaceOntologyFile(reg);
	}
	else if(Central.SERVICE_INSTANCES_RESOURCE_NAME.equals(resourceName)){
	    return calcServiceOntologyFile(reg);
	}
	else{
	    return null;
	}

    }

    /**
     * Stores the given resource to a file for retrieval later with getOntologyFile().
     *
     * @param resourceName one of the resources listed in org.biomoby.shared.Central
     *
     * @return the file where the resource has been cached
     */
    public static File cacheRegistryOntology(Registry reg, String resourceName) throws Exception{
	return cacheRegistryOntology(reg, resourceName, -1);
    }

    /**
     * Stores the given resource to a file for retrieval later with getOntologyFile().
     *
     * @param resourceName one of the resources listed in org.biomoby.shared.Central
     * @param allowedAgeMillis if the hached file exists and is less than this many milliseconds old, a new version will not be downloaded, if zero or negative, overwrite is forced.
     * @return the file where the resource has been cached
     */
    public static File cacheRegistryOntology(Registry reg, String resourceName, long allowedAgeMillis) throws Exception{
	if(resourceName == null){
	    throw new NullPointerException("The resource to be cached was null");
	}

	if(reg == null){
	    reg = getDefaultRegistry();
	}
	if(reg == null){
	    throw new MobyException("No registry, even the default one, could be found.  Ontology caching aborted"); // We can't even get a default registry: abandon ship!
	}

	File cacheFile = null;
	if(Central.DATA_TYPES_RESOURCE_NAME.equals(resourceName)){
	    cacheFile = calcDataTypeOntologyFile(reg);
	}
	else if(Central.SERVICE_TYPES_RESOURCE_NAME.equals(resourceName)){
	    cacheFile = calcServiceTypeOntologyFile(reg);
	}
	else if(Central.NAMESPACES_RESOURCE_NAME.equals(resourceName)){
	    cacheFile = calcNamespaceOntologyFile(reg);
	}
	else if(Central.SERVICE_INSTANCES_RESOURCE_NAME.equals(resourceName)){
	    cacheFile = calcServiceOntologyFile(reg);
	}
	else{
	    throw new MobyException("The resource asked to be cached ("+resourceName+
				    ") was not recognized.");
	}
	URL sourceURL = Registry.findResourceURL(reg, resourceName);

	long currentTime = System.currentTimeMillis();

	if(!cacheFile.exists() || 
	   allowedAgeMillis <= 0 ||
	   currentTime-cacheFile.lastModified() > allowedAgeMillis){
	    // Copy from URL to file, line-by-line
	    FileWriter cacheFileWriter = new FileWriter(cacheFile);
	    LineNumberReader r = new LineNumberReader(new InputStreamReader(sourceURL.openStream()));
	    for(String line = r.readLine(); line != null; line = r.readLine()){
		cacheFileWriter.write(line+"\n");
	    }
	    
	    cacheFileWriter.close();
	}

	return cacheFile;
    }

    public synchronized static Registry getDefaultRegistry(){
	if(defaultRegistry == null){
	    try{
		defaultRegistry = new Registry(Registries.DEFAULT_REGISTRY_SYNONYM, 
					       CentralImpl.getDefaultURL(),
					       CentralImpl.getDefaultURI());
	    } catch(Exception e){
		e.printStackTrace();
	    }
	}
	return defaultRegistry;
    }

    public static File calcDataTypeOntologyFile(Registry reg){
	String regName = reg == null ? Registries.DEFAULT_REGISTRY_SYNONYM : reg.getSynonym();
	String userName = System.getProperty("user.name") == null ? "" : "." + System.getProperty("user.name");
	return new File(tempDir, 
			REG_CACHE_FILE_PREFIX+userName+"."+regName+".DataTypes.rdf");
    }

    public static File getServiceTypeOntologyFile(Registry reg){
	File fileLocation = calcServiceTypeOntologyFile(reg);
	if(fileLocation.exists()){
	    return fileLocation;
	}
	else{
	    return null;
	}
    }

    public static File calcServiceTypeOntologyFile(Registry reg){
	String regName = reg == null ? Registries.DEFAULT_REGISTRY_SYNONYM : reg.getSynonym();
	String userName = System.getProperty("user.name") == null ? "" : "." + System.getProperty("user.name");
	return new File(tempDir, 
			REG_CACHE_FILE_PREFIX+userName+"."+regName+".ServiceTypes.rdf");
    }

    public static File getNamespaceOntologyFile(Registry reg){
	File fileLocation = calcNamespaceOntologyFile(reg);
	if(fileLocation.exists()){
	    return fileLocation;
	}
	else{
	    return null;
	}
    }

    public static File calcNamespaceOntologyFile(Registry reg){
	String regName = reg == null ? Registries.DEFAULT_REGISTRY_SYNONYM : reg.getSynonym();
	String userName = System.getProperty("user.name") == null ? "" : "." + System.getProperty("user.name");
	return new File(tempDir, 
			REG_CACHE_FILE_PREFIX+userName+"."+regName+".Namespaces.rdf");
    }

    public static File getServiceOntologyFile(Registry reg){
	File fileLocation = calcServiceOntologyFile(reg);
	if(fileLocation.exists()){
	    return fileLocation;
	}
	else{
	    return null;
	}
    }

    public static File calcServiceOntologyFile(Registry reg){
	String regName = reg == null ? Registries.DEFAULT_REGISTRY_SYNONYM : reg.getSynonym();
	String userName = System.getProperty("user.name") == null ? "" : "." + System.getProperty("user.name");
	return new File(tempDir, 
			REG_CACHE_FILE_PREFIX+userName+"."+regName+".Services.rdf");
    }

    /**
     * Reports where the message returned by the call is 
     * stored on disk.
     * 
     * @param callKey a unique ID for the Moby Central call (i.e. the contents sent to CentralImpl.doCall())
     *
     * @return the File object for the cached file, or null if it does not exist 
     */
    public static File getCentralCallFile(String centralEndpoint, String callKey){
	File fileLocation = calcCentralCallFile(centralEndpoint, callKey);
	if(fileLocation.exists()){
	    return fileLocation;
	}
	else{
	    return null;
	}
    }

    /**
     * Reports where the message returned by the call should be 
     * stored on disk.
     * 
     * @param callKey a unique ID for the Moby Central call (i.e. the contents sent to CentralImpl.doCall())
     *
     * @return the File object for the cache file
     */
    public static File calcCentralCallFile(String centralEndpoint, String callKey){
	String userName = System.getProperty("user.name") == null ? "" : "." + System.getProperty("user.name");
	return new File(tempDir, 
			CALL_CACHE_FILE_PREFIX+userName+"."+centralEndpoint.hashCode()+"."+callKey.hashCode());
    }

    /**
     * @param dir directory where files should be stored, if null, the working directory
     */
    public static void setTempDir(File dir){
	tempDir = dir;
    }

    /**
     * @param allowedAgeMillis files older than this are deleted, or if negative, all are deleted, or if zero, deleted at the end of the session
     */
    public static void deleteExpiredCacheFiles(long allowedAgeMillis){
	String userName = System.getProperty("user.name") == null ? "" : "." + System.getProperty("user.name");
	String relativeRegFilePrefix = REG_CACHE_FILE_PREFIX+userName;
	String relativeCallFilePrefix = CALL_CACHE_FILE_PREFIX+userName;

	long currentTime = System.currentTimeMillis();

	for(File tempFile: tempDir.listFiles()){
	    // See if it belongs to us, based on the name
	    if(tempFile.getName().startsWith(relativeRegFilePrefix) ||
	       tempFile.getName().startsWith(relativeCallFilePrefix)){
		// 0 means "for this session only"
		if(allowedAgeMillis == 0){
		    tempFile.deleteOnExit();
		}
		else if(allowedAgeMillis < 0 ||
			currentTime-tempFile.lastModified() > allowedAgeMillis){
		    tempFile.delete();
		}
	    }
	}
    }

    public static void deleteAllCacheFiles(){
	deleteExpiredCacheFiles(-1);  // negative time == delete all 
    }
}
