import os, sys
import logging
import json
import weakref
import pygame
from .resourceLoaders import *



class ResourceError(Exception):
    pass
_GAME_PATH=calculate_real_path(os.path.dirname(sys.argv[0]))

def _BuildLogger():
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    log_format = logging.Formatter("[%(levelname)s : %(asctime)s] %(message)s")
    log_handler = logging.FileHandler(join_path(_GAME_PATH, "logs/gbe.log"))
    log_handler.setFormatter(log_format)
    logger.addHandler(log_handler)
    return logger
_l = _BuildLogger()

_RESOURCES={}

def define_resource_type(rtype, sub_path, loader_fn):
    global _RESOURCES, _GAME_PATH, join_path
    if rtype in _RESOURCES:
        _l.error("Resource '{}' already defined.".format(rtype))
        return
    fullpath = join_path(_GAME_PATH, sub_path)
    if not os.path.isdir(fullpath):
        _l.warning("'{}' is not a valid directory.".format(sub_path))
    if not callable(loader_fn):
        raise ResourceError("Expected a callable as the resource loader.")
    _RESOURCES[rtype]={"r":[], "loader":loader_fn, "path":fullpath}
    _l.info("Added resource type '{}' with search path '{}'.".format(rtype, sub_path))


def configure(conf):
    global _RESOURCES, join_path
    if not isinstance(conf, dict):
        raise TypeError("Expected a dictionary.")
    for key in conf:
        if key in _RESOURCES:
            fullpath = join_path(_GAME_PATH, conf[key])
            if not os.path.isdir(fullpath):
                _l.warning("'{}' is not a valid directory.".format(conf[key]))
            _RESOURCES[key]["path"] = fullpath
            _RESOURCES[key]["r"] = [] # Completely drop old list.

class ResourceManager:
    def __init__(self):
        pass

    @property
    def game_path(self):
        global _GAME_PATH
        return _GAME_PATH 

    @property
    def resource_types(self):
        global _RESOURCES
        rtypes = []
        for key in _RESOURCES:
            rtypes.append(key)
        return rtypes

    def _getResourceDict(self, rtype, src):
        global _RESOURCES
        if rtype in _RESOURCES:
            for r in _RESOURCES[rtype]["r"]:
                if r["src"] == src:
                    return r
        return None

    def is_valid(self, rtype, src):
        global _RESOURCES
        if rtype in _RESOURCES:
            return file_exists(join_path(_RESOURCES[rtype]["path"], src))
        return false

    def has(self, rtype, src):
        return (self._getResourceDict(rtype, src) is not None)

    def store(self, rtype, src):
        global _RESOURCES
        if type not in _RESOURCES:
            raise ResourceError("Unknown resource type '{}'.".format(rtype))
        if self._getResourceDict(rtype, src) == None:
            _RESOURCES[rtype]["r"].append({"src":src, "instance":None, "locked":False})
        return self

    def remove(self, rtype, src):
        global _RESOURCES
        d = self._getResourceDict(rtype, src)
        if d is None:
            raise ResourceError("No '{}' resource '{}' stored.".format(rtype, src))
        _RESOURCES[rtype]["r"].remove(d)
        return self

    def clear(self, rtype, src, ignore_lock):
        d = self._getResourceDict(rtype, src)
        if d is None:
            raise ResourceError("No '{}' resource '{}' stored.".format(rtype, src))
        if d["locked"] == False or ignore_lock == True:
            d["instance"] = None
        return self

    def get(self, rtype, src, params={}):
        global _RESOURCES
        if rtype not in _RESOURCES:
            raise ResourceError("Unknown resource type '{}'.".format(rtype))
        d = self._getResourceDict(rtype, src)
        if d is None:
            raise ResourceError("No '{}' resource '{}' stored.".format(rtype, src))
        if d["instance"] is None:
            loader = _RESOURCES[rtype]["loader"]
            filename = join_path(_RESOURCES[rtype]["path"], src)
            try:
                d["instance"] = loader(filename, params)
            except Exception as e:
                _l.error(e.message)
                return None
        return weakref.ref(d["instance"])

    def lock(self, rtype, src, lock=True):
        d = self._getResourceDict(rtype, src)
        if d is None:
            raise ResourceError("No '{}' resource '{}' stored.".format(rtype, src))
        d["locked"]=lock
        return self

    def is_locked(self, rtype, src):
        d = self._getResourceDict(rtype, src)
        if d is not None and d["src"] == src:
            return d["locked"]
        return False

    def clear_resource_type(self, rtype, ignore_lock=False):
        global _RESOURCES
        if rtype in _RESOURCES:
            for r in _RESOURCES[rtype]["r"]:
                if r["locked"] == False or ignore_lock == True:
                    r["instance"] = None
        return self

    def clear_resources(self, ignore_lock=False):
        global _RESOURCES
        for key in _RESOURCES:
            self.clear_resource_type(key, ignore_lock)
        return self

    def remove_resource_type(self, rtype):
        global _RESOURCES
        if rtype not in _RESOURCES:
            raise ResourceError("Unknown resource type '{}'.".format(rtype))
        _RESOURCES[rtype]["r"] = []
        return self

    def remove_resources(self):
        global _RESOURCES
        for key in _RESOURCES:
            _RESOURCES[key]["r"] = []
        return self


# ---------------------------------------------------------------
# Defining the built-in loaders located in resourceLoaders.py
# ---------------------------------------------------------------
define_resource_type("graphic", "graphics/", load_image)
define_resource_type("audio", "audio/", load_audio)
define_resource_type("json", "data/json/", load_JSON)