import os

import numpy as np

from marlben import core
from marlben.lib import material


class Map:
    '''Map object representing a list of tiles

   Also tracks a sparse list of tile updates
   '''

    def __init__(self, config, realm):
        self.config = config

        self.tiles = np.zeros((config.MAP_HEIGHT + 2*config.TERRAIN_BORDER, config.MAP_WIDTH + 2*config.TERRAIN_BORDER),
                              dtype=object)

        for r in range(config.MAP_HEIGHT + 2*config.TERRAIN_BORDER):
            for c in range(config.MAP_WIDTH + 2*config.TERRAIN_BORDER):
                self.tiles[r, c] = core.Tile(config, realm, r, c)

    @property
    def packet(self):
        '''Packet of degenerate resource states'''
        missingResources = []
        for e in self.updateList:
            missingResources.append(e.pos)
        return missingResources

    @property
    def repr(self):
        '''Flat matrix of tile material indices'''
        return [[t.mat.index for t in row] for row in self.tiles]

    def reset(self, realm, idx):
        '''Reuse the current tile objects to load a new map'''
        config = self.config
        self.updateList = set()

        path_map_suffix = config.PATH_MAP_SUFFIX.format(idx)
        fPath = os.path.join(
            config.PATH_CWD, config.PATH_MAPS, path_map_suffix)

        try:
            map_file = np.load(fPath)
        except FileNotFoundError:
            print('Maps not found')
            raise

        materials = {mat.index: mat for mat in material.All}
        for r, row in enumerate(map_file):
            for c, (idx, vc, ac) in enumerate(row):
                mat = materials[idx]
                tile = self.tiles[r, c]
                tile.reset(mat, vc, ac, config)

    def step(self):
        '''Evaluate updatable tiles'''
        for e in self.updateList.copy():
            if e.static:
                self.updateList.remove(e)
            e.step()

    def harvest(self, r, c):
        '''Called by actions that harvest a resource tile'''
        self.updateList.add(self.tiles[r, c])
        return self.tiles[r, c].harvest()
