import types
import new
import math
from zope.interface import interface, implements
from resolve import IIntResolver, IntResolver, IResolveable, IModifier, Resolveable, Resolver, Condition, Modifier
import doctest

class IModifierFactory( interface.Interface ):
    """
    Object that will generate Modifiers based on itself.
    """
    #def __init_( self, function )
    def generateModifier( type, target, condition=None, function=None ):
        """
        Generate a Modifer.
        """
        pass

class Attribute( Resolveable ):
    """
    Attributes have a base stat and can be modified, but do not generate
    modifiers.  They represent "leaves" in the modifier tree, such as Attack
    Bonus, Hit Points, etc.

    >>> attack = Attribute( "Attack Bonus", -4 )
    >>> intResolver = IntResolver( 5 )
    >>> attResolver = AttributeResolver( 5 )
    >>> intResolver.resolve( attack, sum )
    -4
    >>> print attResolver.resolve( attack, '\\n'.join )
    Attack Bonus                                                            :     -4
      Base                                                                  :     -4
    """
    def __init__( self, name, function, condition=None, modifiers=None):
        super(Attribute, self).__init__(name, function, condition, modifiers)
        self.name = name

    def add( self, modifier ):
        """Add a modifier to the list."""
        self.modifiers.append( IModifier(modifier) )

def evaluate( self, resolver ):
    return int(math.ceil((float(IIntResolver(resolver).resolve(self, sum))-10)/2))

class Stat( Attribute ):
    """
    Stats, like Attributes, can be modified, but Stats also generate modifiers.
    They are internal nodes of the modifier tree, like Strength, Dexterity,
    etc.

    >>> attack = Attribute( "Attack Bonus", -4 )
    >>> strength = Stat( "Strength", 14, evaluate )
    >>> attack.add( strength.generateModifier( 'ATTRIBUTE', attack ) )
    >>> attResolver = AttributeResolver( 5 )
    >>> intResolver = IntResolver( 5 )
    >>> intResolver.resolve( attack, sum )
    -2
    >>> print attResolver.resolve( strength, '\\n'.join )
    Strength                                                                :  +  14
      Base                                                                  :  +  14
    >>> print attResolver.resolve( attack, '\\n'.join )
    Attack Bonus                                                            :     -2
      Base                                                                  :     -4
      Modifier for Attack Bonus generated by Strength                       :  +   2
        Strength                                                            :  +  14
          Base                                                              :  +  14
    >>> otherStat = Stat( "FakeStat", 15, evaluate )
    >>> strength.add( otherStat.generateModifier( 'ATTRIBUTE', strength ) )
    >>> print attResolver.resolve( attack, '\\n'.join )
    Attack Bonus                                                            :  +   0
      Base                                                                  :     -4
      Modifier for Attack Bonus generated by Strength                       :  +   4
        Strength                                                            :  +  17
          Base                                                              :  +  14
          Modifier for Strength generated by FakeStat                       :  +   3
            FakeStat                                                        :  +  15
              Base                                                          :  +  15

    """
    implements( IModifierFactory )

    def __init__( self, name, function, modfunction=None, condition=None, modifiers=None):
        super(Stat, self).__init__(name, function, condition, modifiers)
        self.modsprovided = {}
        modfunction=modfunction or self.function
        if type(modfunction) == types.FunctionType:
            modfunction=new.instancemethod(modfunction, self, self.__class__)
        self.modfunction=modfunction

    def generateModifier( self, modtype, target, condition=None, function=None ):
        function=function or self.modfunction
        if type(function) == types.FunctionType:
            function=new.instancemethod(function, self, self.__class__)
        mod=Modifier(modtype, self, target, function, condition )
        self.modsprovided[target]=mod
        return mod

class AttributeResolver( Resolver ):
    """
    Resolves an Attribute under a particular context, outputting a text
    description of where each modifier comes from.

    >>> attack = Attribute( "Attack Bonus", -4 )
    >>> attResolver = AttributeResolver( 5 )
    >>> print attResolver.resolve( attack, '\\n'.join )
    Attack Bonus                                                            :     -4
      Base                                                                  :     -4
    """

    def __init__( self, context, cap=1, identity=0, filterfunction=None ):
        super( AttributeResolver, self ).__init__( context, (lambda self, target: target.function(self)), cap, identity, filterfunction )
        self.intResolver=IntResolver( context, cap, identity, filterfunction )

    def resolveBase( self, target, condense, level, name=None ):
        fmt="%-72s:  %s% 4s"
        if IResolveable.providedBy(target.function):
            subvalue=[self.resolve(target.function, condense, level+1)]
        else:
            subvalue=[]
        if IModifier.providedBy(target):
            subvalue = subvalue + [self.resolve(IModifier(target).parent, condense, level + 1)]
        value=self.evaluate(target)
        name=name or target.name
        base=[fmt % ("  " * level + name, {True: "+", False: " "}[value > -1], value)]
        return condense(base+subvalue)
    resolveModifier=resolveBase

    def resolveTotal( self, target, condense, level, name=None ):
        fmt="%-72s:  %s% 4s"
        value=self.intResolver.resolve(target, sum)
        name=name or target.name
        base=fmt % ("  " * level + name, {True: "+", False: " "}[value > -1], value)
        return base

    def getRequiredResults( self, results ):
        return results

class Continuum( Stat ):
    def __init__( self, magnitude, name=None, ranks=None, names=None ):
        self.ranks = ranks or Continuum.sizeRanks
        self.names = names or Continuum.sizeNames
        name = name or 'Continuum'
        if isinstance( magnitude, Continuum ):
            magnitude = magnitude.magnitude
        elif isinstance( magnitude, str ):
            magnitude = dict( [ ( val, key ) for key, val in self.sizeRanks.items( ) ] )[ magnitude ]
        super( Continuum, self ).__init__( name, magnitude, self.__int__ )

    def __add__( self, other ):
        return self.__init__( self.number + other )
    __radd__ = __add__

    def __sub__( self, other ):
        return self.__init__( self.number - other )
    __rsub__ = __sub__

    def __int__( self ):
        try:
            magnitude = super( Continuum, self ).__int__( )
            absval = abs( magnitude )
            vector = magnitude/ absval
            return ( 2 ** ( absval - 1 ) ) * vector
        except:
            return 0

    def __str__( self ):
        try:
            magnitude = super( Continuum, self ).__int__( )
            category = "%s (%s)" % ( self.ranks[ magnitude ], self.names[ magnitude ] )
        except:
            category = "No category for this size"
        return category

    #Properties
    def getLetter( self ):
        return self.ranks[ super( Continuum, self ).__int__( ) ]
    letter = property( fget=getLetter, doc="The letter of this size" )

    sizeRanks = dict( zip( range( -4, 5 ), [ 'F', 'D', 'T', 'S', 'M', 'L', 'H', 'C', 'G', ] ) )
    sizeNames = dict( zip( range( -4, 5 ), [ "Fine", "Diminuative", "Tiny", "Small", "Medium", "Large", "Huge", "Collosal", "Gargantuan" ] ) )

def foo(context, resolveable, function):
    #import pdb; pdb.set_trace()
    return context.resolve( resolveable, function )

def _test():
    import doctest
    doctest.testmod(verbose=True)
    #doctest.testmod()

if __name__ == '__main__':
    _test()
