package ca.ucalgary.services;

import ca.ucalgary.seahawk.services.MobyClient;
import ca.ucalgary.seahawk.services.TextClient;

import org.biomoby.service.MobyServlet;
import org.biomoby.shared.*;
import org.biomoby.shared.data.*; 

import java.io.*;
import java.net.URL;
import java.util.*;

/**
 * A base servlet for any servlet wanting to transform from Moby
 * datatypers into legacy (semi-structured) data formats.
 */
public class LegacyService extends MobyServlet{
    public final static String TEXT_RULES_LOCATION_PARAM = "mobRulesLoc";
    public final static String MOBY_RULES_LOCATION_PARAM = "demRulesLoc";
    public final static String TEXT_RULES_DEFAULT_RESOURCE = "ca/ucalgary/services/resources/acdRules.xml";
    public final static String MOBY_RULES_DEFAULT_RESOURCE = "ca/ucalgary/services/resources/mobyRules.xsl";

    // For converting unstructured dats to MOBY data
    private MobyClient mobyClient;
    // For converting MOBY data to plain-text representation
    private TextClient textClient;
    // Initialized after createServiceFromConfig()
    protected MobyDataType binaryDataType;

    protected MobyDataInstance getMobyData(String legacyData, 
					   MobyPrimaryData mobyDataTemplate)
	throws Exception{	
	try{
	    return mobyClient.getMobyObject(legacyData, mobyDataTemplate);
	} catch(MobyServiceException mse){
	    //getMobyObject() throws untransformed-data warnings
	    addException(mse);
	}

	return null;
    }
    
    protected MobyDataInstance getMobyData(Map<String,byte[]> legacyDataParts, 
					   MobyPrimaryData mobyDataTemplate)
	throws Exception{	
	try{
	    return mobyClient.getMobyObject(legacyDataParts, mobyDataTemplate);
	} catch(MobyServiceException mse){
	    //getMobyObject() throws untransformed-data warnings
	    addException(mse);
	}

	return null;
    }

    protected MobyDataInstance getMobyData(org.w3c.dom.Node legacyDOMNode, 
					   MobyPrimaryData mobyDataTemplate)
	throws Exception{	
	try{
	    return mobyClient.getMobyObject(legacyDOMNode, mobyDataTemplate);
	} catch(MobyServiceException mse){
	    //getMobyObject() throws untransformed-data warnings
	    addException(mse);
	}

	return null;
    }

    protected byte[] getLegacyData(MobyDataInstance mobyData, String legacyFormatName)
	throws Exception{
	// binary data should be passed through as-is 
	if(mobyData instanceof MobyDataBytes){	
	    return ((MobyDataBytes) mobyData).getBytes();
	}
	String text = textClient.getText(mobyData, legacyFormatName);
	return text == null ? null : text.getBytes(); //TODO: should we check the encoding?
    }
    
    /**
     * Mechanism to incorporate new tranformation rules
     */
    protected String addLoweringMappingsFromURL(URL u) throws Exception{
	return textClient.addMappingsFromURL(u);
    }

    protected void addLiftingMappingsFromURL(URL u) throws Exception{
	mobyClient.addMappingsFromURL(u);
    }

    protected boolean canProduceDataTypeFromString(MobyDataType dataType){
	return mobyClient.canProduceDataTypeFromString(dataType);
    }

    protected boolean canProduceTextTypeFromMoby(String legacyFormatName, 
						 MobyPrimaryData mobyPrimaryInput){
	return textClient.canProduceTextTypeFromMoby(legacyFormatName, mobyPrimaryInput);
    }

    protected MobyClient getMobyClient(){
	return mobyClient;
    }

    public MobyService createServiceFromConfig(javax.servlet.http.HttpServletRequest request)
	throws Exception{
	MobyService service = super.createServiceFromConfig(request);

	URL regexRulesURL = null;
	if(getCoCInitParameter(TEXT_RULES_LOCATION_PARAM) != null){
	    String rulesLocationName = getCoCInitParameter(TEXT_RULES_LOCATION_PARAM);
	    if(rulesLocationName.length() == 0){
		throw new Exception("Parameter " + TEXT_RULES_LOCATION_PARAM + 
				    " was blank in the servlet configuration (please " +
				    "either comment it out, or fill in a value)");
	    }
	    
	    // Is it a URL or a file location?
	    try{
		regexRulesURL = new URL(rulesLocationName);
	    }
	    catch(Exception e){
		// Not a properly formatted URL
		File rulesFile = new File(rulesLocationName);
		if(rulesFile.exists()){
		    if(!rulesFile.isFile()){
			throw new Exception("The rules file inferred from the servlet " +
					    "configuration (" + rulesFile.getPath() + 
					    ") exists, but is not a file, as expected");
		    }
		    regexRulesURL = rulesFile.toURI().toURL();
		}
	    }
	    // Last ditch, try to get it as a resource
	    if(regexRulesURL == null){
		regexRulesURL = getClass().getClassLoader().getResource(rulesLocationName);
	    }

	    if(regexRulesURL == null){
		log("Could not find the specified data mapping rules (" + rulesLocationName +
                    ") as a file, URL or resource, falling back on the default " +
		    "mapping file included with the servlet");
            }
	}
	// Either no file was specified, or the specified one was not found
	if(regexRulesURL == null){
	    regexRulesURL = getClass().getClassLoader().getResource(TEXT_RULES_DEFAULT_RESOURCE);
	}
	if(regexRulesURL == null){
	    throw new Exception("The data mapping rules location " + TEXT_RULES_DEFAULT_RESOURCE + 
				") could not be resolved to an existing Java resource");
	}

	URL xsltRulesURL = null;
	if(getCoCInitParameter(MOBY_RULES_LOCATION_PARAM) != null){
	    String rulesLocationName = getCoCInitParameter(MOBY_RULES_LOCATION_PARAM);
	    if(rulesLocationName.length() == 0){
		throw new Exception("Parameter " + MOBY_RULES_LOCATION_PARAM + 
				    " was blank in the servlet configuration (please " +
				    "either comment it out, or fill in a value)");
	    }
	    
	    // Is it a URL or a file location?
	    try{
		xsltRulesURL = new URL(rulesLocationName);
	    }
	    catch(Exception e){
		// Not a properly formatted URL
		File rulesFile = new File(rulesLocationName);
		if(rulesFile.exists()){
		    if(!rulesFile.isFile()){
			throw new Exception("The XSLT rules file inferred from the servlet " +
					    "configuration (" + rulesFile.getPath() + 
					    ") exists, but is not a file, as expected");
		    }
		    xsltRulesURL = rulesFile.toURI().toURL();
		}
	    }
	    // Last ditch, try to get it as a resource
	    if(xsltRulesURL == null){
		xsltRulesURL = getClass().getClassLoader().getResource(rulesLocationName);
	    }

	    if(xsltRulesURL == null){
		log("Could not find the specified XSLT mapping rules (" + rulesLocationName +
                    ") as a file, URL or resource, falling back on the default " +
		    "mapping file included with the servlet");
            }
	}
	// Either no file was specified, or the specified one was not found
	if(xsltRulesURL == null){
	    xsltRulesURL = getClass().getClassLoader().getResource(MOBY_RULES_DEFAULT_RESOURCE);
	}
	if(xsltRulesURL == null){
	    throw new Exception("The XSLT mapping rules location " + MOBY_RULES_DEFAULT_RESOURCE + 
				") could not be resolved to an existing Java resource");
	}

	// Instantiate the string -> MOBY data mapping engine
	// Load up the rules we can use for data mapping
	// Could fail from bad XML, non-existent or unreachable URL, etc.
	System.setProperty(MobyClient.RESOURCE_SYSTEM_PROPERTY, regexRulesURL.toString());
	mobyClient = new MobyClient(registry);

	// Instantiate the MOBY data -> string mapping engine
	textClient = new TextClient(registry);
	textClient.addMappingsFromURL(xsltRulesURL);

	binaryDataType = MobyDataType.getDataType(MobyDataBytes.BASE64_DATATYPE, registry);

	return service;
    }
}
