# Without DBM database 

# tcp socket connection
import socket
import sys
import os
from array import array
import requests
import binascii

# from thread import *
import threading
import time
import schedule
import datetime
from datetime import datetime
# from dateutil import tz
from logging import NullHandler
import json
from textwrap import wrap
import configparser
import yaml
import traceback

import numpy as np

# mini database library

sockettoimei={}

global objectdata
objectdata={}
with open("/home/ubuntu/magi/kteltrfid/config.yml", "r") as ymlfile:
    cfg = yaml.load(ymlfile, Loader=yaml.FullLoader)
    cfg['IOelement']={239: ['ignition', 0, 1], 9: ['analog_input1', 0, 65535], 181: ['gnss_pdop', 0, 500], 240: ['movement', 0, 1], 80: ['data_mode', 0, 5], 21: ['gsm_signal', 0, 5], 200: ['sleep_mode', 0, 2], 69: ['gnss_status', 0, 500], 182: ['gnss_hdop', 0, 500], 66: ['external_voltage', 0, 65535], 24: ['speed', 0, 350], 205: ['gsm_cell_id', 0, 65535], 206: ['gsm_area_code', 0, 65535], 67: ['battery_voltage', 0, 65535], 68: ['battery_current', 0, 65535], 241: ['active_gsm_operator', 0, 4294967295], 199: ['trip_odometer', 0, 4294967295], 16: ['total_odometer', 0, 4294967295], 1: ['digital_input1', 0, 1], 179: ['digital_input1', 0, 1], 12: ['fuel_used_gps', 0, 4294967295], 13: ['fuel_rate_gps', 0, 3267], 17: ['asis_x', -8000, 8000], 18: ['asis_y', -8000, 8000], 19: ['asis_z', -8000, 8000], 11: ['iccid', 0, ''], 10: ['sd_status', 0, 1], 2: ['digital_input2', 0, 1], 3: ['digital_input3', 0, 1], 6: ['analog_input2', 0, 65535], 180: ['digital_output2', 0, 1], 72: ['dallas_temp', -550, 1150], 73: ['dallas_temp', -550, 1150], 74: ['dallas_temp', -550, 1150], 75: ['dallas_temp', -550, 1150], 76: ['dallas_temp_id1', 0, ''], 77: ['dallas_temp_id2', 0, ''], 79: ['dallas_temp_id3', 0, ''], 71: ['dallas_temp_id4', 0, ''], 78: ['ibutton', 0, ''], 207: ['rfid', 0, ''], 201: ['lls_fuel1', 0, 65535], 202: ['lls_temp1', -128, 127], 203: ['lls_fuel2', 0, 65535], 204: ['lls_temp2', -128, 127], 210: ['lls_fuel3', 0, 65535], 211: ['lls_temp3', -128, 127], 212: ['lls_fuel4', 0, 65535], 213: ['lls_temp4', -128, 127], 214: ['lls_fuel5', 0, 65535], 215: ['lls_temp5', -128, 127], 15: ['eco_score', 0, 65535], 238: ['user_id', 0, ''], 25: ['ble_temp1', -400, 1250], 26: ['ble_temp2', -400, 1250], 27: ['ble_temp3', -400, 1250], 28: ['ble_temp4', -400, 1250], 29: ['ble_battery_voltage1', 0, 100], 20: ['ble_battery_voltage2', 0, 100], 22: ['ble_battery_voltage3', 0, 100], 23: ['ble_battery_voltage4', 0, 100], 86: ['ble_humidity1', 0, 1000], 104: ['ble_humidity2', 0, 1000], 106: ['ble_humidity3', 0, 1000], 108: ['ble_humidity4', 0, 1000], 8: ['authorized_ibutton', 0, ''], 4: ['pulse_counter_din1', 0, 4294967295], 5: ['pulse_counter_din2', 0, 4294967295], 7: ['records_in_flash', 0, 65535], 306: ['ble_fuel_freq1', 0, 2147483647], 307: ['ble_fuel_freq2', 0, 2147483647], 308: ['ble_fuel_freq3', 0, 2147483647], 309: ['ble_fuel_freq4', 0, 2147483647]}
    print(cfg)
load_conf=cfg["load_configuration"]
load_log=cfg["log"]
IOelement=cfg["IOelement"]
objectkvants=cfg["objectFuel_kvants"]

Wrongfile = 'Wrong'
Errorfile = 'Error'
Logfile = 'Log'
if not os.path.exists(Wrongfile):
    os.mkdir(Wrongfile)
if not os.path.exists(Errorfile):
    os.mkdir(Errorfile)
if not os.path.exists(Logfile):
    os.mkdir(Logfile)
logfilename = datetime.now().strftime("%Y-%m-%d")

class ClientThread(threading.Thread):
    def __init__(self, clientAddress, clientsocket):
        printTerminal(threading.Thread)
        threading.Thread.__init__(self)
        self.csocket = clientsocket
        printTerminal("New connection added: " + str(clientAddress))        

    def run(self):
        global clientAddress, logfilename
        numConnection = threading.activeCount()
        printTerminal('Socket Connection Count- ' + str(numConnection - 1))
        
        while True:
            try:
                self.csocket.settimeout(180.0)
                data = self.csocket.recv(4048)
                if not data:
                    closeSocket(self)
                    print('Hi nandha this socket is device Disconnect - ' + str(clientAddress))
                    sys.exit()
                raw=data
                printTerminal ('Received %s' %raw.hex())
                if (len(raw) > 3):
                    keepAlive = read_incoming_packet(self, raw)                                    
                else:
                    self.close()
                    print('cc')
                    break                
            except socket.timeout:
                print('Hi nandha this socket is timeout Disconnect - ' + str(clientAddress))
                # checkandRemoveSocket(self)
                closeSocket(self)
                break
            except ConnectionResetError as exc:
                print('--------------------------------')
                closeSocket(self)
                LOGGER('Error', logfilename, str(exc))
                print('connection closed' + str(clientAddress))
                # checkandRemoveSocket(self)
                break
            except Exception:                
                print(traceback.format_exc())
                LOGGER('Error', logfilename, traceback.format_exc())
                print('--------------------------------')
                print('socket error check the log .. Continue the data - ' + str(clientAddress))
                closeSocket(self)
                # checkandRemoveSocket(self)
                break

# checkString = lambda s: any(c.isalpha() for c in s) and any(c.isdigit() for c in s)
checkString = lambda s: any(c.isalpha() for c in s)

def rfid_to_card(rfid: int) -> int:
    return rfid >> 16

def read_incoming_packet(client, packet):
    try:
        pocket={}
        packet_list = [packet.hex()[i:i+2] for i in range(0, len(packet.hex()), 2)]
        # print(packet_list)
        if dataJoin(packet_list[0:2])=='000f':
            imei=dataJoin(packet_list[2:])
            # imei=bytes(imei, 'utf-8')
            imei=binascii.unhexlify(imei)
            imei=imei.decode('UTF-8')
            LOGGER('Log', logfilename, str(imei)+'-'+packet.hex())
            client.csocket.sendall(b'\x01')
            storeSockettoimei(imei, client)
            
        elif dataJoin(packet_list[0:4])=='00000000':
            firstpocket=dataJoinHex(packet_list[0:4])
            data_lng=dataJoinHex(packet_list[4:8])
            code=int(packet_list[8],base=16)
            num_data1=packet_list[9]
            # reply_to_client(bytes('000000'+ num_data1 , 'utf-8'),client)
            returnvalue=bytes('000000'+ num_data1 , 'utf-8')
            returnvalue=binascii.unhexlify(returnvalue)
            client.csocket.sendall(returnvalue)
            if code==142:
                ProtocolParserData(packet_list[10:-5],client)
            elif code==13 or code==12:
                rfidpo={}
                pocket['cmdqty1']=int(packet_list[9],base=16)
                pocket['cmdtype']=int(packet_list[10],base=16)
                pocket['cmdsize']=dataJoinHex(packet_list[11:15])
                pocket['time']=secTOdatetime(int(dataJoin(packet_list[15:19]),base=16))
                # print('code_13 - %s' %pocket)
                # pocket['cmd']=binascii.unhexlify(dataJoin(packet_list[21:-7])).decode('UTF-8')
                if dataJoin(packet_list[19:22])=='aabb06':
                    # TCP Binary mode in rs232
                    # print(packet_list[23:27])
                    pocket['cmd']=int(dataJoin(ReverseList(packet_list[23:27])),base=16)
                else:                    
                    # TCP ASCII mode in rs232
                    isValidRfid = True
                    if len(packet_list[19:-5])==4:
                            temp_rfid = packet_list[22] + packet_list[21] +packet_list[20] + packet_list[19]
                            pocket['cmd'] = str(int(temp_rfid,16))
                    elif packet_list[19]=='02':
                        print(packet_list[20:-6])
                        temp_rfid = binascii.unhexlify(dataJoin(packet_list[20:28])).decode('UTF-8')
                        temp_rfid = temp_rfid.strip()
                        if checkString(temp_rfid): 
                            pocket['cmd'] = str(int(temp_rfid,16))
                        else:
                            pocket['cmd'] = temp_rfid
                    else:
                        if len(packet_list[19:-5])==14 or len(packet_list[19:-5])=='14':
                            pocket['cmd']=binascii.unhexlify(dataJoin(packet_list[21:-6])).decode('UTF-8')
                            print(pocket)
                        else:
                            act_data = packet_list[19:-5]
                            if (act_data[0] == "01") and len(act_data)==12:
                                command_13=binascii.unhexlify(dataJoin(act_data[1:])).decode('UTF-8')
                                command_13 = command_13.replace("\x02","").replace("\x00","")
                                pocket['cmd'] = command_13
                            else:
                                sdata = dataJoin(packet_list[19:-5])
                                rfidnumber=[]
                                for x in range(len(sdata)):
                                    spdata=splitString(sdata[x],2)
                                    command_13=binascii.unhexlify(dataJoin(spdata[-10:])).decode('UTF-8')
                                    command_13 = command_13.replace("\x02","").replace("\x00","")
                                    if checkString(command_13): 
                                        command_13 = str(int(command_13,16))
                                    else:
                                        command_13 = command_13
                                    rfidnumber.append()
                                    while("" in rfidnumber) :
                                        rfidnumber.remove('')
                                    if len(rfidnumber) == 0:
                                        isValidRfid = False
                                    pocket['cmd'] = ','.join([str(e) for e in rfidnumber])
                                    print(pocket)
                rfidpo['dt_tracker']=pocket['time']
                rfidpo['rfid']=pocket['cmd'].rstrip()
                # rfidpo['rfid']=int(pocket['cmd'],base=16)
                rfidpo['imei']=get_socketimei(client)
                # print(rfidpo)
                if isValidRfid:
                    postdatatoServer_rfid(rfidpo)
            else:
                printTerminal('Invalid Code ID')
            
            # avldata=processAVLdate(packet_list[10:-5],pocket['code'],client)
            pocket['num_data2']=int(packet_list[-5],base=16)
            pocket['CRC']=dataJoin(packet_list[-4:])
            LOGGER('Log', logfilename,packet.hex())
        else:
            printTerminal(packet)
    except:
        print(traceback.format_exc())
        print('Error in incomming packet')

def ProtocolParserData(data,client):
    global objectdata
    try:
        imei=get_socketimei(client)
        if imei in objectdata:
            del objectdata[imei]
        objectdata[imei]={
            'imei':imei,
            'datetime':millisecTOdatetime(int(dataJoin(data[0:8]),base=16)),
            'priority':int(data[8],base=16)
        }
        loadGPSdate(data[9:24],imei)
        get_2byte_io_element(data[24:],imei,client)
        
    except:
        print(traceback.format_exc())
        print('Error in data')

def get_2byte_io_element(io,imei,client):
    global objectdata
    try:
        objectdata[imei].update({'event_id':int(dataJoin(io[0:2]),base=16),
        'event_count':int(dataJoin(io[2:4]),base=16)
        })
        ioelement=io[4:]

        oneByteValue=int(dataJoin(ioelement[0:2]),base=16)
        del ioelement[0:2]
        if oneByteValue>0:
            oneB_Count=oneByteValue*3
            IOelementValue.getOneByteValue(ioelement[0:oneB_Count],imei,oneByteValue)
            del ioelement[0:oneB_Count]

        twoByteValue=int(dataJoin(ioelement[0:2]),base=16)
        del ioelement[0:2]
        if twoByteValue>0:
            twoB_Count=twoByteValue*4
            IOelementValue.getTwoByteValue(ioelement[0:twoB_Count],imei,twoByteValue)
            del ioelement[0:twoB_Count]

        fourByteValue=int(dataJoin(ioelement[0:2]),base=16)
        del ioelement[0:2]
        if fourByteValue>0:
            fourB_Count=fourByteValue*6
            IOelementValue.getFourByteValue(ioelement[0:fourB_Count],imei,fourByteValue)
            del ioelement[0:fourB_Count]

        eightByteValue=int(dataJoin(ioelement[0:2]),base=16)
        del ioelement[0:2]
        if eightByteValue>0:
            eightB_Count=eightByteValue*10
            IOelementValue.getEightByteValue(ioelement[0:eightB_Count],imei,eightByteValue)
            del ioelement[0:eightB_Count]

        objectdata[imei].update({'XBelement_count':int(dataJoin(ioelement[0:2]),base=16)})
        del ioelement[0:2]

        postdatatoServer(objectdata[imei],client)

        if len(ioelement)==1:
            objectdata[imei].update({'alvdata_count':int(dataJoin(ioelement[0]),base=16)})
            del ioelement[0]

        if len(ioelement)>1:
            ProtocolParserData(ioelement,client)

    except:
        print(traceback.format_exc())
        print('Error in IO Element -2b')

class IOelementValue:
    global objectdata,np,IOelement
    def getOneByteValue(data,imei,count):
        values=np.array_split(data, count)
        for x in range(len(values)):
              ioid=int(dataJoin(values[x][0:2]),base=16)
              if ioid in IOelement:
                objectdata[imei].update({IOelement[ioid][0]:int(values[x][2],base=16)})

    def getTwoByteValue(data,imei,count):
        values=np.array_split(data, count)
        for x in range(len(values)):
              ioid=int(dataJoin(values[x][0:2]),base=16)
              if ioid in IOelement:
                objectdata[imei].update({IOelement[ioid][0]:int(dataJoin(values[x][2:]),base=16)})

    def getFourByteValue(data,imei,count):
        values=np.array_split(data, count)
        for x in range(len(values)):
              ioid=int(dataJoin(values[x][0:2]),base=16)
              if ioid in IOelement:
                objectdata[imei].update({IOelement[ioid][0]:int(dataJoin(values[x][2:]),base=16)})

    def getEightByteValue(data,imei,count):
        values=np.array_split(data, count)
        for x in range(len(values)):
              ioid=int(dataJoin(values[x][0:2]),base=16)
              if ioid in IOelement:
                objectdata[imei].update({IOelement[ioid][0]:int(dataJoin(values[x][2:]),base=16)})

def ReverseList(lst): 
    return [ele for ele in reversed(lst)]

def loadGPSdate(gpsdata,imei):
    global objectdata
    try:
        objectdata[imei].update({'lng':int(dataJoin(gpsdata[0:4]),base=16)/10000000,
            'lat':int(dataJoin(gpsdata[4:8]),base=16)/10000000,
            'altitude':int(dataJoin(gpsdata[8:10]),base=16),
            'angle':int(dataJoin(gpsdata[10:12]),base=16),
            'satellites':int(gpsdata[12],base=16),
            'speed':int(dataJoin(gpsdata[13:15]),base=16)
        })
        # return gpsvalue
    except:
        print(traceback.format_exc())
        print('Error in GPS Data')

def postdatatoServer(value,client):
    global load_conf,load_log
    try:
        if 'lls_fuel1' in value:
            value['fuel1']=kvantstoLTR(value['lls_fuel1'],value['imei'])
        if 'lls_fuel2' in value:
            value['fuel2']=kvantstoLTR(value['lls_fuel2'],value['imei'])
        if 'lls_fuel3' in value:
            value['fuel3']=kvantstoLTR(value['lls_fuel3'],value['imei'])
        if 'lls_fuel4' in value:
            value['fuel4']=kvantstoLTR(value['lls_fuel4'],value['imei'])
        if 'lls_fuel5' in value:
            value['fuel5']=kvantstoLTR(value['lls_fuel5'],value['imei'])

        if 'ibutton' in value:
            if value['ibutton']>0:
                if len(decimalToHex_Single(value['ibutton']))%2 !=0:
                    value['ibutton']=str('0') + decimalToHex_Single(value['ibutton'])
                else:
                    value['ibutton']=decimalToHex_Single(value['ibutton'])

                cvalue=splitString(value['ibutton'],2)
                # print(cvalue)
                cvalue=ReverseList(cvalue)
                # print(cvalue)
                # print(cvalue[1:-1])
                value['rfid']=int(dataJoin(cvalue[1:-1]),base=16)
                value['op']="rfid_swipe"
                value['dt_tracker']=value['datetime']


        value['protocol']=load_conf['protocol']
        value['port']=load_conf['port']
        value['method']='teltoInsertNew'
        value['fimei']=get_socketimei(client)
        if load_conf['insertData'] == 1:
            url = str(load_conf['postURL'])
            PARAMS = value
            r = requests.post(url, data=PARAMS)
            data = r.content        
            LOGGER('Log', logfilename, 'parse ' + str(value))
            printresponds=data.decode("utf-8")
            printTerminal(value)
            printTerminal(printresponds)
    except:
        print(traceback.format_exc())
        print('Server Problem')

def postdatatoServer_rfid(value):
    global load_conf,load_log
    try:
        value['protocol']=load_conf['protocol']
        value['port']=load_conf['port']
        value['method']='teltoInsertNew_rfid'
        value['lat']='0'
        value['lng']='0'
        value['op']='rfid_swipe'
        value['event']='rfid_swipe'
        if load_conf['insertData'] == 1:
            url = str(load_conf['postURL_rfid'])
            PARAMS = value
            r = requests.post(url, data=PARAMS)
            data = r.content        
            LOGGER('Log', logfilename, 'parse ' + str(value))
            printresponds=data.decode("utf-8")
            print(value)
            print(printresponds)
    except:
        print(traceback.format_exc())
        print('Server Problem')

def kvantstoLTR(kvants,imei):
    global load_conf,objectkvants    
    ltr_p=0
    a=0
    if str(imei) in objectkvants:
        full=objectkvants[imei]
    else:
        full=load_conf['fuelvalue']
    if a<full and full>kvants and kvants>a:
        s1=kvants-a
        s2=s1*100
        s3=full-a
        s4=s2/s3
        ltr_p=format(s4, '.3f')
    return ltr_p

def secTOdatetime(sec): 
    global datetime
    try:
        # print(datetime.fromtimestamp(millsec/1000.0))
        date=datetime.utcfromtimestamp(sec).strftime('%Y-%m-%d %H:%M:%S')
        return date
    except:
        # print(traceback.format_exc())
        print('Error in Date')
        return '0000-00-00 00:00:00'

def splitString(value,len):
    r=wrap(value, len)
    return r

def get_socketimei(self):
    global sockettoimei
    try:
        if self in sockettoimei:
            LOGGER('Log', logfilename, str(sockettoimei[self])+'-'+str(self))
            return sockettoimei[self]
        else:
            return 'NO'
    except:
        print('no socket')
        return 'NO'

def dataJoinHex(j):
    jData=''.join([ format(int(x, base = 16), '02d') for x in j ])
    return jData

def dataJoin(j):
    jData=''.join(str(x) for x in j)
    return jData

def decimalToHex_Single(v):
    v=str(v)
    v=format(int(v), 'x')
    if len(v)<=1:
        v='0' + v
    return v

def reply_to_client(data,client):
    try:
        returnvalue=binascii.unhexlify(data)
        # client.csocket.sendall(returnvalue)
    except:
        print(traceback.format_exc())
        print('Error in incomming packet')

def millisecTOdatetime(millsec):
    global datetime
    try:
        mill=millsec/1000.0
        # print(datetime.fromtimestamp(millsec/1000.0))
        date=datetime.utcfromtimestamp(millsec//1000).replace(microsecond=millsec%1000*1000).strftime('%Y-%m-%d %H:%M:%S')
        # print(datetime.strptime(millsec,'%Y-%m-%d %X').strftime('%Y-%m-%d %X'))
        # date=datetime.utcfromtimestamp(mill).strftime('%Y-%m-%d %H:%M:%S')
        return date
    except:
        print(traceback.format_exc())
        printTerminal('Error in Date')
        return '0000-00-00 00:00:00'


def LOGGER(path, filename, data):
    global load_log
    if ((load_log['datalog'] == 1 and path == 'Log') or (load_log['formatelog'] == 1 and path == 'Wrong') or (load_log['errorlog'] == 1 and path == 'Error')):
        filename = datetime.now().strftime("%Y-%m-%d")
        f = open(path + '/' + filename + ".txt", "a")
        f.write(data)
        f.write('\n')
        f.close()

def remove_old_socket(imei):
    global sockettoimei
    try:
        socketdata=swapeKeyANDvalue(sockettoimei)
        if imei in socketdata:
            if socketdata[imei].is_alive() is True:
                socketdata[imei].csocket.close()

            del sockettoimei[socketdata[imei]]
    except:
        print('Error connection close')

def add_if_key_not_exist_socket(sockettoimei, key, value):
    if key not in sockettoimei:
        sockettoimei.update({key: value})
    elif key in sockettoimei:
        if key.is_alive() is True:
            key.csocket.close()
        del sockettoimei[key]
        # sockettoimei.update({key: value})

def closeSocket(client):
    global sockettoimei
    if client.is_alive() is True:
        client.csocket.close()

    if client in sockettoimei:
        del sockettoimei[client]

def swapeKeyANDvalue(value):
    res = dict((v,k) for k,v in value.items())
    return res

def printTerminal(value):
    global load_log
    if load_log['printterminal']== '1':
        print(value)

def storeSockettoimei(imei, self):
    global sockettoimei
    remove_old_socket(imei)
    add_if_key_not_exist_socket(sockettoimei,self,imei)

if __name__ == '__main__':
    hostname = socket.gethostname()
    LOCALHOST = socket.gethostbyname(hostname)
    LOCALHOST = "0.0.0.0"
    # LOCALHOST = load_conf['IP']
    PORT = load_conf['port']
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((LOCALHOST, PORT))
    pstate="socket binded to IP & Port - " + LOCALHOST + ' , ', PORT
    print(pstate)
    while True:
        print_lock = threading.Lock()
        server.listen()
        clientsock, clientAddress = server.accept()
        print_lock.acquire()
        newthread = ClientThread(clientAddress, clientsock)
        newthread.start()
