/*
   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
   Queen in Right of Canada (Communications Research Center Canada)
   Copyright (C) 2017
   Matthias P. Braendli, matthias.braendli@mpb.li
    http://www.opendigitalradio.org
   */
/*
   This file is part of ODR-DabMux.
   ODR-DabMux is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as
   published by the Free Software Foundation, either version 3 of the
   License, or (at your option) any later version.
   ODR-DabMux is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with ODR-DabMux.  If not, see .
   */
#pragma once
#ifdef HAVE_CONFIG_H
#   include "config.h"
#endif
#include "InetAddress.h"
#include "ThreadsafeQueue.h"
#include 
#include 
#include 
#include 
#include 
#include 
#define SOCKET           int
#define INVALID_SOCKET   -1
#define SOCKET_ERROR     -1
#define reuseopt_t       int
#include 
#include 
#include 
#include 
#include 
class UdpPacket;
/**
 *  This class represents a socket for sending and receiving UDP packets.
 *
 *  A UDP socket is the sending or receiving point for a packet delivery service.
 *  Each packet sent or received on a datagram socket is individually
 *  addressed and routed. Multiple packets sent from one machine to another may
 *  be routed differently, and may arrive in any order.
 */
class UdpSocket
{
    public:
        /** Create a new socket that will not be bound to any port. To be used
         * for data output.
         */
        UdpSocket();
        /** Create a new socket.
         *  @param port The port number on which the socket will be bound
         */
        UdpSocket(int port);
        /** Create a new socket.
         *  @param port The port number on which the socket will be bound
         *  @param name The IP address on which the socket will be bound.
         *              It is used to bind the socket on a specific interface if
         *              the computer have many NICs.
         */
        UdpSocket(int port, const std::string& name);
        ~UdpSocket();
        UdpSocket(const UdpSocket& other) = delete;
        const UdpSocket& operator=(const UdpSocket& other) = delete;
        /** reinitialise socket. Close the already open socket, and
         * create a new one
         */
        int reinit(int port, const std::string& name);
        /** Close the socket
         */
        int close(void);
        /** Send an UDP packet.
         *  @param packet The UDP packet to be sent. It includes the data and the
         *                destination address
         *  return 0 if ok, -1 if error
         */
        int send(UdpPacket& packet);
        /** Send an UDP packet
         *
         *  return 0 if ok, -1 if error
         */
        int send(const std::vector& data, InetAddress destination);
        /** Receive an UDP packet.
         *  @param packet The packet that will receive the data. The address will be set
         *                to the source address.
         *  @return 0 if ok, -1 if error
         */
        int receive(UdpPacket& packet);
        int joinGroup(const char* groupname, const char *if_addr);
        int setMulticastSource(const char* source_addr);
        int setMulticastTTL(int ttl);
        /** Set blocking mode. By default, the socket is blocking.
         *  @return 0  if ok
         *          -1 if error
         */
        int setBlocking(bool block);
    protected:
        /// The address on which the socket is bound.
        InetAddress address;
        /// The low-level socket used by system functions.
        SOCKET listenSocket;
};
/** This class represents a UDP packet.
 *
 *  A UDP packet contains a payload (sequence of bytes) and an address. For
 *  outgoing packets, the address is the destination address. For incoming
 *  packets, the address tells the user from what source the packet arrived from.
 */
class UdpPacket
{
    public:
        /** Construct an empty UDP packet.
         */
        UdpPacket();
        UdpPacket(size_t initSize);
        /** Give the pointer to data.
         *  @return The pointer
         */
        uint8_t* getData(void);
        /** Append some data at the end of data buffer and adjust size.
         *  @param data Pointer to the data to add
         *  @param size Size in bytes of new data
         */
        void addData(const void *data, size_t size);
        size_t getSize(void);
        /** Changes size of the data buffer size. Keeps data intact unless
         *  truncated.
         */
        void setSize(size_t newSize);
        /** Returns the UDP address of the packet.
         */
        InetAddress getAddress(void);
        const std::vector& getBuffer(void) const {
            return m_buffer;
        }
    private:
        std::vector m_buffer;
        InetAddress address;
};
/* Threaded UDP receiver */
class UdpReceiver {
    public:
        UdpReceiver() : m_port(0), m_thread(), m_stop(false), m_packets() {}
        ~UdpReceiver();
        UdpReceiver(const UdpReceiver&) = delete;
        UdpReceiver operator=(const UdpReceiver&) = delete;
        // Start the receiver in a separate thread
        void start(int port, std::string& bindto, std::string& mcastaddr, size_t max_packets_queued);
        // Get the data contained in a UDP packet, blocks if none available
        // In case of error, throws a runtime_error
        std::vector get_packet_buffer(void);
    private:
        void m_run(void);
        int m_port;
        std::string m_bindto;
        std::string m_mcastaddr;
        size_t m_max_packets_queued;
        std::thread m_thread;
        std::atomic m_stop;
        ThreadsafeQueue m_packets;
        UdpSocket m_sock;
};