#include "sockutil.h"
#include "sys/system.h"
#include "rtmp-client.h"
#include "flv-reader.h"
#include "flv-proto.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

//#define CORRUPT_RTMP_CHUNK_DATA

#if defined(CORRUPT_RTMP_CHUNK_DATA)
static void rtmp_corrupt_data(const void* data, size_t bytes)
{
    static unsigned int seed;
    if (0 == seed)
    {
        seed = (unsigned int)time(NULL);
        srand(seed);
    }

    if (bytes < 1)
        return;

    //size_t i = bytes > 20 ? 20 : bytes;
    //i = rand() % i;

    //uint8_t v = ((uint8_t*)data)[i];
    //((uint8_t*)data)[i] = rand() % 255;
    //printf("rtmp_corrupt_data[%d] %d == %d\n", i, (int)v, (int)((uint8_t*)data)[i]);
 
    if (5 == rand() % 10)
    {
        size_t i = rand() % bytes;
        uint8_t v = ((uint8_t*)data)[i];
        ((uint8_t*)data)[i] = rand() % 255;
        printf("rtmp_corrupt_data[%d] %d == %d\n", i, (int)v, (int)((uint8_t*)data)[i]);
    }
}

static uint8_t s_buffer[4 * 1024 * 1024];
static size_t s_offset;
static FILE* s_fp;
static void fwritepacket(uint32_t timestamp)
{
    assert(4 == fwrite(&s_offset, 1, 4, s_fp));
    assert(4 == fwrite(&timestamp, 1, 4, s_fp));
    assert(s_offset == fwrite(s_buffer, 1, s_offset, s_fp));
    s_offset = 0;
}
#endif

static int rtmp_client_send(void* param, const void* header, size_t len, const void* data, size_t bytes)
{
	socket_t* socket = (socket_t*)param;
	socket_bufvec_t vec[2];
	socket_setbufvec(vec, 0, (void*)header, len);
	socket_setbufvec(vec, 1, (void*)data, bytes);

#if defined(CORRUPT_RTMP_CHUNK_DATA)
	//if (len > 0)
	//{
	//    assert(s_offset + len < sizeof(s_buffer));
	//    memcpy(s_buffer + s_offset, header, len);
	//    s_offset += len;
	//}
	//if (bytes > 0)
	//{
	//    assert(s_offset + bytes < sizeof(s_buffer));
	//    memcpy(s_buffer + s_offset, data, bytes);
	//    s_offset += bytes;
	//}
	
	rtmp_corrupt_data(header, len);
    rtmp_corrupt_data(data, bytes);
#endif
	return socket_send_v_all_by_time(*socket, vec, bytes > 0 ? 2 : 1, 0, 5000);
}

static void rtmp_client_push(const char* flv, rtmp_client_t* rtmp)
{
	int r, type;
	int avcrecord = 0;
    int aacconfig = 0;
	size_t taglen;
	uint32_t timestamp;
	uint32_t s_timestamp = 0;
	uint32_t diff = 0;
	uint32_t clock;
	
	static char packet[2 * 1024 * 1024];
	while (1)
	{
		void* f = flv_reader_create(flv);

		clock = system_clock(); // timestamp start from 0
		while (1 == flv_reader_read(f, &type, &timestamp, &taglen, packet, sizeof(packet)))
		{
			uint32_t t = system_clock();
			if (clock + timestamp > t && clock + timestamp < t + 3 * 1000) // dts skip
				system_sleep(clock + timestamp - t);
			else if (clock + timestamp > t + 3 * 1000)
				clock = t - timestamp;
			
			timestamp += diff;
			s_timestamp = timestamp > s_timestamp ? timestamp : s_timestamp;

			if (FLV_TYPE_AUDIO == type)
			{
                if (0 == packet[1])
                {
                    if(0 != aacconfig)
                        continue;
                    aacconfig = 1;
                }
				r = rtmp_client_push_audio(rtmp, packet, taglen, timestamp);
			}
			else if (FLV_TYPE_VIDEO == type)
			{
                if (0 == packet[1] || 2 == packet[1])
                {
                    if (0 != avcrecord)
                        continue;
                    avcrecord = 1;
                }
				//bool keyframe = 1 == ((packet[0] & 0xF0) >> 4);
				//printf("timestamp: %u, s_timestamp: %u%s\n", timestamp, s_timestamp, keyframe ? " [I]" : "");
				//if (timestamp > 10 * 1000 && keyframe)
				//{
				//	uint8_t header[5];
				//	header[0] = (1 << 4) /* FrameType */ | 7 /* AVC */;
				//	header[1] = 2; // AVC end of sequence
				//	header[2] = 0;
				//	header[3] = 0;
				//	header[4] = 0;
				//	r = rtmp_client_push_video(rtmp, header, 5, timestamp);
				//	system_sleep(600 * 1000);
				//}
				r = rtmp_client_push_video(rtmp, packet, taglen, timestamp);
			}
			else if (FLV_TYPE_SCRIPT == type)
			{
				r = rtmp_client_push_script(rtmp, packet, taglen, timestamp);
			}
			else
			{
				assert(0);
				r = 0; // ignore
			}

			if (0 != r)
			{
				assert(0);
				break; // TODO: handle send failed
			}
		}

		flv_reader_destroy(f);

		diff = s_timestamp + 30;
	}

EXIT:
	return;
}

// rtmp://video-center.alivecdn.com/live/hello?vhost=your.domain
// rtmp_publish_test("video-center.alivecdn.com", "live", "hello?vhost=your.domain", local-flv-file-name)
void rtmp_publish_test(const char* host, const char* app, const char* stream, const char* flv)
{
	static char packet[2 * 1024 * 1024];
	snprintf(packet, sizeof(packet), "rtmp://%s/%s", host, app); // tcurl

	struct rtmp_client_handler_t handler;
	memset(&handler, 0, sizeof(handler));
	handler.send = rtmp_client_send;

	socket_init();
	socket_t socket = socket_connect_host(host, 1935, 2000);
	socket_setnonblock(socket, 0);

	rtmp_client_t* rtmp = rtmp_client_create(app, stream, packet/*tcurl*/, &socket, &handler);
	int r = rtmp_client_start(rtmp, 0);

	while (4 != rtmp_client_getstate(rtmp) && (r = socket_recv(socket, packet, sizeof(packet), 0)) > 0)
	{
		assert(0 == rtmp_client_input(rtmp, packet, r));
	}

	rtmp_client_push(flv, rtmp);

	rtmp_client_destroy(rtmp);
	socket_close(socket);
	socket_cleanup();
}
