/**
 * Copyright 2012 Cabot Communications.  All rights reserved.
 *
 * @created Dec 24, 2012
 * 
 * @brief [TODO: Provide a brief description about the contents of this file]
 * 
 */

#include "ice_streamer.h"
#include "ice_streamer_external_streamer.h"

#include "frost_basicstring.h"
#include "frost_mem.h"
#include "frost_str.h"
#include "frost/frost_time.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utilities_debug/cabot_assert.h"
#include "utilities_debug/trace.h"

TRACE_IMPLEMENT_LEVEL("ice/external_streamer", TRACE_LEVEL_VERBOSE);

#define EXTERNAL_STREAM_BUFFER_SIZE (1024*8*8)

static ice_streamer_handler_t * g_handle = NULL ;
static frost_bool g_seek_enable = frost_false ;

static frost_bool
ice_streamer_external_write_data(frost_uint8 *buffer, frost_uint length, frost_bool end_of_data, void *user_data)
{
    TRACE_VERBOSE(("In %s(): Buffer with length: %d, end of data: %d\n", __FUNCTION__, length, end_of_data));
    frost_bool result = frost_false;
    if (user_data)
    {
        ice_streamer_external_data_t *data = (ice_streamer_external_data_t *) user_data;
        FROST_MutexLock(data->data_mutex);
        if (circular_buffer_add(data->buffer, buffer, length) != -1)
        {
            data->offset += length;
            data->done = end_of_data;
            result = frost_true;
        }
        else
        {
            TRACE_ERROR(("In %s(): Circular buffer is full!!\n", __FUNCTION__));
        }
        FROST_MutexUnlock(data->data_mutex);

        if (buffer)
        {
            free(buffer);
        }

    }
    return result;
}

static frost_int
fill_data_buffer (ice_streamer_handler_t * handle,
                  ice_streamer_external_data_t *data,
                  ice_error_code_t *error)
{
    TRACE_INFO(("In %s(): ENTRY\n", __FUNCTION__));
    frost_return_val_if_fail (handle, -1);
    frost_return_val_if_fail (data, -1);
    frost_return_val_if_fail (error, -1);

    frost_int result = -1;
    TRACE_INFO(("In %s(): Requesting %d bytes of data\n", __FUNCTION__,  EXTERNAL_STREAM_BUFFER_SIZE));
    frost_uint8 *buffer = (frost_uint8 *) malloc(EXTERNAL_STREAM_BUFFER_SIZE);
    if (buffer)
    {
        ice_streamer_handler_need_data(buffer, EXTERNAL_STREAM_BUFFER_SIZE);
        result = 0;
    }

    return result;
}


frost_bool
ice_streamer_external_streamer_plugin_setup (ice_streamer_handler_plugin_t *plugin)
{
    g_handle = NULL; //@@++-- 20120727 g_handle must be reset so that we can get the correct handle at second play | ilters

    TRACE_INFO(("In %s(): ENTRY\n", __FUNCTION__));
    ice_streamer_handler_methods_t methods;

    memset (&methods,0,sizeof(ice_streamer_handler_methods_t));
    methods.init = ice_streamer_external_streamer_init;
    methods.destroy = ice_streamer_external_streamer_destroy;
    methods.read = ice_streamer_external_streamer_read;
    methods.seek = NULL;    // TODO: Seek is disabled

    ice_streamer_handler_plugin_methods_set (plugin, &methods);

    ice_streamer_handler_plugin_indata_add (plugin,
                                  ICE_STREAM_TYPE_URL, "external://*",
                                  ICE_STREAM_TYPE_END);

    return frost_true;
}

frost_bool
ice_streamer_external_streamer_init (ice_streamer_handler_t * handle)
{
    g_handle = NULL; //@@++-- 20120727 g_handle must be reset so that we can get the correct handle at second play | ilters

    TRACE_INFO(("In %s(): ENTRY\n", __FUNCTION__));
    ice_streamer_external_data_t *data = NULL;
    ice_error_code_t error;
    frost_int metaint = 0;
    const char *url;
    frost_bool result = frost_false;

    frost_return_val_if_fail (handle, frost_false);

    data = frost_new0 (ice_streamer_external_data_t, 1);

    frost_return_val_if_fail (data, frost_false);

    data->buffer = circular_buffer_create_new(EXTERNAL_STREAM_BUFFER_SIZE * 2);
    if (data->buffer == NULL)
    {
        frost_free(data);
        result = frost_false;
    }
    else
    {
        data->data_mutex = FROST_MutexCreate ();
        data->buffer_ready = FROST_CondVarCreate();

        ice_streamer_private_data_set(handle, data);
        ice_streamer_write_data_callback_set(ice_streamer_external_write_data, data);

        ice_streamer_handler_outdata_type_add (handle,
                                     ICE_STREAM_TYPE_MIMETYPE,
                                     "application/octet-stream",
                                     ICE_STREAM_TYPE_END);

        g_handle = handle;
        result = frost_true;
    }

    return result;
}

frost_int
ice_streamer_external_streamer_read (ice_streamer_handler_t *handle,
                                     void *buffer, frost_int len,
                                     ice_error_code_t *error)
{
    ice_streamer_external_data_t *data;
    frost_int ret;

    frost_return_val_if_fail (handle, -1);
    frost_return_val_if_fail (buffer, -1);
    frost_return_val_if_fail (error, -1);

    if(NULL == g_handle)
    {
        g_handle = handle ;
        TRACE_INFO(("g_handle = handle = 0x%x\n",g_handle));
    }

    data = (ice_streamer_external_data_t *)ice_streamer_private_data_get (handle);
    frost_return_val_if_fail (data, -1);

    TRACE_INFO(("In %s(): Read data len: %d\n", __FUNCTION__, len));

    FROST_MutexLock(data->data_mutex);
    if (data->done && circular_buffer_empty(data->buffer))
    {
        TRACE_VERBOSE(("In %s(): End of data\n", __FUNCTION__));
        FROST_MutexUnlock(data->data_mutex);
        return 0;
    }

    if (data->done != frost_true)
    {
        ret = fill_data_buffer (handle, data, error);
        if (ret < 0)
        {
            TRACE_ERROR(("In %s(): Failed to fill data buffer\n", __FUNCTION__));
            FROST_MutexUnlock(data->data_mutex);
            return ret;
        }
    }

    while(circular_buffer_empty(data->buffer) && data->done != frost_true)
    {
        // Wait for data to arrive
        FROST_MutexUnlock(data->data_mutex);
        FROST_Sleep(50);    // TODO: The correct way to do this is by using a cond-var or semaphore.
        FROST_MutexLock(data->data_mutex);
    }

    frost_int data_avail = circular_buffer_retrieve(data->buffer, (frost_uint8 *) buffer, len);
    FROST_MutexUnlock(data->data_mutex);

    TRACE_INFO(("In %s(): Returning %d bytes from buffer\n", __FUNCTION__, data_avail));
    return data_avail;
}

void
ice_streamer_external_data_free_data (ice_streamer_external_data_t *data)
{
    frost_return_if_fail (data);

    circular_buffer_destroy(data->buffer);
    FROST_MutexDestroy(data->data_mutex);
    FROST_CondVarDestroy(data->buffer_ready);

    frost_free (data);

    g_handle = NULL; //@@++-- 20120727 g_handle must be reset so that we can get the correct handle at second play | ilters
}

void
ice_streamer_external_streamer_destroy (ice_streamer_handler_t * handle)
{
    ice_streamer_external_data_t *data;

    TRACE_INFO(("In %s(): ENTRY\n", __FUNCTION__));
    frost_return_if_fail (handle);

    data = (ice_streamer_external_data_t *)ice_streamer_private_data_get (handle);
    frost_return_if_fail (data);

    ice_streamer_external_data_free_data (data);
}

frost_int64
ice_streamer_external_data_seek (ice_streamer_handler_t * handle, frost_int64 offset,
                ice_seek_mode_t whence, ice_error_code_t *error)
{
    TRACE_INFO(("  ############ ice_streamer_curl_seek  whence =%d   offset:%lld, handle: %d\n",whence, offset, handle));

    frost_int64 result = -1;
    ice_streamer_external_data_t * data = (ice_streamer_external_data_t *)ice_streamer_private_data_get(handle);

    FROST_MutexLock(data->data_mutex);

    if(g_seek_enable && g_handle && data ) //at this point whence should be ICE_SEEK_SET
    {
        ice_streamer_external_data_free_data(data);

        data->offset = offset ;

        if(ice_streamer_external_streamer_init(handle))
        {
            TRACE_INFO(("============> External data plugin reinit successfull for seek \n"));
            result = offset;
        }
        else
        {
            ice_streamer_error_set (error, ICE_ERROR_INVAL, "External Streamer init failed for seek");
            result = -1;
        }
    }
    else
    {
        ice_streamer_error_set (error, ICE_ERROR_INVAL, "Couldn't seek");
        result = -1;
    }
    FROST_MutexUnlock(data->data_mutex);

    return result;
}

void
ice_streamer_external_data_seek_enable(frost_bool enable)
{
    if(g_seek_enable != enable)
    {
        g_seek_enable = enable ;
    }
}

static const ICE_STREAMER_PLUGIN(ICE_PLUGIN_TYPE_XFORM,
                    1,
                    "External Streamer",
                    "Plugin for external data streamers",
                    "1.0",
                    "Retrieves data from external streamers like Opera",
                    (frost_bool (*)(void*)) ice_streamer_external_streamer_plugin_setup
                    );

const ice_streamer_plugin_desc_t * getExternalDataPluginDesc()
{
    return &ICE_STREAMER_PLUGIN_DESC ;
}

