class Stage(object):
    """
    An object with tracking ability
    """
    
    def __init__(self):
        
        self.processed_by = set()
        
        
    def __str__(self):
        
        return self.long()
    
    
class Child(object):
    """
    Delegating to parent
    """
    
    def initial(self):
        
        return self.parent().initial()
        
    
    def partial(self):
        
        return self.initial().partial()
        

# Mixin classes for table formatting
class Depth0(object):
    
    def depth(self):
        
        return 0
    
class Depth1(object):
    
    def depth(self):
        
        return 1
    

class Depth2(object):
    
    def depth(self):
        
        return 2
    

class Depth3(object):
    
    def depth(self):
        
        return 3
    
    
class Depth4(object):
    
    def depth(self):
        
        return 4
    
    
class Depth5(object):
    
    def depth(self):
        
        return 5
    

class Depth6(object):
    
    def depth(self):
        
        return 6
    

# Stages in structure solution
class Initial(Stage, Depth0):
    """
    A potential partial structure for extension
    """
    
    def __init__(self, case, partial, missing, index):
        
        super( Initial, self ).__init__()
        self.case = case
        self._partial = partial
        self.missing = missing
        self.index = index
        
        
    def parent(self):
        
        return None
    
    
    def initial(self):
        
        return self
    
    
    def partial(self):
        
        return self._partial
    
    
    def score(self):
        
        return self.case.score_for[ self.partial() ]
    
        
    def short(self):
        
        return "Partial structure #%d" % self.index
    
    
    def long(self):
        
        return "%s, LLG = %.2f" % ( self.short(), self.score() )
    
    
    def entry(self, width):
        
        return ( "#%d" % self.index )[ : width ]
    
    
class TemplateHit(Stage, Child):
    """
    A homology search hit applicable for a model template
    """
    
    def __init__(self, initial, intype, index):
        
        super( TemplateHit, self ).__init__()
        self._initial = initial
        self.hss = intype
        self.index = index
        
        
    def parent(self):
        
        return self._initial
    
    
    def score(self):
        
        return ( self.initial().score(), self.hss.score(), -self.index )
        
        
    def short(self):
        
        return "%s\nPossible template #%d: %s" % (
            self.initial(),
            self.index,
            self.hss,
            )
    
    
    def long(self):
        
        return self.short()
    
    
class ModelTemplate(Stage, Child):
    """
    Applicable template for ensemble
    """
    
    def __init__(self, initial, intype, index):
        
        super( ModelTemplate, self ).__init__()
        self._initial = initial
        self.template = intype
        self.index = index
        
        
    def parent(self):
        
        return self._initial
    
    
    def score(self):
        
        return ( self.initial().score(), self.template.score(), -self.index )
        
        
    def short(self):
        
        return "%s\nModel template #%d: %s" % (
            self.initial(),
            self.index,
            self.template,
            )
    
    
    def long(self):
        
        return "%s, identity = %.0f%%" % ( self.short(), self.template.identity() * 100 )
    
    
class ModelCollection(Stage, Child):
    """
    Applicable models to make an ensemble
    """
    
    def __init__(self, initial, intype, index):
        
        super( ModelCollection, self ).__init__()
        self._initial = initial
        self.collection = intype
        self.index = index
        
        
    def parent(self):
        
        return self._initial
    
    
    def score(self):
        
        return ( self.initial().score(), self.collection.score(), -self.index )
        
        
    def short(self):
        
        return "%s\nModel collection #%d: %s" % (
            self.initial(),
            self.index,
            self.collection,
            )
    
    
    def long(self):
        
        return "%s, rmsd = %.2fA" % ( self.short(), self.collection.rms() )
    
    
class Ensemble(Stage, Child, Depth1):
    """
    Applicable ensemble
    """
    
    def __init__(self, initial, intype, index):
        
        super( Ensemble, self ).__init__()
        self._initial = initial
        self.ensemble = intype
        self.index = index
        
    
    def parent(self):
        
        return self._initial
    
    
    def score(self):
        
        return ( self.initial().score(), self.ensemble.score(), -self.index )
        
        
    def short(self):
        
        return "%s\nEnsemble #%d: %s" % (
            self.initial(),
            self.index,
            self.ensemble,
            )
    
    
    def long(self):
        
        return "%s, rmsd = %.2fA" % ( self.short(), self.ensemble.rms() )
    
    
    def entry(self, width):
        
        name = str( self.ensemble )
        return (
            name if len( name ) <= width else (
                "..." + name[ min( -width + 3, -1 ) : ]
                )
            )
    
    
class Rotation(Stage, Child, Depth2):
    """
    Rotation peak
    """
    
    def __init__(self, ensemble, peak, index):
        
        super( Rotation, self ).__init__()
        self.ensemble = ensemble
        self.peak = peak
        self.index = index
        self.translation_done = False
        
    
    def parent(self):
        
        return self.ensemble
        
        
    def score(self):
        
        return self.peak.score()
    
    
    def rfz(self):
        
        return self.peak.rfz
        
        
    def peakname(self):
        
        return "Rotation peak #%d" % self.index
    
    
    def short(self):
        
        return "%s\n%s" % ( self.ensemble, self.peakname() )
    
    
    def long(self):
        
        return "%s, RF = %.2f, RFZ = %.2f" % ( self.short(), self.peak.rf, self.peak.rfz )
    
    
    def entry(self, width):
        
        return ( "%.1f" % self.peak.rf )[ : width ]
    
    
class NormalRotation(Rotation):
    """
    Rotation peak located via rotation function
    """
    
    
class PreviouslyEstablishedRotation(Rotation):
    """
    A special rotation peak, for tracking and presentation
    """ 
    
    def peakname(self):
        
        return "Previous rotation peak #%d" % self.index
    
    
    def entry(self, width):
        
        return ( "%.1fLPR" % self.peak.rf )[ : width ]
    
    
class Translation(Stage, Child, Depth3):
    """
    Translation peak
    """
    
    def __init__(self, rotation, peak, index):
        
        super( Translation, self ).__init__()
        self.rotation = rotation
        self.peak = peak
        self.index = index
        self.packing_done = False
        
        
    def parent(self):
        
        return self.rotation
        
        
    def score(self):
        
        return self.peak.score()
    
    
    def rfz(self):
        
        return self.parent().rfz()
        
        
    def peakname(self):
        
        return "Translation peak #%d" % self.index
    
    
    def history(self):
        
        return "%s\n%s" % ( self.rotation.peakname(), self.peakname() )
    
    
    def short(self):
        
        return "%s\n%s" % ( self.rotation, self.peakname() )
    
    
    def long(self):
        
        return "%s, TF = %.2f, TFZ = %.2f" % ( self.short(), self.peak.tf, self.peak.tfz )
    
    
    def entry(self, width):
        
        return self.evaluation_entry()[ : width ]
    
    
    def evaluation_entry(self):
        
        return "%.1f" % self.peak.tfz
    
    
class Clash(Stage, Child, Depth4):
    """
    Structure failing clash test
    """
    
    def __init__(self, translation):
        
        super( Clash, self ).__init__()
        self.translation = translation
        
        
    def parent(self):
        
        return self.translation
    
    
    def rfz(self):
        
        return self.parent().rfz()
    
    
    def entry(self, width):
        
        return "No"[ : width ]
        

class Pack(Stage, Child, Depth4):
    """
    Base class for packing-type stages
    """
    
    def entry(self, width):
        
        return "Yes"[ : width ]
    
            
class Packed(Pack):
    """
    Structure passing clash test
    """
    
    def __init__(self, translation, peak):
        
        super( Packed, self ).__init__()
        self.translation = translation
        self.peak = peak
        
        
    def parent(self):
        
        return self.translation
    
    
    def tf_peak(self):
        
        return self.translation.peak
        
        
    def score(self):
        
        return self.translation.score()
    
    
    def rfz(self):
        
        return self.parent().rfz()
    
    
    def history(self):
        
        return self.translation.history()
        
        
    def short(self):
        
        return str( self.translation )
    
    
    def long(self):
        
        return "%s\nClashes: no" % self.short()
    
    
    def evaluation_entry(self):
        
        return self.translation.evaluation_entry()
    
    
class Filled(Pack):
    """
    A peak filled in that passes clash test
    """
    
    def __init__(self, ensemble, peak, index, entry_code):
        
        super( Filled, self ).__init__()
        self.ensemble = ensemble
        self.peak = peak
        self.index = index
        self.entry_code = entry_code
        
        
    def parent(self):
        
        return self.ensemble
    
    
    def tf_peak(self):
        
        return self.peak
        
        
    def score(self):
        
        return self.peak.score()
    
    
    def rfz(self):
        
        return None
    
    
    def history(self):
        
        return "Filled peak #%d" % self.index
        
        
    def short(self):
        
        return "%s\n%s" % ( self.ensemble, self.history() )
    
    
    def long(self):
        
        return "%s, TF = %.2f, TFZ = %.2f" % ( self.short(), self.peak.tf, self.peak.tfz )
    
    
    def evaluation_entry(self):
        
        return "%.1f%s" % ( self.peak.tfz, self.entry_code )


class Evaluated(Stage, Child, Depth5):
    """
    Base class for identifying final stages
    """
    
    
    def mixable_for_amalgamation(self, other):
        
        if self == other:
            return False
        
        elif self.initial() != other.initial():
            return False
        
        else:
            return self.mixable_with_stage( other = other )
        
    
class Scored(Evaluated):
    """
    Structure that went through identification procedure
    """
    
    def __init__(self, pack, significant):
        
        super( Scored, self ).__init__()
        self.pack = pack
        self._significant = significant
        
        
    def parent(self):
        
        return self.pack
    
    
    def peak(self):
        
        return self.pack.peak
    
    
    def tf_peak(self):
        
        return self.pack.tf_peak()
    
    
    def rfz(self):
        
        return self.parent().rfz()
    
    
    def score(self):
        
        return self.pack.score()
    
    
    def significant(self):
        
        return self._significant
    
    
    def is_refined(self):
        
        return False
    
    
    def history(self):
        
        return self.pack.history()
        
    
    def short(self):
        
        return str( self.pack )
        
        
    def long(self):
        
        return "%s\nSignificant: %s" % ( self.short(), self.significant() )
    
    
    def entry(self, width):
        
        return (
            "%s%s" % (
                self.pack.evaluation_entry(),
                "*" if self.significant() else "",
                )
            )[ : width ]
        
    
    def mixable_with_stage(self, other):
        
        return other.mixable_with_scored_stage( other = self )
    
    
    def mixable_with_scored_stage(self, other):
        
        return True
    
    
    def mixable_with_templated_stage(self, other):
        
        return other.template != self
    

class Templated(Evaluated):
    """
    Structures that were found by matching a model to a template (superposition)
    """
    
    def __init__(self, ensemble, template, peak, refined, llg = 0):
        
        super( Templated, self ).__init__()
        self.ensemble = ensemble
        self.template = template
        self._peak = peak
        self.refined = refined
        self.llg = llg
        
    
    def parent(self):
        
        return self.ensemble
    
    
    def rfz(self):
        
        return None
    
    
    def score(self):
        
        return self._peak.tf
    
    
    def peak(self):
        
        return self._peak
    
    
    def tf_peak(self):
        
        return self._peak
    
    
    def significant(self):
        
        return True
    
    
    def is_refined(self):
        
        return self.refined
        
        
    def short(self):
        
        return "%s\n%s" % ( self.ensemble, self.template.history() )
    
    
    def long(self):
        
        return "%s\nSuperposed: %s, TF = %.2f, TFZ = %.2f%s" % (
            self.short(),
            "refined" if self.is_refined() else "unrefined",
            self.peak().tf,
            self.peak().tfz,
            ", LLG = %.2f" % self.llg if self.refined else "",
            )
    
    
    def entry(self, width):
        
        return ( "%.1fS*" % self.peak().tfz )[ : width ]
    
    
    def mixable_with_stage(self, other):
        
        return other.mixable_with_templated_stage( other = self )
    
    
    def mixable_with_scored_stage(self, other):
        
        return self.template != other
    
    
    def mixable_with_templated_stage(self, other):
        
        return other.template != self.template
    
    
class Refined(Stage, Child, Depth6):
    """
    Refined peak
    """
    
    def __init__(self, evaluated, structure, llg):
        
        super( Refined, self ).__init__()
        self.evaluated = evaluated
        self.structure = structure
        self.llg = llg
        
        
    def parent(self):
        
        return self.evaluated
        
        
    def score(self):
        
        return self.llg
    
    
    def rfz(self):
        
        return self.parent().rfz()
        
    
    def short(self):
        
        return "%s\nRefined" % self.evaluated
    
    
    def long(self):
        
        return "%s, LLG = %.2f" % ( self.short(), self.llg )
    
    
    def entry(self, width):
        
        return  ( "%.2f" % self.llg )[ : width ]


class Suspension(object):
    """
    Suspend something until a condition gets True
    """
    
    def __init__(self, condition):
        
        self.condition = condition
        
        
    def __str__(self):
        
        return "rotation function suspension"

