#!/usr/bin/env python
# -*- coding: utf-8 -*-

#https://scapy.readthedocs.io/en/latest/api/scapy.packet.html#scapy.packet.Packet
#https://scapy.readthedocs.io/en/latest/api/scapy.contrib.modbus.html

import getpass
import scapy.all as scapy
import scapy.contrib.modbus as mb

from triPacket import triPacket

def BytesToBits(value):
    status = []
    for i in range(len(value)):
        #Transform the i'th byte into 1 binary string
        val = str(bin(value[i]))
        #Delete the '0x' header
        val = val[2:]
        #Fill with '0' to have 8 values
        val = val.rjust(8,'0')
        #Revert the list to get into the growing order
        val = val[::-1]
        #Add the 8 values on `status`
        status.extend(j for j in [*val])
    return status


def decode(pkt):
    '''Insert the packet's `pkt` the [type_call,address,status] informations into the `connec` database '''
    if "ModbusADU" in pkt:
        global miniL
        modpkt = pkt["ModbusADU"]
        type_call = ""

        #Multiple Coils/Registers request-> else: Single request
        if "Multiple" in modpkt.payload.name:
            type_call += "m"
        else:
            type_call += "s"

        #Coils or Registers request
        if "Coil" in modpkt.payload.name:
            type_call += "C"
        else:
            type_call += "R"

        
        miniL[0] = type_call

        #Read request (read request are all "Multiple request")
        if "Read" in modpkt.payload.name:
            type_call += "r"
            #Response for a read request packet's 
            if "Response" in modpkt.payload.name:
                #Number of byte that have been read
                #byte_count = modpkt.payload.getfieldval("byteCount")
                status = []
                #Response for a read coils request packet's
                if "C" in type_call:
                    #Get the list of byte's values that have been read
                    value = modpkt.payload.getfieldval("coilStatus")

                    #Transform the byte's values into a list of bits values for each byte
                    status = BytesToBits(value)
                #Response for a read registers request packet's
                else:
                    #Get the list of byte's values that have been read
                    status = modpkt.payload.getfieldval("registerVal")

                #Fill the list of the values/address that have been read
                LPackets = []
                for j in range(len(status)):
                    LPackets.append([str.lower(miniL[0][1]),miniL[2]+j,status[j]])

                #Add the read's status into the `connect` database
                triPacket(LPackets,connec)
                #Reset the globalization's list
                ####miniL = [0,0,0]

            #First request for a read packet's : Get the starting address and globalize it before getting the response
            else:
                addr = modpkt.payload.getfieldval("startAddr")
                miniL[2] = addr
        
        #Write request
        else:
            type_call += "w"
            #Write request needed information aren't on the Response packet's 
            if "Response" in modpkt.payload.name:
                #Reset of the globalization
                miniL = [0,0,0]
            else:
                #Multiple write request
                if "m" in type_call:
                    #Get the starting address
                    addr = modpkt.payload.getfieldval("startAddr")
                    #Get the list of values that have been read
                    value = modpkt.payload.getfieldval("outputsValue")
                    status = []
                    #Multiple write coils request (register's values are already on the good format)
                    LPackets = []
                    #Get the number of bytes to be write in order to not reset to 0, address on the same bytes of the written one's
                    if "C" in type_call:
                        #Transform the byte's values into a list of bits values for each byte
                        status = BytesToBits(value)
                        byte_count = modpkt.payload.getfieldval("quantityOutput")
                        #Fill the list of the values/address that while be write
                    else:
                        byte_count = modpkt.payload.getfieldval("quantityRegisters")
                        status=value
                    for j in range(byte_count):
                        if miniL[0][1]=='C' and status[j]==65280:
                            status[j]=1
                        LPackets.append([str.lower(miniL[0][1]),addr+j,status[j]])
                    
                    #Add and check the write's status into the `connect` database
                    triPacket(LPackets,connec)
                #Single write request
                else:
                    #Single write coil request
                    if "C" in type_call:
                        addr = modpkt.payload.getfieldval("outputAddr")
                        value = modpkt.payload.getfieldval("outputValue")
                        if value==65280:
                            value=1
                    #Single write register request
                    else:
                        addr = modpkt.payload.getfieldval("registerAddr")
                        value = modpkt.payload.getfieldval("registerValue")
                    #Add the write's status into the `connect` database
                    triPacket([str.lower(miniL[0][1]),addr,value],connec)



#Connection to the database
print("In order for data sniffed to be stored inside the database, please register the following :")
DB_HOST = input('host of the database server : ')
if not DB_HOST:
    DB_HOST = '192.168.128.141'
DB_NAME = input('name of the database : ')
if not DB_NAME:
    DB_NAME = 'dblodufour1'
DB_USER = input('login of the user : ')
if not DB_USER:
    DB_USER = 'lodufour1'
DB_PASSWORD = getpass.getpass('user password : ')
connec=[DB_HOST,DB_NAME,DB_USER,DB_PASSWORD]

#Initialization in order globalize the First Packet with his response
miniL = [0,0,0]

#Sniff the packets received on the localhost's network interface and call the decode() function for each of them
scapy.sniff(iface="lo", prn=decode)

