package ca.ucalgary.seahawk.util;

import ca.ucalgary.seahawk.gui.MobyContentGUI;
import ca.ucalgary.seahawk.gui.MobyServicesGUI;
import ca.ucalgary.seahawk.services.MobyClient;

import org.biomoby.registry.meta.Registry;
import org.biomoby.shared.data.*;
import org.biomoby.shared.MobyDataType;
import org.biomoby.shared.MobyNamespace;

import java.awt.datatransfer.Transferable;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Vector;
import javax.swing.JLabel;

/**
 * Contains utility methods for Moby Object creation from unstructured data, and for
 * creating the Seahawk GUI. 
 */

public class MobyUtils{
    public static final int MAX_OBJ_NAME_LEN = 18;

    private static MobyContentGUI mobyContentGUI = null;
    private static MobyServicesGUI mobyServicesGUI = null;

    public static Transferable createTransferable(MobyContentInstance contents){
	return null;
    }
    
    /**
     * The purpose of this method is to create a MOBY document to encapsulate
     * a binary data object stored at the given URL.  If the data does
     * not represent a single binary MOBY object, the method returns null.
     * If the provided client or URL is null, null will be returned too.
     */
    public static MobyContentInstance convertURLtoMobyBinaryData(MobyClient client, URL url)throws Exception{
	if(url == null || client == null){
	    return null;
	}

	ByteArrayOutputStream bytes = new ByteArrayOutputStream();
	InputStream is = url.openStream();

	// Slurp up the file as bytes
	byte[] buffer = new byte[4096];
	for(int bytesRead = is.read(buffer); bytesRead != -1; bytesRead = is.read(buffer)){
	    bytes.write(buffer, 0, bytesRead);
	}

	// Find MOBY Objects in it
	MobyDataObject[] foundObjects = client.getMobyObjects(bytes.toByteArray(), 
							      MobyDataType.getDataType(MobyDataBytes.BASE64_DATATYPE, 
                                                                                       SeahawkOptions.getRegistry()));
	// Did we unambiguously find one binary object?
	if(foundObjects != null && foundObjects.length == 1){
	    MobyContentInstance mci = new MobyContentInstance();
	    String[] dataName = url.getPath().split("/");
	    MobyDataJob job = new MobyDataJob();
	    job.put(dataName[dataName.length-1], foundObjects[0]);
	    System.err.println("Namespace of found object was " + foundObjects[0].getPrimaryNamespace().getName());
	    mci.put(dataName[dataName.length-1], job); // name the data by the last part of the URL path
	    return mci;
	}
	return null;
    }

    public static MobyDataInstance[] convertStringToObjects(String data){
	if(data == null){
	    return new MobyDataInstance[0];
	}
	
	Vector<MobyDataInstance> objects = new Vector<MobyDataInstance>();
	String objectName = data;

	// Truncate name if necessary
	if(objectName.length() > MAX_OBJ_NAME_LEN+3){
	    objectName = objectName.substring(0, MAX_OBJ_NAME_LEN)+"...";
	}
	
	// Can always use the data as a string
	MobyDataString strObject = new MobyDataString(objectName, data, SeahawkOptions.getRegistry());
	strObject.setPrimaryNamespace(new MobyNamespace("seahawk"));
	objects.add(strObject);

	// See if it's sequence
	MobyDataComposite mdc = createMobySequence(data, "user-selection");
	if(mdc != null){
	    mdc.setName(objectName);
	    objects.add(mdc);
	}
	// If it's a single word, give keyword options
	else{
	    if(data.trim().matches("^[A-Za-z0-9_-]{3,35}$")){
		objects.add(new MobyDataComposite("Global_Keyword",
						  "dummy_name", 
						  "unknown", 
						  data.trim(),
						  SeahawkOptions.getRegistry()));
	    }
	}


	return (MobyDataInstance[]) objects.toArray(new MobyDataInstance[objects.size()]);
    }

    public static void clearGUIs(){
	mobyServicesGUI = null;
	mobyContentGUI = null;
    }

    /**
     * The first time this method is called within a JVM instance, the GUI is created.
     * In subsequent calls, the same object is returned.
     */
    public static MobyServicesGUI getMobyServicesGUI(JLabel status){
	if(mobyServicesGUI != null){
	    return mobyServicesGUI;
	}

	// PG: Adding MOBY here since it needs the BluejayDObjectList, to resolve XPath prefices
        try{
	    mobyServicesGUI = new MobyServicesGUI();
	    org.biomoby.client.MobyRequestEventHandler handler = null;
	    if(mobyContentGUI == null){
		mobyContentGUI = new MobyContentGUI(mobyServicesGUI);
	    }
	    if (status == null) {
		status = mobyContentGUI.getStatusComponent();
	    }
	    handler = mobyContentGUI;
	    mobyServicesGUI.setResponseHandler(handler);
	}
	catch(Exception e){
	    System.out.println("Caught exception while trying to initiate MOBY client: " + e);
            e.printStackTrace();
	    return null;
	}
	return mobyServicesGUI;
    }

    public static MobyContentGUI getMobyContentGUI(JLabel status){
	if(mobyContentGUI != null){
	    return mobyContentGUI;
	}
	// Need services to have contents...
	getMobyServicesGUI(status);
	return mobyContentGUI;
    }

    public static void destroyMobyGUI(){
	mobyContentGUI = null;
	mobyServicesGUI = null;
    }

    public static MobyDataComposite createMobySequence(String sequenceData, String id){
	MobyDataComposite sequence = null;
	if(Sequence.isDNA(sequenceData)){
	    sequence = new MobyDataComposite("DNASequence", 
					     "dummy_name", 
					     "seahawk", 
					     id,
					     SeahawkOptions.getRegistry());
	}
	else if(Sequence.isRNA(sequenceData)){
	    sequence = new MobyDataComposite("RNASequence", 
					     "dummy_name", 
					     "seahawk", 
					     id,
					     SeahawkOptions.getRegistry());
	}
	else if(Sequence.isProtein(sequenceData)){
	    sequence = new MobyDataComposite("AminoAcidSequence", 
					     "dummy_name", 
					     "seahawk", 
					     id,
					     SeahawkOptions.getRegistry());
	}
	// Not a recognized type of sequence
	else{
	    return null;
	}

	sequenceData = sequenceData.replaceAll("[ \t\r\n0-9]", "");  // strip whitespace and possible milestone digits
	sequence.put("SequenceString", new MobyDataString(sequenceData, SeahawkOptions.getRegistry()));
	sequence.put("Length", new MobyDataInt(sequenceData.length(), SeahawkOptions.getRegistry()));

	return sequence;
    }

}
