if not hasattr(__builtins__, 'frozenset'):
  from sets import ImmutableSet as frozenset
  
import types

from matplotlib.lines import lineMarkers

from compClust.iplot.mappers.Mapper import Mapper, scaleList
from compClust.iplot.views import IPlotView

DEFAULT_MARKER_SIZE = 20.0

class MarkersMapper(Mapper):

  """
  This is a support class for the IPlotView class which descrbes
  how the element markers and connecting lines should be plotted.

  """

  def getMarkerColors(self):
    """
    """

    return(None)

  def getPlotMarkers(self):
    """
    getPlotMarkers

    returns a list of markers to be added to the plot...  These are
    overriden by the weights/styles """

    return(None)

  def getMarkerSizes(self):
    """
    getMarkerSize

    this function returns the desired marker size.  These are
    overriden by the weights/styes.  """

    return ( DEFAULT_MARKER_SIZE )
  
class RowMarkersMapper(MarkersMapper):

  symbols = {"plus": '+', "square": 's',  "cross": '+', "triangle": '^',  "circle": 'o'}
  def __init__(self, plotView):
    super(MarkersMapper, self).__init__(plotView)
    self.__maxSize = 10
    self.__minSize = 1
    self.__sizes   = 5.0
    self.__markers = '+'

  def setPlotMarkers(self, marker='o'):
    if lineMarkers.has_key(marker):
      self.__markers = [marker]
    elif RowMarkersMapper.symbols.has_key(marker):
      self.__markers = [symbols[marker]]
    else:
      print "unrecognized marker"
      
  def setMaxSize(self, max):
    self.__maxSize = max
    if type(self.__sizes) in [types.ListType, types.TupleType, nx.ArrayType]:
      self.__sizes = scaleList(self.__sizes, minReturn=self.__minSize, maxReturn = self.__maxSize)
    else:
      if self.__sizes > self.__maxSize:
        self.__sizes = self.__maxSize
    
  def setMinSize(self, min):
    self.__minSize = min
    if type(self.__sizes) in [types.ListType, types.TupleType, nx.ArrayType]:
      self.__sizes = scaleList(self.__sizes, minReturn=self.__minSize, maxReturn = self.__maxSize)
    else:
      if self.__sizes < self.__minSize:
        self.__sizes = self.__minSize
    
  def getMarkerSizes(self):
    """
    getMarkerSize

    this function returns the desired marker size.  These are
    overriden by the weights/styes.  """

    return(self.__sizes)
  
  def setMarkerSizeByLabeling(self, labeling, minValue=None, maxValue=None):
    self.__sizes =  scaleList(labeling.getLabelByRows(), minValue, maxValue, self.__minSize, self.__maxSize)
    
  def setMarkerSizeByColValue(self, col, minValue=None, maxValue=None):
    data = self.dataset.getColData(col)
    if maxValue is None:
      maxValue = nx.mlab.max(data)
    if minValue is None:
      minValue = nx.mlab.min(data)
    self.__sizes= scaleList(data, minValue, maxValue, self.__minSize, self.__maxSize)

  def setMarkerSizeByIndex(self, minValue=None, maxValue=None):
    self.__sizes = scaleList(range(self.dataset.getNumRows()), minValue, maxValue, self.__minSize, self.__maxSize)

  def setMarkerSizeByFunction(self,function, minValue=None, maxValue=None):
    """
    sets the maker size by the given function...  the function prototype should be:
    value = function(dataset, row)
    """
    data = map(lambda row: function(self.dataset, row), range(self.dataset.getNumRows()))
    self.__sizes = scaleList(data, minValue, maxValue, self.__minSize, self.__maxSize)

  def invertMarkerSizes(self):

    if type(self.__sizes) in [types.ListType, nx.ArrayType, types.TupleType]:
      numRows = len(self.__sizes)
      max = nx.mlab.max(self.__sizes)
      min = nx.mlab.min(self.__sizes)
      self.__sizes = map(operator.sub, [max]*numRows, self.__sizes)

  def setUniformMarkerSize(self, size= .04):
    self.__sizes = size

  def clearSizes(self):
    self.__sizes = None
  
  def getPlotMarkers(self):
    """
    getPlotMarkers

    returns a list of markers to be added to the plot...  These are
    overriden by the weights/styles """

    return(self.__markers)

  def setPlotMarkersByLabeling(self, labeling, labels=None):

    """ sets the plot markers according to labeling.  If optional
    labels are supplied the marker shapes set only for the specified
    labels.  Note that there are only 8 unique marker styles, so if
    more than 8 labels are listed (or contained inside of labeling)
    symbols will cycle.
    """

    numSymbols = len(RowMarkersMapper.symbols)
    numRows = self.dataset.getNumRows()

    if labels is None:
      labels = frozenset(labeling.getLabelByRows())

    if len(labels) > numSymbols:
      symbols = RowMarkersMapper.symbols*int(nx.ceil(len(labels)/numSymbols))
    else:
      symbols = RowMarkersMapper.symbols
    
    labelToSymbol = {}
    map(labelToSymbol.setdefault, labels, symbols[:len(labels)])

    self.__markers = map(labelToSymbol.get, labeling.getLabelByRows(), [RowMarkersMapper.symbols[-1]]*numRows)
    return(labelToSymbol)

  def setUniformPlotMarker(self, marker='circle'):

    """
    sets the marker symbol to be the same for all points.

     are the availble shapes
     'plus', 'square',  'diamond',  'cross', 'splus', 'scross', 'triangle',  'circle' and 'none'
    """
    self.__markers = marker
