/*
 * ice_streamer_disk_write_output_plugin.cpp
 *
 *  Created on: Jul 4, 2012
 *      Author: ergino
 */

#include "ice_streamer.h"

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

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

TRACE_IMPLEMENT("ice/ice_streamer_disk_write_output_plugin");

/*
 * Defines
 */
#define HEADER_SIZE 44

#define PUT_16(buf, val) do { \
    frost_uint16 tmp = frost_uint16_TO_LE (val); \
    memcpy (buf, &tmp, 2); \
    buf += 2; \
} while (0)

#define PUT_32(buf, val) do { \
    frost_uint32 tmp = frost_uint32_TO_LE (val); \
    memcpy (buf, &tmp, 4); \
    buf += 4; \
} while (0)

#define PUT_STR(buf, str) do { \
    size_t len = strlen (str); \
    strncpy ((char *) buf, str, len); \
    buf += len; \
} while (0)

/*
 * Type definitions
 */

typedef struct {
    FILE *fp;
    char destdir[ICE_STREAMER_PATH_MAX];
} ice_streamer_diskwrite_data_t;

/*
 * Function prototypes
 */

static frost_bool ice_streamer_diskwrite_new (ice_streamer_output_t *output);
static void ice_streamer_diskwrite_destroy (ice_streamer_output_t *output);
static frost_bool ice_streamer_diskwrite_open (ice_streamer_output_t *output);
static void ice_streamer_diskwrite_close (ice_streamer_output_t *output);
static void ice_streamer_diskwrite_write (ice_streamer_output_t *output, frost_pointer buffer,
                                  frost_int len, ice_error_code_t *error);
static void ice_streamer_diskwrite_flush (ice_streamer_output_t *output);
static void
ice_streamer_diskwrite_header (ice_streamer_output_t *output, frost_pointer buffer, ice_error_code_t *error);
static frost_bool
ice_streamer_diskwrite_check_progress(ice_streamer_output_t *output,ice_streamer_playback_status_t wanted_status );

static void finalize (ice_streamer_diskwrite_data_t *data);

/*
 * Plugin header
 */

frost_bool
ice_streamer_diskwrite_plugin_setup (ice_streamer_output_plugin_t *plugin)
{
    ice_streamer_output_methods_t methods;

    memset (&methods,0,sizeof(ice_streamer_output_methods_t));

    methods.create = ice_streamer_diskwrite_new;
    methods.destroy = ice_streamer_diskwrite_destroy;

    methods.open = ice_streamer_diskwrite_open;
    methods.close = ice_streamer_diskwrite_close;

    methods.flush = ice_streamer_diskwrite_flush;

    methods.write = ice_streamer_diskwrite_write;
    methods.header = ice_streamer_diskwrite_header ;
    methods.set_status = ice_streamer_diskwrite_check_progress;

    ice_streamer_output_plugin_methods_set (plugin, &methods);

    ice_streamer_output_plugin_config_property_register (plugin,
                                                 "destination_directory",
                                                 "/tmp", NULL, NULL);
    return frost_true;
}

/*
 * Member functions
 */

static frost_bool
ice_streamer_diskwrite_new (ice_streamer_output_t *output)
{
    ice_streamer_diskwrite_data_t *data;
    ice_streamer_config_property_t *val;
    const char *tmp;

    frost_return_val_if_fail (output, frost_false);

    data = frost_new (ice_streamer_diskwrite_data_t, 1);
    frost_return_val_if_fail (data, frost_false);

    ice_streamer_output_private_data_set (output, data);

    ice_streamer_output_stream_type_add (output,
                                 ICE_STREAM_TYPE_MIMETYPE, "audio/x-ffmpeg-*",
                                 ICE_STREAM_TYPE_END);

    ice_streamer_output_stream_type_add (output,
                                 ICE_STREAM_TYPE_MIMETYPE, "video/x-h264",
                                 ICE_STREAM_TYPE_END);

    ice_streamer_output_stream_type_add (output,
                                 ICE_STREAM_TYPE_MIMETYPE, "application/octet-stream",
                                 ICE_STREAM_TYPE_END);

    frost_snprintf (data->destdir, sizeof (data->destdir), "%s/out_%d.bin", "/tmp", output->source_id);

//    ice_streamer_object_connect (ICEST_OBJECT (output),
//                         ICE_IPC_SIGNAL_PLAYBACK_CURRENTID,
//                         on_playlist_entry_changed,
//                         data);


    return frost_true;
}

static void
ice_streamer_diskwrite_destroy (ice_streamer_output_t *output)
{
    frost_pointer data;

    frost_return_if_fail (output);

    data = (ice_streamer_diskwrite_data_t *)ice_streamer_output_private_data_get (output);

    frost_free (data);
}

static frost_bool
ice_streamer_diskwrite_check_progress(ice_streamer_output_t *output,ice_streamer_playback_status_t wanted_status )
{
    ice_streamer_diskwrite_data_t *data;
    frost_uint i;

    frost_return_val_if_fail (output,frost_false);
    frost_return_val_if_fail (wanted_status,frost_false);
    data = (ice_streamer_diskwrite_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_val_if_fail (data,frost_false);

//    wanted_status = ICE_PLAYBACK_STATUS_PLAY ;

    return frost_true ;
}

static frost_bool
ice_streamer_diskwrite_open (ice_streamer_output_t *output)
{
    ice_streamer_diskwrite_data_t *data;
    frost_int ret;

    frost_return_val_if_fail (output, frost_false);

    data = (ice_streamer_diskwrite_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_val_if_fail (data, frost_false);

    /* create the destination directory if it doesn't exist yet */
    if (data->fp = fopen(data->destdir, "wb"))
    {
        TRACE_INFO(("fopen successfully \n"));
    }

    if (ret == -1) {
        TRACE_ERROR( ("errno (%d) %s", errno, strerror (errno)));
        return frost_false;
    } else {
        return frost_true;
    }
}

static void
ice_streamer_diskwrite_close (ice_streamer_output_t *output)
{
    ice_streamer_diskwrite_data_t *data;

    frost_return_if_fail (output);

    data = (ice_streamer_diskwrite_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_if_fail (data);

    if (data->fp) {
//        finalize (data);
        fclose (data->fp);
        data->fp = NULL;
    }
}

static void
ice_streamer_diskwrite_write (ice_streamer_output_t *output, frost_pointer buffer, frost_int len,
                      ice_error_code_t *error)
{
    ice_streamer_diskwrite_data_t *data;

    frost_return_if_fail (output);
    frost_return_if_fail (buffer);
    frost_return_if_fail (len > 0);
    data = (ice_streamer_diskwrite_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_if_fail (data);
    frost_return_if_fail (data->fp);


    if(len)
    {
        fwrite (buffer, sizeof (char), len, data->fp);
        sync();
    }
}

static void
ice_streamer_diskwrite_header (ice_streamer_output_t *output, frost_pointer buffer,
                      ice_error_code_t *error)
{
    ice_streamer_diskwrite_data_t *data;

    frost_return_if_fail (output);
    frost_return_if_fail (buffer);
    data = (ice_streamer_diskwrite_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_if_fail (data);
    frost_return_if_fail (data->fp);
    ice_streamer_asf_parsed_header_t  * header = (ice_streamer_asf_parsed_header_t  *) buffer;

    if(header->header_size)
    {
        fwrite (header->header, sizeof (char), header->header_size, data->fp);
        sync();
    }
}

static void
ice_streamer_diskwrite_flush (ice_streamer_output_t *output)
{
    ice_streamer_diskwrite_data_t *data;

    frost_return_if_fail (output);

    data = (ice_streamer_diskwrite_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_if_fail (data);
    frost_return_if_fail (data->fp);

    fflush (data->fp);
}

static void
finalize (ice_streamer_diskwrite_data_t *data)
{
    long pos;
    frost_int8 hdr[HEADER_SIZE];
    frost_int8 *ptr = hdr;

    //TODO: Get header from input

    fseek (data->fp, 0, SEEK_SET);
    fwrite (hdr, sizeof (frost_uint8), HEADER_SIZE, data->fp);
    sync();
}

ICE_STREAMER_OUTPUT_BUILTIN (diskwrite,
                    "Diskwriter Output",
                    ICE_STREAMER_VERSION,
                    "Dumps audio data to disk",
                    ice_streamer_diskwrite_plugin_setup);

const ice_streamer_plugin_desc_t * getDiskWritePluginDesc()
{
    return &ice_streamer_builtin_diskwrite ;
}
