from __future__ import nested_scopes

import new

from compClust.mlx import datasets
from compClust.mlx import labelings
from compClust.mlx import views
from compClust.util import listOps
#import bioinformatics

#from Bio import Seq
#from Bio.Alphabet import IUPAC


def BioDataset(data):
  return datasets.ExtendedDataset(data).addMixin(BioMixin)
    
def MicroarrayDataset(data):
  return datasets.ExtendedDataset(data).addMixin(MicroarrayMixin)


class BioMixin:
  """BioMixin provides extensions to the MLX dataset for biological data

  Primarily this consists of marking some labelings as common bioinformatics
  annotation types.

  # Multidirectional Labeling
  #############################
  primaryLabeling - a unique ID for both all the genes and conditions
                    in the dataset (defaults to index of the rows / cols)

  # Feature Labelings
  ####################

  geneDescriptions - a long string describing the gene 
  geneSequences - the gene sequence if known
  unigeneIds - the unigene ID for a given gene
  """
  def setUniqueLabeling(self, label):
    """ Sets the given labeling to be the primary labeling.  This
    labeling must have a unique ID for all rows (features) and a
    unique ID for all columns (conditions) in the dataset """

    if not isinstance(label, labelings.Labeling):
      raise ValueError("Label must be subclass of compClust.mlx.labelings.Labeling")
    
    # check to make sure labels are unique for every row and for every col
    if not len(label.getLabelsByRows(range(self.getNumRows()))) == \
          len(listOps.unique(label.getLabelsByRows(range(self.getNumRows())))):
      raise ValueError('labeling is not rowname-unique')
    if not len(label.getLabelsByCols(range(self.getNumCols()))) == \
          len(listOps.unique(label.getLabelsByCols(range(self.getNumCols())))):
      raise ValueError('labeling is not colname-unique')

    gLabel = GlobalLabel.castToGlobalLabeling(label)
    self.__primaryLabeling = gLabel


  def getUniqueLabeling(self):
    """
    returns the primaryLabeling for the dataset
    Warning:  if primaryLabeling has been removed (via view.removeLabeling())
              then this function will fail.
    """
    label = None
    if "__primaryLabeling" in dir(self):
      label = self.__primaryLabeling
      # check to make sure labels are unique for every row and for every col
      if not len(label.getLabelsByRows(range(self.getNumRows()))) == \
         len(listOps.unique(label.getLabelsByRows(range(self.getNumRows())))):
        raise 'labeling is not rowname-unique', ValueError()
      if not len(label.getLabelsByCols(range(self.getNumCols()))) == \
         len(listOps.unique(label.getLabelsByCols(range(self.getNumCols())))):
        raise 'labeling is not colname-unique.', ValueError()
      
    return label

  def setAnnotationLabeling(self, label):
    """Uses the provided labeling as the 'annotation' labeling
    """
    if not isinstance(label, labelings.Labeling):
      raise ValueError("Label must be subclass of compClust.mlx.labelings.Labeling")

    gLabel = GlobalLabel.castToGlobalLabeling(label)
    self.__annotationLabeling = gLabel
    
  def getAnnotationLabeling(self):
    """Return the current annotation labeling
    """
    if "__annotationLabeling" in dir(self):
      return self.__annotationLabeling
    else:
      return None

  def setSelectionLabeling(self, label):
    """Sets labeling as the 'selection' labeling for sharing plot
    selections between different plot instances.
    """
    if not isinstance(label, labelings.Labeling):
      raise ValueError("Label must be subclass of compClust.mlx.labelings.Labeling")

    self.__selectionLabeling = GlobalLabel.castToGlobalLabeling(label)
    
  def getSelectionLabeling(self):
    """Return the current selection labeling
    """
    if "__annotationLabeling" in dir(self):
      return self.__annotationLabeling
    else:
      return None

class MicroarrayMixin:
  """
  MicroarrayMixin

  Extensions to the MLS extended dataset such to ease use with
  microarray datasets.  Primarly this consists of add setter and
  getter routines for default labelings.

  Mostly this consists of a set of default labelings:

  # Condition Labelings
  #####################

  condition - a string indicating the microarray condition.  Experimental replicates
              should be given the same condition identifier (this will be used later)
              
  xvalues - an option float representing the coordinate xaxis value
            used to plot the genevector against (defaults to the index)

  """

class CruftMixin:
  """leftover old stuff
  """
  def setter(self, lab, labelingID, setterFunctionName):
    """
    A generic setter function for labeling pointer functions below
    """
    try:
      gLab = lab.getGlobalLabeling()
    except:
      gLab = None
    if self.__module__ == \
       bioinformatics.schema.datasets.MicroarrayDataset.__module__:
      self.__dict__[labelingID] = gLab
    else:
      # this will (probably) not work with supersetted datasets
      roots = map(lambda x: x[-1], self.getLineage())
      for root in roots:
        root.setter(lab, labelingID, setterFunctionName)

    
  def getter(self, labelingID):
    """
    A generic getter function for labeling getter functions below
    Warning: in the scenario where a view has multiple root datasets,
             this function returns only the first labeling
    """
    labs = []
    roots = map(lambda x: x[-1], self.getLineage())
    for root in roots:
      gLab = root.__dict__.get(labelingID)
      if gLab:
        labs.append(labelings.GlobalWrapper(self, glabeling = gLab))
      else:
        labs.append(None)
    return labs[0]
    
  def _containsOnlyBioSeqObjects(self, lab):
    labels = lab.getLabels()
    labels = filter(None, labels)
    flag   = 1
    for label in labels:
      try:
        label.__module__ == Seq.__name__
      except:
        flag = 0

    return flag

  def _containsOnlyUnigeneIDs(self, lab):
    labels = lab.getLabels()
    labels = filter(None, labels)
    flag   = 1
    for label in labels:
      if not len(label.split('.')) == 2:
        flag = 0
        break
      [organism, ID] = label.split('.')
      try:
        # this shouldn't be possible, since organism is a string
        # of characters ('Mm' == 'Mus Musculus')
        int(organism)
        flag = 0
        break
      except:
        pass

    return flag

  def setSelectedLabeling(self, lab = None):
    self.setter(lab, '_selectedLabeling', 'setSelectedLabeling')

  def getSelectedLabeling(self, lab = None):
    return self.getter('_selectedLabeling')

  def setDisplayedLabelings(self, labs):
    labelingID = '_displayedLabelings'
    roots = map(lambda x: x[-1], self.getLineage())
    for root in roots:
      glabs = map(lambda x: x.getGlobalLabeling(), labs)
      root.__dict__[labelingID] = glabs

  def getDisplayedLabelings(self):
    labelingID = '_displayedLabelings'
    roots = map(lambda x: x[-1], self.getLineage())
    labs = []
    for root in roots:
      labs += root.__dict__.get(labelingID)
      
    return labs
    
  def setFeatureDescriptions(self, lab = None):

    """
    Sets the given labeling to be the featureDescriptions labeling.  These
    are long text descriptions of each feature on an array (often gene
    descriptions)
    """
    self.setter(lab, '_featureDescriptions', 'setFeatureDescriptions')

  def getFeatureDescriptions(self):

    """
    returns the featureDescriptions Labeling.
    """
    return self.getter('_featureDescriptions')
    
  def setFeatureSequences(self, lab = None):

    """
    Sets the given labeling to be the featueSequences labeling.  
    """

    if lab:
      # check to make sure featureSequence contains biopython seq objects
      labelingCheck = self._containsOnlyBioSeqObjects(lab)
      if not labelingCheck:
        raise ValueError()
    self.setter(lab, '_featureSequences', 'setFeatureSequences')
                
  def getFeatureSequences(self):

    """
    Returns the featureSequences labeling.  These are thought to
    be the sequences (if known ) which are targets on the slide
    """
    lab = self.getter('_featureSequences')
    
    # check to make sure featureSequence contains biopython seq objects
    if lab is not None:
      labelingCheck = self._containsOnlyBioSeqObjects(lab)
      if not labelingCheck:
        raise ValueError()

    return lab
    
    
  def setFeatureCanonicalSequences(self, lab = None):

    """
    Sets the given labeling to be the conicalSequences labeling.
    These are the sequences which targets are thought to represent
    """

    if lab:
      labelingCheck = self._containsOnlyBioSeqObjects(lab)
      if not labelingCheck:
        raise ValueError()

    self.setter(lab, '_featureCanonicalSequences', \
                'setFeatureCanonicalSequences')
        
  def getFeatureCanonicalSequences(self):

    """
    returns FeatureConicalSequences labeling
    """
    lab = self.getter('_featureCanonicalSequences')
    if lab is not None:
      labelingCheck = self._containsOnlyBioSeqObjects(lab)
      if not labelingCheck:
        raise ValueError()
      
    return lab

  
  def setUnigeneIDs(self, lab = None):

    """
    Sets given labeling to be unigeneID labeling
    """

    if lab:
      labelingCheck = self._containsOnlyUnigeneIDs(lab)
      if not labelingCheck:
        raise ValueError()
    
    self.setter(lab, '_unigeneIDs', 'setUnigeneIDs')

      
  def getUnigeneIDs(self):

    """
    returns unigeneID labeling
    """
    lab =  self.getter('_unigeneIDs')
    if lab is not None:
      labelingCheck = self._containsOnlyUnigeneIDs(lab)
      if not labelingCheck:
        raise ValueError()

    return lab
    
  def setConditions(self, lab = None):

    """
    Sets given labeling to be condition labels
    """
    self.setter(lab, '_conditions', 'setConditions')
  
  def getConditions(self):

    """
    returns conditions labeling
    """
    return self.getter('_conditions')
  
  def setXValues(self, lab = None):

    """
    Sets values to be plotted along x-axis in any plot
    """
    self.setter(lab, '_xValues', 'setXValues')

  def getXValues(self):

    """
    returns xvalue labeling
    """
    return self.getter('_xValues')



def morphView(classObj):

  """
  returns a View class which is derived from MicroarrayDataset...
  recursively builds a whold new class hiearchy.
  """
  if classObj.__bases__:
    for base in classObj.__bases__:
      baseClass = morphView(base)
      if base == datasets.Dataset:
        maBaseClass = new.classobj ('MA%s'%(classObj.__name__),
                                    classObj.__bases__,
                                    classObj.__dict__)
        bases = list(maBaseClass.__bases__)
        bases[bases.index(datasets.Dataset)] = MicroarrayDataset
        maBaseClass.__bases__ = tuple(bases)
      else:
        maBaseClass = baseClass

      
      maClassObj = new.classobj ('MA%s'%(classObj.__name__),
                                 classObj.__bases__,
                                 classObj.__dict__)
      bases = list(maClassObj.__bases__)
      bases[bases.index(base)] = maBaseClass
      maClassObj.__bases__ = tuple(bases)
  else:
    maClassObj = classObj

  print maClassObj
  return (maClassObj)

