/*
Copyright (C) 2008-2011 Romain Moret at Grame

This program 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 2 of the License, or
(at your option) any later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackNetInterface__
#define __JackNetInterface__

#include "JackNetTool.h"
#include <limits.h>

namespace Jack
{

#define DEFAULT_MULTICAST_IP "225.3.19.154"
#define DEFAULT_PORT    19000
#define DEFAULT_MTU     1500
#define MAX_MTU         9000

#define SLAVE_SETUP_RETRY   5

#define MANAGER_INIT_TIMEOUT    1000000 * 2   // in usec
#define MASTER_INIT_TIMEOUT     1000000 * 10  // in usec
#define SLAVE_INIT_TIMEOUT      1000000 * 1   // in usec
#define PACKET_TIMEOUT          1000000       // in usec

#define NETWORK_DEFAULT_LATENCY     2
#define NETWORK_MAX_LATENCY         30  // maximum possible latency in network master/slave loop

    /**
    \Brief This class describes the basic Net Interface, used by both master and slave.
    */

    class SERVER_EXPORT JackNetInterface
    {

        friend class JackNetExt;

        protected:

            bool fSetTimeOut;
            int fPacketTimeOut;

            void Initialize();

            session_params_t fParams;
            JackNetSocket fSocket;
            char fMulticastIP[32];

            // headers
            packet_header_t fTxHeader;
            packet_header_t fRxHeader;

            // transport
            net_transport_data_t fSendTransportData;
            net_transport_data_t fReturnTransportData;

            // network buffers
            char* fTxBuffer;
            char* fRxBuffer;
            char* fTxData;
            char* fRxData;

            // JACK buffers
            NetMidiBuffer* fNetMidiCaptureBuffer;
            NetMidiBuffer* fNetMidiPlaybackBuffer;
            NetAudioBuffer* fNetAudioCaptureBuffer;
            NetAudioBuffer* fNetAudioPlaybackBuffer;

            // utility methods
            int SetNetBufferSize();
            void FreeNetworkBuffers();

            // virtual methods : depends on the sub class master/slave
            virtual bool SetParams();
            virtual bool Init() = 0;

            // transport
            virtual void EncodeTransportData() = 0;
            virtual void DecodeTransportData() = 0;

            // sync packet
            virtual void EncodeSyncPacket(int frames = -1) = 0;
            virtual void DecodeSyncPacket(int& frames) = 0;

            virtual int SyncRecv() = 0;
            virtual int SyncSend() = 0;
            virtual int DataRecv() = 0;
            virtual int DataSend() = 0;

            virtual int Send(size_t size, int flags) = 0;
            virtual int Recv(size_t size, int flags) = 0;

            virtual void FatalRecvError() = 0;
            virtual void FatalSendError() = 0;

            int MidiSend(NetMidiBuffer* buffer, int midi_channnels, int audio_channels);
            int AudioSend(NetAudioBuffer* buffer, int audio_channels);

            int MidiRecv(packet_header_t* rx_head, NetMidiBuffer* buffer, uint& recvd_midi_pckt);
            int AudioRecv(packet_header_t* rx_head, NetAudioBuffer* buffer);

            int FinishRecv(NetAudioBuffer* buffer);

            void SetRcvTimeOut();
            void SetPacketTimeOut(int time_out)
            {
                // New time out
                fPacketTimeOut = time_out;
                fSetTimeOut = false;
            }

            NetAudioBuffer* AudioBufferFactory(int nports, char* buffer);

        public:

            JackNetInterface();
            JackNetInterface(const char* multicast_ip, int port);
            JackNetInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip);

            virtual ~JackNetInterface();

    };

    /**
    \Brief This class describes the Net Interface for masters (NetMaster)
    */

    class SERVER_EXPORT JackNetMasterInterface : public JackNetInterface
    {

        protected:

            bool fRunning;
            int fCurrentCycleOffset;
            int fMaxCycleOffset;
            bool fSynched;

            bool Init();
            bool SetParams();

            void Exit();

            int SyncRecv();
            int SyncSend();

            int DataRecv();
            int DataSend();

            // sync packet
            void EncodeSyncPacket(int frames = -1);
            void DecodeSyncPacket(int& frames);

            int Send(size_t size, int flags);
            int Recv(size_t size, int flags);

            void FatalRecvError();
            void FatalSendError();

        public:

            JackNetMasterInterface() 
                : JackNetInterface(), 
                fRunning(false), 
                fCurrentCycleOffset(0), 
                fMaxCycleOffset(0), 
                fSynched(false)
            {}
            JackNetMasterInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip)
                    : JackNetInterface(params, socket, multicast_ip), 
                    fRunning(false), 
                    fCurrentCycleOffset(0), 
                    fMaxCycleOffset(0), 
                    fSynched(false)
            {}

            virtual~JackNetMasterInterface()
            {}
    };

    /**
    \Brief This class describes the Net Interface for slaves (NetDriver and NetAdapter)
    */

    class SERVER_EXPORT JackNetSlaveInterface : public JackNetInterface
    {

        protected:

            static uint fSlaveCounter;

            bool Init();
            bool InitConnection(int time_out_sec);
            bool InitRendering();

            net_status_t SendAvailableToMaster(int count = INT_MAX);
            net_status_t SendStartToMaster();

            bool SetParams();

            int SyncRecv();
            int SyncSend();

            int DataRecv();
            int DataSend();

            // sync packet
            void EncodeSyncPacket(int frames = -1);
            void DecodeSyncPacket(int& frames);

            int Recv(size_t size, int flags);
            int Send(size_t size, int flags);

            void FatalRecvError();
            void FatalSendError();

            void InitAPI();

        public:

            JackNetSlaveInterface() : JackNetInterface()
            {
                InitAPI();
            }

            JackNetSlaveInterface(const char* ip, int port) : JackNetInterface(ip, port)
            {
                InitAPI();
            }

            virtual ~JackNetSlaveInterface()
            {
                // close Socket API with the last slave
                if (--fSlaveCounter == 0) {
                    SocketAPIEnd();
                }
            }
    };
}

#endif
