import os
import sys
import time
import logging
import datetime

import simpleplugin.outputs as outputs
import simpleplugin.inputs as inputs
import simpleplugin.inputs.all
from simpleplugin import Metric
from accumulator import newAccumulator


import simpleplugin.inputs as inputs

class Agent(object):
    def __init__(self, configPrefix):
        self.inputs = []
        self.outputs = []
        self.dst = None
        self.configPrefix = configPrefix

    @classmethod
    def newAgent(cls, configPrefix):        
        outps = outputs.loadPlugins(configPrefix)
        inps = inputs.loadPlugins(configPrefix)
        
        agent = cls(configPrefix=configPrefix)
        agent.inputs = inps
        agent.outputs = outps
        return agent

    def start(self):
        err = self.initPlugins()
        if err:
            return err

        src, _, err = self.startOutputs()
        if err:
            return err

        self.startInput()

        for m in src:
            for output in self.outputs:
                output.write([m])

        return None

    def startEx(self, dst):
        self.dst = dst
        err = self.initPlugins()
        if err:
            return err

        self.startInput()

        return None

    def initPlugins(self):
        for output in self.outputs:
            if isinstance(output, simpleplugin.Initializer):
                err = output.init()
                if err:
                    return err

        for inp in self.inputs:
            if isinstance(inp, simpleplugin.Initializer):
                err = inp.init()
                if err:
                    return err

        return None

    def startOutput(self):
        src = []
        for output in self.outputs:
            err = self.connectOutput(output)
            if err:
                return None, None, err

        return src, src, None

    def connectOutput(self, output):
        try:
            output.connect()
            return None
        except Exception as e:
            return e

    def startInput(self):
        #print('startInput step -1 ', self.inputs)
        for inp in self.inputs:
            #print('startInput step -2 ', inp)
            acc = newAccumulator(datetime.timedelta(milliseconds=1), self.dst)
            #print('startInput step -3 ', isinstance(inp, simpleplugin.ServiceInput))
            if isinstance(inp, simpleplugin.ServiceInput):
                #print('startInput step -4 ')
            
                err = inp.start(acc)
                if err:
                    logging.error("Starting input %s: %s", inp.Name(), err)
            else:
                #print('startInput step -5 ')
                self.createInputLoop(acc, inp)

    def createInputLoop(self, acc, inp):
        #print('createInputLoop ', acc, inp)
        def input_loop():
            #print('createInputLoop step -1')
            last_exec_time = 0
            while True:
                
                now = int(time.time()*1000 )
                #print('createInputLoop step -2 ', now - last_exec_time > inp.interval)
                if now - last_exec_time > inp.interval:
                    inp.gather(acc)
                    last_exec_time = now

                time.sleep(inp.interval / 1000.0 / 10.0 )

        import threading
        t = threading.Thread(target=input_loop)
        t.daemon = True
        t.start()

    def getInputToml(self, inputfile):
        return inputs.getInputToml(inputfile)

    def getAllInput(self):
        return inputs.getAllInputs()

    def setInputToml(self, inputfile, inputToml):
        if self.dst is None:
            raise ValueError("output error call startEx first")

        if inputs.hasInput(inputfile):
            return inputs.setInputToml(inputfile, inputToml)
        else:
            conf, _ = inputs.validate(inputToml)

            if not conf:
                raise Exception("plugin validation error: {}".format(inputfile))

            issucceded = inputs.saveConfigFile(inputfile, inputToml)
            if not issucceded:
                raise Exception("plugin save error: {}".format(inputfile))

            inputFullPath = os.path.join(self.configPrefix, inputfile)
            loadedInputs = []
            inputs.loadConfig(inputFullPath, loadedInputs)
            
            for loadedInput in loadedInputs:
                if isinstance(loadedInput, simpleplugin.Initializer):
                    loadedInput.init()
                    
                acc = newAccumulator(datetime.timedelta(milliseconds=1), self.dst)
                if isinstance(loadedInput, simpleplugin.ServiceInput):
                    loadedInput.start(acc)
                else:
                    self.createInputLoop(acc, loadedInput)

                self.inputs.append(loadedInput)

        return

    def deleteInput(self, inputfile):
        if self.dst is None:
            raise ValueError("output error call startEx first")

        if inputs.hasInput(inputfile):
            return inputs.deleteInput(inputfile)
        else:
            raise ValueError("input {} not found".format(inputfile))
