# -*- coding: utf-8 -*-
import os
import sys
import json
import socket
from time import sleep
from ...kernel.system.Directory import Directory
from ...kernel.adapter.FileAdapter import FileAdapter
from ...middleware.remote.RemoteAdm import RemoteAdm
from ...middleware.remote.AdmListener import AdmListener

class Adm(object):
    class __Adm:
        def __init__(self):
            self.val = None
            self.conf = None
            self.adapters = {}
            self.agentsTable = {}
            self.containerList = []
            
        def __str__(self):
            return repr(self) + self.val

        def start(self):
            self.startByConf('')

        def startByConf(self, conf):            
            
            if conf == '':
                self.conf = {
                    "user" : "local",
                    "ip" : "localhost",
                    "port" : 8080,
                    "remote" : None     
                }
            else:
                CONF_DIR = conf
                fa = FileAdapter({'alias':'JsonAdapter', 'type': 'JSON', 'path': CONF_DIR})
                fa.setUp()
                param = fa.request()
                self.conf = param['conf']

            if self.conf['remote']:
                remote = self.conf['remote']                
                if remote['mode'] == 'MASTER':
                    self.remoteAdm = RemoteAdm(remote['ip'], remote['port'])
                    self.remoteAdm.start()    
                else:
                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)                        
                    attempts = remote['attempts']
                    master = remote['master']
                    data = '{"command": "REGISTER", "alias":"' + remote['container_alias'] + '", "ip":"' + remote['ip'] + '", "port":"'+ str(remote['port']) +'"}'
                    for x in range(1, attempts):                        
                        try:                            
                            sock.connect((master['master_ip'], master['master_port']))                            
                            sock.sendall(bytes(data + "\n", "utf-8"))
                            received = str(sock.recv(1024), "utf-8")
                            print("Received: {}".format(received))                                                            
                            if received == 'ACK' + "\n":
                                break
                        except:
                            sleep(1) # 1 second.
                        finally:
                            sock.close()

                    admListener = AdmListener(remote['ip'], remote['port'])
                    admListener.start()
            
        def addAgent(self, agent):
            self.agentsTable[agent.id] = agent
            ip = ''
            port = ''
            if self.conf['remote']:
                remote = self.conf['remote'] 
                ip = remote['ip']
                port = remote['port']
            directory = Directory()
            directory.addAgent({'agent': agent.id, 'ip': ip, 'port' : port})
            if self.conf['remote']:
                remote = self.conf['remote']
                agents = directory.getAgents()
                dto = '{"command":"UPDATE", "agents" : ' + json.dumps(agents, ensure_ascii=False) + '}'                
                if remote['mode'] == 'MASTER':
                    containers = directory.getContainers()                    
                    for ctn in containers:
                        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                        try:                            
                            sock.connect( (ctn['ip'], int(ctn['port'])) )                            
                            sock.sendall(bytes(dto + "\n", "utf-8"))
                        except:
                            pass
                            
                        finally:
                            sock.close()
                else:
                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)                        
                    attempts = remote['attempts']
                    master = remote['master']                                              
                    try:                            
                        sock.connect((master['master_ip'], master['master_port']))                            
                        sock.sendall(bytes(dto + "\n", "utf-8"))
                    except:
                        sleep(1) # 1 second.
                    finally:
                        sock.close()
            
        def sendEvent(self, agentID, event, data):
            try:
                ag = self.agentsTable[agentID]
                if (ag):
                    ag.sendEvent(event, data)
                    return True
                else:
                    remote = self.conf['remote'] 
                    attempts = remote['attempts']
                    agents = Directory().getAgents()
                    for agent in agents:
                        if agentID == agent['agent']:
                            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                            aux = str(data)
                            aux = aux.replace("\'", "\"")                                                
                            dto = '{"command": "SENDEVENT", "alias":"' + agentID + '", "event":"' + event + '", "data":'+ aux +'}'
                            print('Send::')
                            print(dto)                               
                            for x in range(1, attempts):                        
                                try:                            
                                    sock.connect((agent['ip'], agent['port']))                            
                                    sock.sendall(bytes(dto + "\n", "utf-8"))
                                    received = str(sock.recv(1024), "utf-8")
                                    print("Received: {}".format(received))                                                            
                                    if received == 'ACK' + "\n":
                                        break
                                except:
                                    sleep(1) # 1 second.
                                finally:
                                    sock.close()
                    return True

            except Exception as inst:
                print('Controled ecxeption in Adm: ')
                print(inst)
                return False

        def addAdapter(self, id, adapter):
            self.adapters[id] = adapter

        def getAdapter(self, id):
            return self.adapters[id]

        def getConf(self):
            return self.conf

        def addContainer(self, container):
            self.containerList.append(container)

        def moveAgent(self, agentID, container):
            ag = self.agentsTable[agentID]
            ag.finalize()
            dto = ag.toDTO()
            print (dto)
            containers = Directory().getContainers()
            for ctn in containers:
                if container == ctn['alias']:
                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    try:                            
                        sock.connect( (ctn['ip'], int(ctn['port'])) )                            
                        sock.sendall(bytes(dto + "\n", "utf-8"))
                    except:
                        pass
                    finally:
                        sock.close()
                    break    
            self.agentsTable[agentID] = None
            Directory().removeAgent(agentID)

        def setEndPoint(self, agID, socket):
            ag = self.agentsTable[agID]
            ag.setSocket(socket)
        
    instance = None
    def __new__(cls):
        if not Adm.instance:
            Adm.instance = Adm.__Adm()
        return Adm.instance
    def __getattr__(self, name):
        return getattr(self.instance, name)
    def __setattr__(self, name):
        return setattr(self.instance, name)
