import os
import time
from uuid import uuid4
import uuid
from flask import jsonify
import json

from marshmallow import EXCLUDE, ValidationError
#from vrpmdvcreatemoncmd import VRPMDV_CreateMonCmd
#from vrpmdvcreatemonschema import VRPMDV_CreateMonSchema
#from vrpmdvdeletemoncmd import VRPMDV_DeleteMonCmd
#from vrpmdvdeletemonschema import VRPMDV_DeleteMonSchema
from vrpmdvmonfilescompleteschema import VRPMDV_MonfilesCompleteSchema
from vrpmdvmonfilter import VRPMDV_MonFilter
from vrpmdvmonfilterschema import VRPMDV_MonFilterSchema
from vrpmdvmonsensorschema import VRPMDV_MonSensorSchema
from vrpmdvmonchannelschema import VRPMDV_MonChannelSchema
from vrpmdvmonfilesschema import VRPMDV_MonfilesSchema
from vrpmdvmonitoring import VRPMDV_Monitoring 
from vrpmdvmonitoringschema import VRPMDV_MonitoringSchema
from vrpmdvmonreqschema import VRPMDV_MonReqSchema
# from vrpmdvntlink import trySend

from extensions.rt_service import rt_service as rts
from vrpmdvmonitoringState import VRPMDVMonitoringState
#from vrpmdvlogging import createCoproLoggingTask 
import logging


class VRPMDV_Data:   

    def __init__(self):
        #self.loaded = False
        format = "%(asctime)s: %(message)s"
        logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
        self.mons:list = []
        #TODO ML: uncomment this lines
        self.rtservice = rts.RT_Service()
        if (self.rtservice != None): 
            logging.info("init: initCoproFW")
            self.rtservice.initCoproFW("home/root/elffile","zephyr_openamp_rsc_table.elf")
        self.logTask = None #createCoproLoggingTask() 
        self.loaded = self.loadFile()
        self.monfilespath = '/home/root/monfiles/'
        #start the monitorings
        time.sleep(1)
        self.startMons();
        
        

    def loadFile(self):
        try: 
            with open('./mons.json', 'r') as f:
                fmons = f.read()

                if not fmons:
                    # return no Item
                    return True
                else:
                    # data = json.loads(fmons)
                    schema = VRPMDV_MonitoringSchema(many=True)
                    #res = schema.loads(fmons) # data
                    self.mons = schema.loads(fmons) # .append(res)
                    return True
        except:
            #nothing todo ML we should create a file later the database
            print("file not found")

    def saveFile(self):
        try: 
            with open('./mons.json', 'w') as f:
                schema = VRPMDV_MonitoringSchema()
                result = schema.dumps(self.mons, many=True) 
                f.write(result)
                return True
        except:
            #nothing todo
            print("could not write file")

    def startMons(self):
        try:
            logging.info("MainThread: startMons")                                                                    
            # for i, val in enumerate(self.mons):
            #    if (val.status == 'started') :                   
            #        val.startMonitoring()
        except:
            print("could not start monitorings") 
            


    def findMon(mon):
        return mon

    def getMonitorings(self):
        # 1st time read file
        # if not self.loaded :
        #     self.loaded = self.loadFile()
        
        schema = VRPMDV_MonitoringSchema()
        result = schema.dumps(self.mons, many=True)
        return result

    def getMonitoring(self, vrpmid):
        try:
            matched_obj = next(x for x in self.mons if str(x.id) == vrpmid)
        except:
            return "no Item found"
        
        # we find it and we return it
        schema = VRPMDV_MonitoringSchema()
        return schema.dumps(matched_obj)



    def setMonitoring(self, id, request):
        try:
            matched_obj = next(x for x in self.mons if str(x.id) == id)
        except:
            return "no Item found"
        
        if 'name' in request : matched_obj.name = request['name']
        if 'samplerate' in request : matched_obj.samplerate = request['samplerate']
        if 'sampleperiod' in request : matched_obj.sampleperiod = request['sampleperiod']
        if 'downtime' in request : matched_obj.downtime = request['downtime']
        if 'status' in request : 
            if (request['status'] == 'started'):
                if (matched_obj.status == VRPMDVMonitoringState.STOPPED):
                    logging.info("MainThread: started %s", matched_obj.id)
                    matched_obj.startMonitoring()                                                        
                # matched_obj.setStatus(self.createMonOnDevice(matched_obj))
            elif (matched_obj.status == 'started') :
                logging.info("MainThread: stopped %s", matched_obj.id)                                                        
                # matched_obj.setStatus(self._stopMonitoring(matched_obj))
                matched_obj.stopMonitoring()
        self.saveFile()

        # we find it and we return it
        schema = VRPMDV_MonitoringSchema()
        return schema.dumps(matched_obj)


    def createMonitoring(self, request):
        id = uuid.uuid4()
        createjson = request.get_json()
        vrpmreq = VRPMDV_MonReqSchema().load(createjson)       
        
        mon = VRPMDV_Monitoring(0, id, vrpmreq.name, vrpmreq.samplerate, vrpmreq.sampleperiod, vrpmreq.downtime, vrpmreq.status, vrpmreq.owner)
        logging.info("Monitoring %s: creating", str(id))

        # create Monitoring on the realtime side 
        if (vrpmreq.status == 'started'): 
            logging.info("Monitoring %s: createMonOnDevice", str(id))
            mon.startMonitoring()            
            # status = self.createMonOnDevice(mon)
            # logging.info("Monitoring %s: _start executed. status:%s", str(id), str(status))            
            # mon.status = status

        self.mons.append(mon)
        #save to file
        self.saveFile()
        #create result Object in json
        schema = VRPMDV_MonitoringSchema()
        return schema.dumps(mon) 


    def deleteMonitoring(self, vrpmid):
        # if not self.loaded :
        #     self.loaded = self.loadFile()

        # find monitoring with uuid
        #result = filter(lambda mon: str(mon.uuid) == vrpmid["uuid"], self.mons)
        try:
            matched_obj = next(x for x in self.mons if str(x.id) == vrpmid)
        except:
            return "no Item found"
        

        # we find it, delete on realtime side and now remove from list
        matched_obj.stopMonitoring()
        self.mons.remove(matched_obj)
        #save the list
        self.saveFile()
        #make result
        schema = VRPMDV_MonitoringSchema()
        return schema.dump(matched_obj)


# internal helper function

    # ML: transfer to Monitoring class
    # def createMonOnDevice(self, monitoring):
    #     #call the start API of the c++ driver
    #     try:
        
    #         logging.info("MainThread: createMonOnDevice => before vrpmdCreateCmd samplerate:%d, samplerate:%d, samplerate:%d", monitoring.samplerate, monitoring.sampleperiod, monitoring.downtime)                        
    #         vrpmdCreateCmd = VRPMDV_CreateMonCmd(monitoring.samplerate, monitoring.sampleperiod, monitoring.downtime)
    #         logging.info("MainThread: createMonOnDevice => before schemaCreateCmd")                                    
    #         schemaCreateCmd = VRPMDV_CreateMonSchema() 
    #         logging.info("MainThread: try send start monitoring starting %s", schemaCreateCmd.dumps(vrpmdCreateCmd))                        
    #         #res = trySend(schemaCreateCmd.dumps(vrpmdCreateCmd))
    #         #logging.info("MainThread: try send start monitoring done %s", res)                                    
    #         self.status = "started"
    #         # set status if we have a success TODO
    #     except:
    #         self.status = "stopped"
    #         return "stopped"
    #     return "started"


    # def _stopMonitoring(self, monitoring):
    #     #call the stop API of the c++ driver
    #     try:
    #         logging.info("MainThread: stop monitoring")
    #         vrpmdDeleteCmd = VRPMDV_DeleteMonCmd(2, monitoring.id)
    #         schemaDeleteCmd = VRPMDV_DeleteMonSchema() 
            
    #         #trySend(schemaDeleteCmd.dumps(vrpmdDeleteCmd))
    #         self.status = "stopped"
    #         logging.info("MainThread: stop monitoring done")            

    #     except:
    #         self.status = "started"
    #         return "started"

    #     return monitoring.status


# old impl ML
#
    # def createMonitoring(self, request):
    #     id = uuid.uuid4()

    #     createjson = request.get_json()
    #     vrpmreq = VRPMDV_MonReqSchema().load(createjson)
    #     vrpmdCreateCmd = VRPMDV_CreateMonCmd(1, id , vrpmreq.samplerate, vrpmreq.sampleperiod, vrpmreq.downtime, vrpmreq.status)
    #     schemaCreateCmd = VRPMDV_CreateMonSchema() 

    #     valstr = schemaCreateCmd.dumps(vrpmdCreateCmd)
    #     trySend(schemaCreateCmd.dumps(vrpmdCreateCmd))
        
    #     mon = VRPMDV_Monitoring(id, vrpmreq.name, vrpmreq.samplerate, vrpmreq.sampleperiod, vrpmreq.downtime, vrpmreq.status, vrpmreq.owner)
    #     logging.info("Monitoring %s: starting", str(id))
    #     mon.startMonitoring()

    #     self.mons.append(mon)
    #     #save to file
    #     self.saveFile()
    #     #create result Object in json
    #     schema = VRPMDV_MonitoringSchema()
    #     return schema.dumps(mon) 




    # def createMonitoring(self, request):
    #     id = uuid.uuid4()

    #     createjson = request.get_json()
    #     vrpmreq = VRPMDV_MonReqSchema().load(createjson)
    #     vrpmdCreateCmd = VRPMDV_CreateMonCmd(1, id , vrpmreq.samplerate, vrpmreq.sampleperiod, vrpmreq.downtime, vrpmreq.status)
    #     schemaCreateCmd = VRPMDV_CreateMonSchema() 

    #     valstr = schemaCreateCmd.dumps(vrpmdCreateCmd)
    #     trySend(schemaCreateCmd.dumps(vrpmdCreateCmd))
        
    #     mon = VRPMDV_Monitoring(id, vrpmreq.name, vrpmreq.samplerate, vrpmreq.sampleperiod, vrpmreq.downtime, vrpmreq.status, vrpmreq.owner)
    #     logging.info("Monitoring %s: starting", str(id))
    #     mon.startMonitoring()

    #     self.mons.append(mon)
    #     #save to file
    #     self.saveFile()
    #     #create result Object in json
    #     schema = VRPMDV_MonitoringSchema()
    #     return schema.dumps(mon) 


    # def deleteMonitoring(self, vrpmid):
    #     # if not self.loaded :
    #     #     self.loaded = self.loadFile()

    #     # find monitoring with uuid
    #     #result = filter(lambda mon: str(mon.uuid) == vrpmid["uuid"], self.mons)
    #     try:
    #         matched_obj = next(x for x in self.mons if str(x.id) == vrpmid)
    #     except:
    #         return "no Item found"
        

    #     # we find it, delete on realtime side and now remove from list
    #     #matched_obj.deleteMonitoring()
    #     self.mons.remove(matched_obj)
    #     #save the list
    #     self.saveFile()
    #     #make result
    #     schema = VRPMDV_MonitoringSchema()
    #     return schema.dump(matched_obj)


    # def startMonitoring(self, vrpmid):
    #     # if not self.loaded :
    #     #    self.loaded = self.loadFile()

    #     #call the start API of the c++ driver
    #     try:
    #         matched_obj = next(x for x in self.mons if str(x.id) == vrpmid)

    #     except:
    #         return "no Item found"

    #     if matched_obj.startMonitoring() :
    #         #TODO ML return the state
    #         return "started"

    #     return "created"

    # def stopMonitoring(self, vrpmid):
    #     # if not self.loaded :
    #     #    self.loaded = self.loadFile()

    #     #call the stop API of the c++ driver
    #     try:
    #         matched_obj = next(x for x in self.mons if str(x.id) == vrpmid)
    #         logging.info("MainThread: stop monitoring starting")
    #         vrpmdDeleteCmd = VRPMDV_DeleteMonCmd(2, matched_obj.id)
    #         schemaDeleteCmd = VRPMDV_DeleteMonSchema() 
    #         valstr = schemaDeleteCmd.dumps(vrpmdDeleteCmd)
    #         trySend(schemaDeleteCmd.dumps(vrpmdDeleteCmd))
    #         logging.info("MainThread: stop monitoring done")            

    #     except:
    #         return "no Item found"

    #     matched_obj.stopMonitoring()
    #     return matched_obj.monstate.name


    def setStatus(self, id, vrpmStatus):
        if 'status' in vrpmStatus :
            # matched_obj = self.findMonitoring(id)
            #call the start API of the c++ driver
            if (vrpmStatus['status'] == 'started'):
                return self.startMonitoring(id)
            else :
                return self.stopMonitoring(id)

        #     return matched_obj.status
        
        # return ""

    def findMonitoring(self, id):
        try:
            return next(x for x in self.mons if str(x.id) == id)
        except:
            return None #"no Item found"
    
    def getMonitoring2files(self):
        #search in the given directory
        dirs = [d for d in os.listdir(self.monfilespath) if os.path.isdir(d)]
        #return for the filter


    def getMonitoringfiles(self, id, params):
        #search in the given directory
        monId = params.get('monId')
        logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")            
        logging.info("getMonitoringfiles: monId:%s", monId)                        

        if (monId == None):
            return None
   
        complete = False
        
        mons = self.getMonofMonfiles(monId)
        logging.info("getMonitoringfiles: mons.length:%d", len(mons))                                
        if (len(mons) == 0) :
            return None
        else :
            #TODO ML: only one monitoring
            monId = mons[0].id

        #TODO ML change to array
        #for file in files:
        filename = self.monfilespath + str(monId) + '/'
        monfiles = self.loadMonfiles(filename, complete)
        schema = VRPMDV_MonfilesSchema()
        result = schema.dumps(monfiles, many=True)
        return result

    def loadMonfiles(self, id, complete):
        #files = [f for f in os.listdir() if os.path.isfile(f)]
        logging.info("loadMonfiles: monId:%s", id)                                
        completeMonPath = os.path.dirname(id)
        monfiles = []                   
        for f in os.listdir(completeMonPath) :
            # if (os.path.isfile(f)) :
            try:
                completeFilepath = id + str(f)    
                with open(completeFilepath, 'r') as f:
                    logging.info("loadMonfiles: before fmonfile:%s", completeFilepath)                    
                    fmonfile = f.read()
                    logging.info("loadMonfiles: after fmonfile:%s", completeFilepath)
                    if fmonfile:
                        # if (complete):
                        #     schema = VRPMDV_MonfilesCompleteSchema()                            
                        # else :
                        logging.info("loadMonfiles: before schemaload") 
                        if (complete):
                            schema = VRPMDV_MonfilesCompleteSchema()
                        else:
                            schema = VRPMDV_MonfilesSchema()
                        try:                               
                            monfile = schema.loads(fmonfile, unknown=EXCLUDE)
                        except ValidationError as err:
                            logging.info("SchemaError: %s",err.messages)  # => {"email": ['"foo" is not a valid email address.']}
                            logging.info("Data: %s",err.valid_data)  # => {"name": "John"}                            
                        monfiles.append(monfile)
            except:
                #nothing todo ML we should create a file later the database
                print("file not found")
                logging.info("loadMonfiles: file not found:%s", completeFilepath)
        return monfiles


    def getMonsDFAvalable(self):
        monfilters = []
        mons = self.getMonofMonfiles(0)
        if (len(mons) == 0):
            return None
        else:
            schema = VRPMDV_MonFilterSchema()
            for mon in mons:           
                monfilter = VRPMDV_MonFilter(str(mon.id), mon.name)
                monfilters.append(monfilter)                
            return schema.dumps(monfilters, many=True)

    def getMonofMonfiles(self, monId) :
        dirname = os.path.dirname(self.monfilespath)
        dirs = []
        for d in os.listdir(dirname) : 
            path_file = self.monfilespath + d #os.sep.join([self.monfilespath, d])
            if os.path.isdir(path_file) :              
                dirs.append(d)
        mons = []
        for dir in dirs:
            mon = self.findMonitoring(dir)
            if ((mon != None) and ((monId == 0 ) or (monId == str(mon.id)))):
                mons.append(mon)
                # monId = mon.id 
            # else:
            #     return None
        
        return mons


    def getMFDownload(self, params):
        #search in the given directory
        monId = params.get('monId')
        if (monId == None):
            return None

        fileId = params.get('fileid')
        mons = self.getMonofMonfiles(monId)

        #TODO ML change to array
        #for file in files:
        filename = self.monfilespath + str(monId) + '/'
        monfiles = self.loadMonfiles(filename, True)
        schema = VRPMDV_MonfilesCompleteSchema()
        result = schema.dumps(monfiles, many=True)
        return result


 
# This is to get the directory that the program 
# is currently running in.
# dir_path = os.path.dirname(os.path.realpath(__file__))
 
# for root, dirs, files in os.walk(dir_path):
#     for file in files: 
 
#         # change the extension from '.mp3' to 
#         # the one of your choice.
#         if file.endswith('.mp3'):
#             print (root+'/'+str(file))
