from core.component.research import ResearchComponent
from core.helper import SERVICE_LAYERS

from siege import game
from siege.subsystem import Subsystem


class ResearchSystem(Subsystem):
    NAME = "research"

    def getName(self):
        return self.NAME

    def __init__(self):
        Subsystem.__init__(self)
        self.components = []
        # Mapping between material (contentId) and a tuple (recipe contentId, recipe level)
        self.materials = {}
        self.freeze()

    def create(self, entity, type, definition):
        component = ResearchComponent(definition)
        self.components.append(component)
        entity.add(component)
        component.setup()

    def add(self, entity):
        if entity.has(ResearchComponent.CID):
            self.components.append(entity.research)

    def remove(self, entity):
        try:
            del self.components[self.components.index(entity.research)]
        except (AttributeError, ValueError):
            pass

    def clear(self, entity):
        self.components = []
        self.materials = []

    def update(self, frameTime):
        pass

    def getCategories(self, entity, material):
        categories = {}
        if material in self.materials:
            component = entity.research
            if not self.materials:
                self._updateMaterialMap()
            for recipe, level in self.materials[material]:
                if recipe not in component.recipes or material not in component.recipes[recipe]:
                    categories[game.content.get(recipe).entity.craft.category.lower()] = True
        return categories

    def analyze(self, entity, category, material):
        recipes = []
        if material in self.materials:
            component = entity.research
            if not self.materials:
                self._updateMaterialMap()
            for recipe, level in self.materials[material]:
                if recipe not in component.recipes or material not in component.recipes[recipe]:
                    if game.content.get(recipe).entity.craft.category.lower() == category:
                        recipes.append((recipe, level))
            # Sort by number of revealed recipes and then level
            recipes.sort(key=lambda recipe: (10 - (0 if recipe[0] not in component.recipes else len(component.recipes[recipe[0]])), recipe[1]))
        return recipes

    def updateMaterials(self):
        self._updateMaterialMap()

    def _updateMaterialMap(self):
        for craftComponent in game.craft.getComponents():
            component = craftComponent.data()
            if not component.isResearchable:
                continue
            for material in component.materials:
                if material.content.id not in self.materials:
                    self.materials[material.content.id] = []
                self.materials[material.content.id].append((craftComponent.key(), component.level))
        for value in self.materials.values():
            value.sort(key=lambda recipe: recipe[1])

    def findNearbyResearchStation(self, player):
        #TODO: Technically this gets the first desk in the list and not the closest...
        for layer in SERVICE_LAYERS:
                entities = player.entity.realm.layers[layer].getNearby(player.entity.physics.getCenter(), 128)
                for entity in entities:
                    if entity.has('surface') and entity.surface.has('research'):
                        return entity

    def isResearchAvailable(self, player):
        services = game.craft.getAvailableServices(player.entity.realm.layers, SERVICE_LAYERS, player.entity)
        return 'research' in services

    @staticmethod
    def register():
        researchSystem = ResearchSystem()
        game.registerSubsystem(researchSystem, ResearchSystem.NAME, ResearchComponent.TYPE)
