package ca.ucalgary.seahawk.gui;

import ca.ucalgary.seahawk.util.SeahawkOptions;
import ca.ucalgary.seahawk.services.TextClient;

import org.biomoby.shared.MobyPrimaryData;
import org.biomoby.shared.data.*;

import foxtrot.Task; // for fancy synchronous GUI handling on data paste to external apps
import foxtrot.Worker;

import java.awt.datatransfer.*;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import java.util.*;
import java.util.logging.*;

/**
 * This class handles the export of data from the Seahawk interface into other applications,
 * with special consideration for the use of drag 'n' drop in wrapping WSDL and CGI interfaces
 * using Seahawk.
 */

public class SeahawkTransferable implements Transferable{
    MobyContentPane pane;
    private String lastPastedValue;
    private String lastTransformRuleURI;

    private static TextClient textClient;
    private static Logger logger = Logger.getLogger(SeahawkTransferable.class.getName());

    public SeahawkTransferable(MobyContentPane p){
	pane = p;
    }
    
    private synchronized TextClient getTextClient() throws Exception{
	if(textClient == null){
	    textClient = new TextClient(SeahawkOptions.getRegistry());
	    //auto populate the rules
	}
	return textClient;
    }

    public Object getTransferData(DataFlavor flavor) 
	throws UnsupportedFlavorException, java.io.IOException{
	if(!isDataFlavorSupported(flavor)){
	    return null;
	}
	MobyDataInstance field = pane.getDraggedData();

	Class clazz = flavor.getRepresentationClass();
	// Data drag within the JVM, pass the object as-is
	if(clazz.getName().equals(MobyDataInstance.class.getName())){
	    //System.err.println("Drag producing "+field);
	    return field;
	}

	// Essentially, we let the user drag Moby objects or submembers rather than substrings of members' text
	// so that we can make simple rules for decomposition in programming-by-example.  OR an XSLT "DEM" rules
	// could be applied to provide a particular format e.g. DNASequence -> FastA
	String fieldString = null;
	String transformRuleURI = null;
	// If primitive, do straight toString();
	if(field instanceof MobyDataString){
	    fieldString = ((MobyDataObject) field).getValue().toString();
	    transformRuleURI = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:String2String";
	}
	else if(field instanceof MobyDataInt){
	    fieldString = ((MobyDataObject) field).getValue().toString();
	    transformRuleURI = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:Int2String";
	}
	else if(field instanceof MobyDataFloat){
	    fieldString = ((MobyDataObject) field).getValue().toString();
	    transformRuleURI = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:Float2String";
	}
	else if(field instanceof MobyDataBoolean){
	    fieldString = ((MobyDataObject) field).getValue().toString();
	    transformRuleURI = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:Boolean2String";
	}
	else if(field instanceof MobyDataDateTime){ //change this later for reformatting?
	    fieldString = ((MobyDataObject) field).getValue().toString();
	    transformRuleURI = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:DateTime2String";
	}
	else if(field instanceof MobyDataObjectVector){	
	    //todo?
	}
	else if(field instanceof MobyDataComposite){
	    try{
		// Is there a DEM rule we can use?
		boolean ONLY_IF_HAS_URN = true;
		final String[] formatChoices = getTextClient().getPossibleTextTypes((MobyPrimaryData) field, ONLY_IF_HAS_URN);
		String targetTextType = null;
		if(formatChoices != null && formatChoices.length != 0){
		    if(formatChoices.length == 1){
			targetTextType = formatChoices[0];
		    }
		    else if(pane != null){
			// We can't show a Java dialog to get the format choice, because this
			// code is being launched from the event queue thread (would cause deadlock, 
			// even using foxtrot due to JDialog GUI needing painting, which also happens on this thread).
			// Instead, the MobyContentPane will offload the selection GUI to the Web browser
			// in some way and wait synchronously for the user's choice.
			try{
			    targetTextType = (String) Worker.post(new Task(){
				    public Object run() throws Exception{
					int chosenIndex = pane.getExportOption(formatChoices); 
					if(chosenIndex < 0 || chosenIndex >= formatChoices.length){
					    return null;
					}
					return formatChoices[chosenIndex];
				    }});
			    logger.log(Level.INFO, "Chose "+formatChoices[0]+" instead of "+formatChoices[1]);
			} catch (Exception e){
			    logger.log(Level.WARNING, "Failed to get user's format choice from data paste dialog, "+
				       "using default choice "+formatChoices[0], e);
			    // fallback on failure is to take first type
			    targetTextType = formatChoices[0];  
			}
			if(targetTextType == null){
			    // user cancel
			    return null;
			}
		    }
		    else{
			logger.log(Level.INFO, 
				   "No content pane to coordinate paste format choice, choosing default of " + 
				   formatChoices[0]);
			targetTextType = formatChoices[0];
		    }
		}
		fieldString = getTextClient().getText(field, targetTextType);
		transformRuleURI = getTextClient().getTemplateURN(targetTextType);
	    } catch(Exception e){
		pane.setStatus("Could not transform Moby object to text: " + e.getMessage());
		logger.log(Level.SEVERE, "Could not transform Moby object to text: ", e);
	    }
	}
	else{
	    // Must be just a base MobyDataObject
	    fieldString = ((MobyDataObject) field).getId();
	    transformRuleURI = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:ID2String";
	}

	//System.err.println("\""+fieldString+"\": Requested transfer class was " + clazz.getName() + 
	//		   ", MIME type was " + flavor.getPrimaryType() + "/" + flavor.getSubType());
	lastPastedValue = fieldString;
	lastTransformRuleURI = transformRuleURI;
	return (new StringSelection(fieldString)).getTransferData(flavor);
    }

    public DataFlavor[] getTransferDataFlavors(){
	Vector<DataFlavor> flavors = new Vector<DataFlavor>();

	for(DataFlavor stringFlavor: (new StringSelection("boo")).getTransferDataFlavors()){
	    flavors.add(stringFlavor);
	}
	DataFlavor mobyJavaObjectFlavor = new DataFlavor(MobyDataInstance.class, MobyDataInstance.class.getName());
	flavors.add(mobyJavaObjectFlavor);
	// We can create a file or text out of just about anything
	//if(flavor.equals(DataFlavor.javaFileListFlavor) ||
	//   flavor.isFlavorTextType()){
	//   return true;
	//}
	// TODO: We may be able to transfer an image, if an image is in the selected Seahawk data...
	return flavors.toArray(new DataFlavor[flavors.size()]);	
    }

    public boolean isDataFlavorSupported(DataFlavor flavor){
	//System.err.println("Asked if flavor is supported:" + flavor.getPrimaryType() + "/" + flavor.getSubType() 
	//		   + "with representation " + flavor.getRepresentationClass());
	for(DataFlavor supportedFlavor: getTransferDataFlavors()){
	    if(supportedFlavor.equals(flavor)){
		return true;
	    }
	}
	return false;
    }

    // Called after the external paste/drop is complete
    public void exportDone(){
	//let the PBE system record that the particular value is being copied
	pane.exportDone(lastPastedValue, lastTransformRuleURI);
	//System.err.println("Export complete");
    }
}
