package bilab;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.LinkedList;

import scigol.Debug;


import org.openscience.cdk.*;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.io.*;



//a molecule that isn't a Seq (i.e. not RNA, DNA etc.)
@Sophistication(Sophistication.Developer)
public class MoleculeImpl implements molecule, IResourceIOProvider
{
  
  public MoleculeImpl(String name)
  {
    this._name = name;
    mol = null;
    associatedResource = null;
    _annotation = new scigol.Map();
    structureKnown = false;
    
    if (!initializedStaticMolecules)
      initializeStaticMolecules(BilabPlugin.getResourceManager());

  }

  
  private static final List<String> supportedImportResourceTypes;
  private static final List<String> supportedExportResourceTypes;
  
  static {
    // list of supported resource name type (not extensions)
    supportedImportResourceTypes = new LinkedList<String>();
    supportedImportResourceTypes.add("pdb");
    supportedImportResourceTypes.add("pqs");
    supportedImportResourceTypes.add("mol");
    supportedImportResourceTypes.add("sdf");
    supportedImportResourceTypes.add("xyz");
    
    supportedExportResourceTypes = new LinkedList<String>();
  }
  
  
  public static List<String> getSupportedImportResourceTypes()
  {
    return supportedImportResourceTypes;
  }
  
  public static List<String> getSupportedExportResourceTypes()
  {
    return supportedExportResourceTypes;
  }


  /*
  @Summary("check to see if this resource is in a supported format (by name and possibly also content)")
  boolean isSupportedResourceType(String resourceName)
  {
    // we don't bother to look into the resource, we just return true if any of the types that could
    //  be represented by the resource's extension are in our supported list
    
    if (resourceName.length() < 4) return false;
    String ext = resourceName.substring(resourceName.length()-3,resourceName.length());
    
    // just compare extensions
    java.util.List<String> extTypes = BilabPlugin.getResourcesTypesWithExtension(ext);
    
    // return true if any type is one we handle
    for(String type : extTypes)
      if (supportedResourceTypes.contains(type)) return true;
      
    return false; // we handle none of the possible types for the given resource's extension 
  }
  */

  

  @Summary("create a molecule from a resource containing data in a supported format")
  public static Object importResource(ResourceManager rm, String resourceName, String resourceType)
  {
    scigol.List mols = new scigol.List();
    
    java.io.InputStreamReader streamReader = null;
    try {
      streamReader = new java.io.InputStreamReader(rm.findResourceStream(resourceName));
      if (streamReader == null)
        throw new BilabException("unable to open resource:"+resourceName);
      
      ChemObjectReader reader = null;
      
      
//      if (resourceType.equals("pdb"))
//        reader = new PDBReader(streamReader);
//      else if (resourceType.equals("mol") || resourceType.equals("sdf"))
//        reader = new MDLReader(streamReader);
//      else
        reader = new ReaderFactory().createReader(streamReader);
//Debug.WL("MoleculeImpl reader class="+reader.getClass());//!!!

      if ((reader == null) || (reader instanceof DummyReader))
        throw new BilabException("unknown molecule file format");

      ChemFile chemFile = (ChemFile)reader.read((ChemObject)new ChemFile());
      if (chemFile == null)
        throw new BilabException("unable to interpt stream as a molecule:" + resourceName);

      // dig the molecules out of the ChemFile
      for (int cs=0; cs<chemFile.getChemSequenceCount(); cs++) {
        ChemSequence chemSeq = chemFile.getChemSequence(cs);
//Debug.WL("cs="+cs);      
        for(int cm=0; cm<chemSeq.getChemModelCount(); cm++) {
          ChemModel chemModel = chemSeq.getChemModel(cm);
          SetOfMolecules molSet = chemModel.getSetOfMolecules();
//Debug.WL("cm="+cm);      
      
          for(int mi=0; mi<molSet.getMoleculeCount(); mi++) {
//Debug.WL("mi="+mi);      
            
            MoleculeImpl m = new MoleculeImpl(resourceName);
            m.mol = molSet.getMolecule(mi);
      
//Debug.WL(m.mol.getProperties().toString());//!!!
            
            m.structureKnown = true;
            m.associatedResource = rm.findResource(resourceName).toString();
            m._name = m.mol.getID();
            try {
              if (m._name == null) 
                m._name = new File(new URI(m.associatedResource)).getName(); //!!! associatedResource may contain spaces in dir path, which need to be encoded for a URL
            } catch (Exception e) {
              m._name = "<unknown>";
            }
      
            mols.add(m);
            
          }
        }
      }
    } catch (IOException e) {
      throw new BilabException("unable to locate resource to import as molecule: "+resourceName);
    } catch (CDKException e) {
      throw new BilabException("unable to import resource "+resourceName+" as molecule due to CDK error: "+e.getMessage(),e);
    }
    finally {
      try { streamReader.close(); } catch(Exception e) {}
    }

    
    if (mols.get_size() == 1)
      return (mols.get_head()).value;
    else
      return mols;
  }

  
//!!! update this to be the static method above and to return a List of molecules if approproate  
  /*
  @Sophistication(Sophistication.Advanced)
  protected void readResource(String resourceName, String resourceType)
  {
    try {
      java.io.InputStreamReader streamReader = new java.io.InputStreamReader(BilabPlugin.findResourceStream(resourceName));
      if (streamReader == null)
        throw new BilabException("unable to open resource:"+resourceName);
      
      ChemObjectReader reader = null;
      
      if (resourceType.equals("pdb"))
        reader = new PDBReader(streamReader);
      else if (resourceType.equals("mol") || resourceType.equals("sdf"))
        reader = new MDLReader(streamReader);
      else
        reader = new ReaderFactory().createReader(streamReader);

      if ((reader == null) || (reader instanceof DummyReader))
        throw new BilabException("unknown molecule file format");

      ChemFile chemFile = (ChemFile)reader.read((ChemObject)new ChemFile());
      if (chemFile == null)
        throw new BilabException("unable to interpt stream as a molecule:" + resourceName);

      // dig the molecule out of the ChemFile
      if (chemFile.getChemSequenceCount() != 1) {
        if (chemFile.getChemSequenceCount() > 1)
          Notify.userWarning(this, "file contains potentially more that one molecule, only the first will be used:" +resourceName);
        else
          throw new BilabException("no molecules found in resource: " + resourceName);
      }
      ChemSequence chemSeq = chemFile.getChemSequence(0);
      
      if (chemSeq.getChemModelCount() != 1) {
        if (chemSeq.getChemModelCount() > 1)
          Notify.userWarning(this, "file contains potentially more that one molecule, only the first will be used: "+resourceName);
        else
          throw new BilabException("no molecules found in resource: " + resourceName);
      }
      
      ChemModel chemModel = chemSeq.getChemModel(0);
      SetOfMolecules molSet = chemModel.getSetOfMolecules();
      if (molSet.getMoleculeCount() != 1) {
        if (molSet.getMoleculeCount() > 1)
          Notify.userWarning(this, "file contains more that one molecule ("+molSet.getMoleculeCount()+"), only the first will be used: "+resourceName);
        else
          throw new BilabException("no molecules found in file: "+resourceName);
      }
      
      mol = molSet.getMolecule(0);
      
      structureKnown = true;
      associatedResource = BilabPlugin.findResource(resourceName).toString();
      _name = mol.getID();
      try {
        if (_name == null) 
          _name = new File(new URI(associatedResource)).getName(); //!!! associatedResource may contain spaces in dir path, which need to be encoded for a URL
      } catch (Exception e) {
        _name = "<unknown>";
      }
      
    } catch (IOException e) {
      throw new BilabException("unable to locate resource to import as molecule: "+resourceName);
    } catch (CDKException e) {
      throw new BilabException("unable to import resource "+resourceName+" as molecule due to CDK error: "+e.getMessage(),e);
    }
    
  }
*/

  
  @Summary("create a resource containing data in a supported format from a molecule")
  public static void exportResource(ResourceManager rm, molecule m, String resourceName, String resourceType)
  {
    Debug.Unimplemented();
  }

  


  public boolean get_StructureKnown() 
  {
    return structureKnown; 
  }

  public String get_name() 
  {
    return _name; 
  }
  
  public void set_name(String value)
  {
    _name = value; 
  }



  public String get_ShortText()
  {
    return _name; 
  }


  public String get_DetailText()
  {
    return _name;
  }


  public String toString()
  {
    return _name;
  }
  
  

  public String get_AssociatedResource()
  {
    return associatedResource;
  }



  public scigol.Map get_annotations()
  {
    return _annotation; 
  }


  public scigol.Any get_annotation(String key)
  {
    return get_annotations().get_Item(key);
  }



  protected String _name;
  protected boolean structureKnown;
  protected org.openscience.cdk.Molecule mol;
  protected String associatedResource;
  protected scigol.Map _annotation;



  // helpers
  private void initializeStaticMolecules(ResourceManager rm)
  {
    initializedStaticMolecules = true;
    A = fromResource(rm, "adenine.mol", "Adenine");
    C = fromResource(rm, "cytosine.mol", "Cytosine");
    G = fromResource(rm, "guanine.mol", "Guanine");
    T = fromResource(rm, "thymine.mol", "Thymine");
    U = fromResource(rm, "uracil.mol", "Uracil");
    
    Ala = fromResource(rm, "ala.pdb", "Alanine");
    Arg = fromResource(rm, "arg.pdb", "Arginine"); 
    Asn = fromResource(rm, "asn.pdb", "Asparagine ");
    Asp = fromResource(rm, "asp.pdb", "Aspartate "); 
    Cys = fromResource(rm, "cys.pdb", "Cysteine"); 
    Gln = fromResource(rm, "gln.pdb", "Glutamine"); 
    Glu = fromResource(rm, "glu.pdb", "Glutamate"); 
    Gly = fromResource(rm, "gly.pdb", "Glycine"); 
    His = fromResource(rm, "his.pdb", "Histidine"); 
    Ile = fromResource(rm, "ile.pdb", "Isoleucine"); 
    Leu = fromResource(rm, "leu.pdb", "Leucine"); 
    Lys = fromResource(rm, "lys.pdb" ,"Lysine"); 
    Met = fromResource(rm, "met.pdb" ,"Methionine"); 
    Phe = fromResource(rm, "phe.pdb", "Phenylalanine"); 
    Pro = fromResource(rm, "pro.pdb", "Proline"); 
    Ser = fromResource(rm, "ser.pdb", "Serine"); 
    Thr = fromResource(rm, "thr.pdb", "Threonine"); 
    Trp = fromResource(rm, "trp.pdb", "Tryptophan"); 
    Tyr = fromResource(rm, "tyr.pdb", "Tyrosine"); 
    Val = fromResource(rm, "val.pdb", "Valine");  
    
    Water = fromResource(rm, "water.xyz", "Water"); 
    
  }
  
  
  private molecule fromResource(ResourceManager rm, String resName, String molName)
  {
    MoleculeImpl m = (MoleculeImpl)importResource(rm,"molecules/"+resName, "unknown");
    m.set_name(molName);
    return m;
  }

  private static boolean initializedStaticMolecules = false;
  
  
  // convenient access to common molecules
  public static molecule A,C,G,T,U,
                         Ala, Arg, Asn, Asp, Cys, Gln, Glu, Gly, His, Ile,
                         Leu, Lys, Met, Phe, Pro, Ser, Thr, Trp, Tyr, Val,
                         
                         Water;
  
 
}
