#ifndef SNIFFER_H
#define SNIFFER_H

#include <QObject>
#include <QString>
#include <QDebug>
#include "ip_packet.h"
#include <QWaitCondition>
#include <QMutex>

#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ethernet_hdr.h"
#include "ip_hdr.h"
#include "tcp_hdr.h"
#include "udp_hdr.h"

/* default snap length (maximum bytes per packet to capture) */
#define SNAP_LEN 1518

/* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14

#define IPV4 8
///
/// \brief The Sniffer class is the one that use the pcap library to
/// extract the packet information.  It discards any packet that is not
/// Ethernet->IP->(TCP|UDP), and pass up to the GUI the packets that are
/// Ethernet->IP->(TCP|UDP).
///
class Sniffer : public QObject
{
    Q_OBJECT

public:

    /// \fn Sniffer::Sniffer()
    /// \~English
    /// \brief Constructor, does nothing
    /// \~Spanish
    /// \brief Constructor, no hace nada
    Sniffer() ;

    /// \fn Sniffer::Sniffer(QWaitCondition *pw, QMutex *mx, int *ps)
    /// \~English
    /// \brief Sets the mutexes for the inter communication between
    /// the sniffer and the GUI.
    /// \param pw used to pause the capture of packets
    /// \param mx used to protect critical region
    /// \param ps flag to pause the packet capture
    /// \~Spanish
    /// \brief Ajusta los mutexes para la inter comunicacion entre
    /// el sniffer y el GUI.
    /// \param pw se usa para pausar la captura de paquetes
    /// \param mx usado para proteger la region critica
    /// \param ps bandera para pausar la captura de paquetes
    Sniffer(QWaitCondition *pw, QMutex *mx, int *ps) ;

    /// \fn Sniffer::~Sniffer()
    /// \~English
    /// \brief Destructor, does nothing
    /// \~Spanish
    /// \brief Destructor, no hace nada
    ~Sniffer() ;

    /// \fn vector<ip_packet> *Sniffer::getPacketList()
    /// \~English
    /// \brief Returns the packet list that contains the packets that are
    /// Ethernet -> IP -> (TCP|UDP)
    /// \return vector of packets
    /// \~Spanish
    /// \brief Devuelve la lista de paquetes que contiene los paquetes que son
    /// Ethernet -> IP -> (TCP|UDP)
    /// \return vector de paquetes
    vector<ip_packet> * getPacketList() ;

    /// \fn string Sniffer::format_payload(const char *payload, int len)
    /// \~English
    /// \brief Formats the payload from a byte stream into a string of ascci
    /// \param payload payload of the packet
    /// \param len length of the packet
    /// \return the payload in a string of ascii
    /// \~Spanish
    /// \brief Formatea la carga de un torrente de bytes a un cadena de
    /// caracteres ascii
    /// \param payload carga del paquete
    /// \param len largo del paquete
    /// \return la carga en una cadena de caracteres ascii
    string format_payload(const char *payload, int len);

    /// \fn string Sniffer::format_hex_ascii_line(const char *payload, int len, int offset)
    /// \~English
    /// \brief Return string with the bytes of a payload line in ascii
    /// \param payload payload of the packet
    /// \param len  length of the packet
    /// \param offset offset of packet
    /// \return a string with the bytes of a payload line in ascii
    /// \~Spanish
    /// \brief Devuelve una cadena de caracteres con los bytes de una linea
    /// de la carga en ascii
    /// \param payload carga del paquete
    /// \param len largo del paquete
    /// \param offset distancia del paquete
    /// \return a string with the bytes of a payload line in ascii
    string format_hex_ascii_line(const char *payload, int len, int offset);

    /// \fn void Sniffer::find_devices(vector<string> &devs)
    /// \~English
    /// \brief Find the network devices in the computer, and store them in vector devs.
    /// \param devs vector of string for device names
    /// \~Spanish
    /// \brief Encuentra los dispositivos de red en la computadora, y los almacena en
    /// el vector devs
    /// \param devs vector de cadenas de caracteres para los nombres
    void find_devices(vector<string> &) ;

    /// \fn void Sniffer::setDevice(string dev)
    /// \~English
    /// \brief Sets the device to capture packets to dev
    /// \param dev name of the device
    /// \~Spanish
    /// \brief Ajusta el dispositivo para caputurar paquetes a dev
    /// \param dev nombre del dispositivo
    void setDevice(string) ;

public slots:
    void process()  ;

signals:
    void resultReady(unsigned long index);

private:

    vector<ip_packet> packet_list ;
    QWaitCondition *wait_pause ;
    QMutex  * mutex ;
    string device ;
    string filter ;
    int *pause ;

    /// \fn void Sniffer::print_payload(const u_char *payload, int len)
    /// \~English
    /// \brief Prints the payload in ascii
    /// \param payload payload of the packet
    /// \param len payload length
    /// \~Spanish
    /// \brief Imprime la carga en ascii
    /// \param payload carga del paquete
    /// \param len largo de la carga
    void print_payload(const u_char *payload, int len);

    /// \fn void Sniffer::print_hex_ascii_line(const u_char *payload, int len, int offset)
    /// \~English
    /// \brief Prints to std output the a payload in ascii
    /// \param payload payload of the packet
    /// \param len  length of the packet
    /// \param offset offset
    /// \~Spanish
    /// \brief Imprime la carga en ascii a std output
    /// \param payload carga del paquete
    /// \param len largo de la carga
    /// \param offset distancia
    void print_hex_ascii_line(const u_char *payload, int len, int offset);

    /// \fn void Sniffer::got_packet(const struct pcap_pkthdr *, const u_char *packet)
    /// \~English
    /// \brief Disects the received packet. Takes out the info needed.
    /// \param pcap_pkthdr header of the pcap packet
    /// \param packet recieved packet
    /// \~Spanish
    /// \brief Secciona el paquete recibido. Extrae la informacion necesaria.
    /// \param pcap_pkthdr encabezado del paquete de pcap
    /// \param packet paquete recibido
    void
    got_packet(const struct pcap_pkthdr *header, const u_char *packet);

};

#endif // SNIFFER_H
