import os
import os.path

import logging
import toml
import simpleplugin

Inputs = {}
LoadedInput = {}
loadedPluginPrefix = ""

class Config(object):
    def __init__(self):
        self.Inputs = {}

    def load(self, parsed):
        self.Inputs = parsed.get('inputs')

def getAllInputs():
    return LoadedInput.keys()

def add(name, creator):
    Inputs[name] = creator

def loadPlugins(pluginPrefix):
    global loadedPluginPrefix
    loadedPluginPrefix = pluginPrefix
    all_inputs_this_time = []
    listPluginConfigs(pluginPrefix, lambda configfile: loadConfig(configfile, all_inputs_this_time))
    return all_inputs_this_time

def loadConfig(filePath, all_inputs):
    try:
        with open(filePath, 'r') as f:
            content = f.read()
    except IOError as e:
        logging.debug("Error reading file {}: {}".format(filePath, e))
        return

    conf, parsedDict = validate(content)
    if conf is None:
        logging.debug("Error decoding TOML: {}".format(filePath))
        return
    
    inputs = loadConfigIntoInputs(filePath, parsedDict, conf.Inputs)

    all_inputs.extend(inputs)
    return inputs

def setLoadedInputCache(configPath, name, inst):
    _, only_fileName = os.path.split(configPath)
    LoadedInput[only_fileName] = simpleplugin.LoadedInput(configPath, inst, name)

def listPluginConfigs(pluginPrefix, h1):
    try:
        for root, _, files in os.walk(pluginPrefix):
            for fileName in files:
                configPath = os.path.join(root, fileName)
                if os.path.isfile(configPath) and os.path.getsize(configPath) > 0:
                    h1(configPath)
    except IOError, e:
        raise Exception("Error walking plugin directory {}: {}".format(pluginPrefix, e))

def loadConfigIntoInputs(filePath, parsedDict, inputConfigs):
    rendered_inputs = []
    
    for name, primitives in inputConfigs.iteritems():
        inputCreator = Inputs.get(name)
        if inputCreator is None:
            logging.error("unknown input {}".format(name))
            continue

        for primitive in primitives:
            inp = inputCreator()

            inp.load(primitive)
            
            rendered_inputs.append(inp)

            setLoadedInputCache(filePath, name, inp)

    return rendered_inputs

def getInputToml(filename):
    loadedInput = LoadedInput.get(filename)
    if loadedInput is not None:
        try:
            with open(loadedInput.ConfigPath, 'r') as f:
                inputToml = f.read()
            return inputToml
        except IOError, e:
            return None

    return None

def setInputToml(inputFile, inputToml):
    conf, parsedDict = validate(inputToml)
    if not conf:
        logging.error("set_inputToml invalid toml for input {}".format(inputFile))
        return
    if inputFile in LoadedInput:
        restartInputService(conf, parsedDict, inputFile)
    else:
        loadConfigIntoInputs(inputFile, parsedDict, conf.Inputs)

    saveConfigFile(inputFile, inputToml)

    return

def deleteInput(inputFile):
    err = removeInputService(inputFile)
    deleteConfigFile(inputFile)
    return err

def hasInput(inputFile):
    return inputFile in LoadedInput

def saveConfigFile(inputFile, inputToml):
    try:
        os.stat(loadedPluginPrefix)
        
        full_path = os.path.join(loadedPluginPrefix, inputFile)

        with open(full_path, 'w') as f:
            f.write(inputToml)
        
        return True
    except IOError as e:
        logging.error("Error writing file {}: {}".format(full_path, e))
        return False
    
def deleteConfigFile(inputFile):
    try:
        os.stat(loadedPluginPrefix)
        full_path = os.path.join(loadedPluginPrefix, inputFile)
        os.remove(full_path)
        return True
    except IOError, e:
        logging.error("Error removing file {}: {}".format(full_path, e))
        return False
    except OSError, e:
        logging.error("Error finding file {}: {}".format(full_path, e))
        return False

def restartInputService(conf, parseddict, inputFile):
    loadedInput = LoadedInput.get(inputFile)
    if loadedInput is not None:
        if loadedInput.Instance is not None and loadedInput.name() in conf.Inputs:
            if isinstance(loadedInput.Instance, simpleplugin.ServiceInput):
                loadedInput.Instance.stop()
                loadedInput.Instance.load(conf.Inputs[loadedInput.name()] )
                loadedInput.Instance.restart()

    else:
        logging.error("input {} not found".format(inputFile))
        return False

    return True

def stopInputService(inputFile):
    loadedInput = LoadedInput.get(inputFile)
    if loadedInput is not None:
        if loadedInput.Instance is not None:
            if isinstance(loadedInput.Instance, simpleplugin.ServiceInput):
                loadedInput.Instance.stop()
    else:
        logging.error("input {} not found".format(inputFile))
        return False

    return True

def removeInputService(inputFile):
    loadedInput = LoadedInput.get(inputFile)
    if loadedInput is not None:
        if loadedInput.Instance is not None:
            if isinstance(loadedInput.Instance, simpleplugin.ServiceInput):
                loadedInput.Instance.stop()
        del LoadedInput[inputFile]
    else:
        logging.error("input {} not found".format(inputFile))
        return False

    return True

def validate(inputToml):
    conf = Config()
    try:
        parsed = toml.loads(inputToml)
        if not parsed.has_key("inputs") and not parsed.has_key(u"inputs") :
            return None, parsed
        conf.load(parsed)
    except Exception, e:
        logging.error("input validation error {}".format(e))
        
        return None, None

    return conf, parsed

