"""
Prototype 
"""
from ..Base.WorkingLSTM import WorkingLSTM
from ..Base.Item import Item
from ..Base.Buffer import Buffer
from Narsese import Budget
from Narsese import Truth
from .Location import Location
from .Concept import Concept
from .Composition import Composition, Belief, CompositionMirror
from .Task import Task
from Model.Base.Item import Item



class Prototype(Concept):
    def __init__(self, capacity: int = 7, capacity_instances: int = None, budget_m: Budget = None, budget_c: Budget = None) -> None:
        Item.__init__(self, budget_m, budget_c)
        self.compositions = WorkingLSTM[Composition](capacity, capacity)
        self.tasks = Buffer[Task](
            capacity_instances if capacity_instances is not None else capacity*capacity)
        self.beliefs = set[Belief]()
        self.upper_links = dict[Belief, Composition]()
        self.lower_links = set[Composition]()

        self.truth_event = Truth(0.0, 0.0)
        self.truth_anticipation = Truth(0.0, 0.0)

        self.scale = 11.0

    def new_composition(self, proto: 'Prototype', location: Location):
        belief = Belief(proto)
        composition = Composition(belief, self, location)
        
        self.compositions.insert(composition)

        proto.beliefs.add(belief)
        proto.upper_links[belief] = composition

        return composition


    def decay(self, ts_now: float, duration=20):
        self.budget_m.decay(ts_now - self.budget_m.ts_update)
        self.budget_m.ts_update = ts_now
        self.budget_c.decay(ts_now - self.budget_c.ts_update)
        self.budget_c.ts_update = ts_now

        self.truth_event.decay(
            Truth.get_decay_factor(duration),
            ts_now - self.truth_event.ts_update)
        self.truth_event.ts_update = ts_now
        
        self.truth_anticipation.decay(
            Truth.get_decay_factor(duration),
            ts_now - self.truth_event.ts_update)
        self.truth_anticipation.ts_update = ts_now

        # decay beliefs
        for belief in self.beliefs:
            belief.decay(ts_now, duration)

        for task in self.tasks:
            task.decay(ts_now, duration)
            

    def move(self, dx, dy):
        if len(self.tasks) == 0:
            return None
        for instance in self.tasks:
            instance.location.move(dx, dy)
        instance = self.tasks.item_maxpriority()
        return instance.location.center

    def move_to(self, x, y):
        instance = self.tasks.item_maxpriority()
        if instance is not None:
            loc = instance.location.center
            dx, dy = x - loc[0], y - loc[1]
            self.move(dx, dy)

    def new_task(self, center: tuple[float, float], truth: Truth = None, budget_c: Budget = None):
        mirrors = [CompositionMirror(c, budget_c=Budget(p=0.1)) for c in self.compositions]
        task = Task(center, mirrors, truth, budget_c=budget_c)
        self.tasks.insert(task)
        return task


    @property
    def location(self):
        return self.tasks.item_maxpriority().location if len(self.tasks) > 0 else None

    def __len__(self):
        return len(self.compositions)
    
    def __str__(self):
        s = []
        for c in self.compositions:
            x, y = c.location.center 
            s.append(f"{str(hex(id(c.part.concept)))[-4:]}[({x:.2f},{y:.2f})]")
        return f"{{| {(', '.join(s)+' ' if len(s)>0 else '')}|}}"

    def __repr__(self) -> str:
        return f"{str(hex(id(self)))[-4:]}: {str(self)}"


if __name__ == "__main__":
    part1 = Prototype()
    part2 = Prototype()
    whole1 = Prototype()

    whole1.new_composition(part1, Location((0.1, 0)))
    whole1.new_composition(part1, Location((-0.1, 0)))
    whole1.new_composition(part2, Location((0.0, 0.2)))
    print(repr(part1))
    print(repr(part2))
    print(repr(whole1))
    print(part1.beliefs)
    print(part2.beliefs)
    print(whole1.beliefs)

    whole1.new_task((0.1, 0.1))

    print("done.")
