#ifndef __RTMP_H__
#define __RTMP_H__
/*
 *      Copyright (C) 2005-2008 Team XBMC
 *      http://www.xbmc.org
 *      Copyright (C) 2008-2009 Andrej Stepanchuk
 *      Copyright (C) 2009-2010 Howard Chu
 *
 *  This file is part of librtmp.
 *
 *  librtmp is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1,
 *  or (at your option) any later version.
 *
 *  librtmp 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 Lesser General Public License
 *  along with librtmp see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA  02110-1301, USA.
 *  http://www.gnu.org/copyleft/lgpl.html
 */

#define NO_CRYPTO
//#define CRYPTO

#if !defined(NO_CRYPTO) && !defined(CRYPTO)
#define CRYPTO
#endif

#include <errno.h>
#include <stddef.h>
#include <stdint.h>

#include "amf.h"
#include "error.h"

#ifdef __cplusplus
extern "C" {
#endif

#define RTMP_LIB_VERSION 0x020300 /* 2.3 */

#define RTMP_FEATURE_HTTP 0x01
#define RTMP_FEATURE_ENC 0x02
#define RTMP_FEATURE_SSL 0x04
#define RTMP_FEATURE_MFP 0x08 /* not yet supported */
#define RTMP_FEATURE_WRITE 0x10 /* publish, not play */
#define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */

#define RTMP_PROTOCOL_UNDEFINED -1
#define RTMP_PROTOCOL_RTMP 0
#define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC
#define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP
#define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL
#define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP | RTMP_FEATURE_ENC)
#define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP | RTMP_FEATURE_SSL)
#define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP

#define RTMP_DEFAULT_CHUNKSIZE 128

/* needs to fit largest number of bytes recv() may return */
#define RTMP_BUFFER_CACHE_SIZE (16 * 1024)

#define RTMP_CHANNELS 65600

extern const char PILI_RTMPProtocolStringsLower[][7];
extern const AVal PILI_RTMP_DefaultFlashVer;
extern int PILI_RTMP_ctrlC;

uint32_t PILI_RTMP_GetTime(void);

#define RTMP_PACKET_TYPE_AUDIO 0x08
#define RTMP_PACKET_TYPE_VIDEO 0x09
#define RTMP_PACKET_TYPE_INFO 0x12

#define RTMP_MAX_HEADER_SIZE 18

#define RTMP_PACKET_SIZE_LARGE 0
#define RTMP_PACKET_SIZE_MEDIUM 1
#define RTMP_PACKET_SIZE_SMALL 2
#define RTMP_PACKET_SIZE_MINIMUM 3

typedef struct PILI_RTMPChunk {
    int c_headerSize;
    int c_chunkSize;
    char *c_chunk;
    char c_header[RTMP_MAX_HEADER_SIZE];
} PILI_RTMPChunk;

typedef struct PILI_RTMPPacket {
    uint8_t m_headerType;
    uint8_t m_packetType;
    uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */
    int m_nChannel;
    uint32_t m_nTimeStamp; /* timestamp */
    int32_t m_nInfoField2; /* last 4 bytes in a long header */
    uint32_t m_nBodySize;
    uint32_t m_nBytesRead;
    PILI_RTMPChunk *m_chunk;
    char *m_body;
} PILI_RTMPPacket;

typedef struct PILI_RTMPSockBuf {
    int sb_socket;
    int sb_size; /* number of unprocessed bytes in buffer */
    char *sb_start; /* pointer into sb_pBuffer of next byte to process */
    char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */
    int sb_timedout;
    void *sb_ssl;
} PILI_RTMPSockBuf;

void PILI_RTMPPacket_Reset(PILI_RTMPPacket *p);
void PILI_RTMPPacket_Dump(PILI_RTMPPacket *p);
int PILI_RTMPPacket_Alloc(PILI_RTMPPacket *p, int nSize);
void PILI_RTMPPacket_Free(PILI_RTMPPacket *p);

#define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize)

typedef struct PILI_RTMP_LNK {
    AVal hostname;
    AVal domain;
    AVal sockshost;

    AVal playpath0; /* parsed from URL */
    AVal playpath; /* passed in explicitly */
    AVal tcUrl;
    AVal swfUrl;
    AVal pageUrl;
    AVal app;
    AVal auth;
    AVal flashVer;
    AVal subscribepath;
    AVal token;
    AMFObject extras;
    int edepth;

    int seekTime;
    int stopTime;

#define RTMP_LF_AUTH 0x0001 /* using auth param */
#define RTMP_LF_LIVE 0x0002 /* stream is live */
#define RTMP_LF_SWFV 0x0004 /* do SWF verification */
#define RTMP_LF_PLST 0x0008 /* send playlist before play */
#define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */
#define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */
    int lFlags;

    int swfAge;

    int protocol;
    int timeout; /* connection timeout in seconds */
    int send_timeout; /* send data timeout */

    unsigned short socksport;
    unsigned short port;

#ifdef CRYPTO
#define RTMP_SWF_HASHLEN 32
    void *dh; /* for encryption */
    void *rc4keyIn;
    void *rc4keyOut;

    uint32_t SWFSize;
    uint8_t SWFHash[RTMP_SWF_HASHLEN];
    char SWFVerificationResponse[RTMP_SWF_HASHLEN + 10];
#endif
} PILI_RTMP_LNK;

/* state for read() wrapper */
typedef struct PILI_RTMP_READ {
    char *buf;
    char *bufpos;
    unsigned int buflen;
    uint32_t timestamp;
    uint8_t dataType;
    uint8_t flags;
#define RTMP_READ_HEADER 0x01
#define RTMP_READ_RESUME 0x02
#define RTMP_READ_NO_IGNORE 0x04
#define RTMP_READ_GOTKF 0x08
#define RTMP_READ_GOTFLVK 0x10
#define RTMP_READ_SEEKING 0x20
    int8_t status;
#define RTMP_READ_COMPLETE -3
#define RTMP_READ_ERROR -2
#define RTMP_READ_EOF -1
#define RTMP_READ_IGNORE 0

    /* if bResume == TRUE */
    uint8_t initialFrameType;
    uint32_t nResumeTS;
    char *metaHeader;
    char *initialFrame;
    uint32_t nMetaHeaderSize;
    uint32_t nInitialFrameSize;
    uint32_t nIgnoredFrameCounter;
    uint32_t nIgnoredFlvFrameCounter;
} PILI_RTMP_READ;

typedef struct PILI_RTMP_METHOD {
    AVal name;
    int num;
} PILI_RTMP_METHOD;

typedef void (*PILI_RTMPErrorCallback)(RTMPError *error, void *userData);

typedef struct PILI_CONNECTION_TIME {
    uint32_t connect_time;
    uint32_t handshake_time;
} PILI_CONNECTION_TIME;

typedef void (*PILI_RTMP_ConnectionTimeCallback)(
    PILI_CONNECTION_TIME *conn_time, void *userData);

typedef struct PILI_RTMP {
    int m_inChunkSize;
    int m_outChunkSize;
    int m_nBWCheckCounter;
    int m_nBytesIn;
    int m_nBytesInSent;
    int m_nBufferMS;
    int m_stream_id; /* returned in _result from createStream */
    int m_mediaChannel;
    uint32_t m_mediaStamp;
    uint32_t m_pauseStamp;
    int m_pausing;
    int m_nServerBW;
    int m_nClientBW;
    uint8_t m_nClientBW2;
    uint8_t m_bPlaying;
    uint8_t m_bSendEncoding;
    uint8_t m_bSendCounter;

    int m_numInvokes;
    int m_numCalls;
    PILI_RTMP_METHOD *m_methodCalls; /* remote method calls queue */

    PILI_RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS];
    PILI_RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS];
    int m_channelTimestamp[RTMP_CHANNELS]; /* abs timestamp of last packet */

    double m_fAudioCodecs; /* audioCodecs for the connect packet */
    double m_fVideoCodecs; /* videoCodecs for the connect packet */
    double m_fEncoding; /* AMF0 or AMF3 */

    double m_fDuration; /* duration of stream in seconds */

    int m_msgCounter; /* RTMPT stuff */
    int m_polling;
    int m_resplen;
    int m_unackd;
    AVal m_clientID;

    PILI_RTMP_READ m_read;
    PILI_RTMPPacket m_write;
    PILI_RTMPSockBuf m_sb;
    PILI_RTMP_LNK Link;

    PILI_RTMPErrorCallback m_errorCallback;
    PILI_RTMP_ConnectionTimeCallback m_connCallback;
    RTMPError *m_error;
    void *m_userData;
    int m_is_closing;
    int m_tcp_nodelay;
    uint32_t ip;
} PILI_RTMP;

int PILI_RTMP_ParseURL(const char *url, int *protocol, AVal *host,
                       unsigned int *port, AVal *playpath, AVal *app);

int PILI_RTMP_ParseURL2(const char *url, int *protocol, AVal *host,
                        unsigned int *port, AVal *playpath, AVal *app, AVal *domain);

void PILI_RTMP_ParsePlaypath(AVal *in, AVal *out);
void PILI_RTMP_SetBufferMS(PILI_RTMP *r, int size);
void PILI_RTMP_UpdateBufferMS(PILI_RTMP *r, RTMPError *error);

int PILI_RTMP_SetOpt(PILI_RTMP *r, const AVal *opt, AVal *arg,
                     RTMPError *error);
int PILI_RTMP_SetupURL(PILI_RTMP *r, const char *url, RTMPError *error);
void PILI_RTMP_SetupStream(PILI_RTMP *r, int protocol, AVal *hostname,
                           unsigned int port, AVal *sockshost, AVal *playpath,
                           AVal *tcUrl, AVal *swfUrl, AVal *pageUrl, AVal *app,
                           AVal *auth, AVal *swfSHA256Hash, uint32_t swfSize,
                           AVal *flashVer, AVal *subscribepath, int dStart,
                           int dStop, int bLiveStream, long int timeout);

int PILI_RTMP_Connect(PILI_RTMP *r, PILI_RTMPPacket *cp, RTMPError *error);
struct sockaddr;
int PILI_RTMP_Connect0(PILI_RTMP *r, struct addrinfo *ai, unsigned short port,
                       RTMPError *error);
int PILI_RTMP_Connect1(PILI_RTMP *r, PILI_RTMPPacket *cp, RTMPError *error);
int PILI_RTMP_Serve(PILI_RTMP *r, RTMPError *error);

int PILI_RTMP_ReadPacket(PILI_RTMP *r, PILI_RTMPPacket *packet);
int PILI_RTMP_SendPacket(PILI_RTMP *r, PILI_RTMPPacket *packet, int queue,
                         RTMPError *error);
int PILI_RTMP_SendChunk(PILI_RTMP *r, PILI_RTMPChunk *chunk, RTMPError *error);
int PILI_RTMP_IsConnected(PILI_RTMP *r);
int PILI_RTMP_Socket(PILI_RTMP *r);
int PILI_RTMP_IsTimedout(PILI_RTMP *r);
double PILI_RTMP_GetDuration(PILI_RTMP *r);
int PILI_RTMP_ToggleStream(PILI_RTMP *r, RTMPError *error);

int PILI_RTMP_ConnectStream(PILI_RTMP *r, int seekTime, RTMPError *error);
int PILI_RTMP_ReconnectStream(PILI_RTMP *r, int seekTime, RTMPError *error);
void PILI_RTMP_DeleteStream(PILI_RTMP *r, RTMPError *error);
int PILI_RTMP_GetNextMediaPacket(PILI_RTMP *r, PILI_RTMPPacket *packet);
int PILI_RTMP_ClientPacket(PILI_RTMP *r, PILI_RTMPPacket *packet);

void PILI_RTMP_Init(PILI_RTMP *r);
void PILI_RTMP_Close(PILI_RTMP *r, RTMPError *error);
PILI_RTMP *PILI_RTMP_Alloc(void);
void PILI_RTMP_Free(PILI_RTMP *r);
void PILI_RTMP_EnableWrite(PILI_RTMP *r);

int PILI_RTMP_LibVersion(void);
void PILI_RTMP_UserInterrupt(void); /* user typed Ctrl-C */

int PILI_RTMP_SendCtrl(PILI_RTMP *r, short nType, unsigned int nObject,
                       unsigned int nTime, RTMPError *error);

/* caller probably doesn't know current timestamp, should
   * just use RTMP_Pause instead
   */
int PILI_RTMP_SendPause(PILI_RTMP *r, int DoPause, int dTime, RTMPError *error);
int PILI_RTMP_Pause(PILI_RTMP *r, int DoPause, RTMPError *error);

int PILI_RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
                                        AMFObjectProperty *p);

int PILI_RTMPSockBuf_Fill(PILI_RTMPSockBuf *sb);
int PILI_RTMPSockBuf_Send(PILI_RTMPSockBuf *sb, const char *buf, int len);
int PILI_RTMPSockBuf_Close(PILI_RTMPSockBuf *sb);

int PILI_RTMP_SendCreateStream(PILI_RTMP *r, RTMPError *error);
int PILI_RTMP_SendSeek(PILI_RTMP *r, int dTime, RTMPError *error);
int PILI_RTMP_SendServerBW(PILI_RTMP *r, RTMPError *error);
int PILI_RTMP_SendClientBW(PILI_RTMP *r, RTMPError *error);
void PILI_RTMP_DropRequest(PILI_RTMP *r, int i, int freeit);
int PILI_RTMP_Read(PILI_RTMP *r, char *buf, int size);
int PILI_RTMP_Write(PILI_RTMP *r, const char *buf, int size, RTMPError *error);

/* hashswf.c */
int PILI_RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
                      int age);

#ifdef __cplusplus
};
#endif

#endif
