
#include "ice_streamer.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

TRACE_IMPLEMENT("ice/ice_streamer_asf");

//#define DUMP_DATA 1
#ifdef DUMP_DATA
    #define TRACE_DUMP_INFO(x)  printf x
#else
    #define TRACE_DUMP_INFO(x)
#endif

extern "C"void ICE_MediaPlayerGetStreamerFileDetails( const ICE_MediaPlayerFileDetails * streamer_file_details);
ice_streamer_handler_t g_ice_streamer_asf_handle ;

int32_t ice_streamer_asf_read_callback (void *opaque, void *buffer, int32_t size);
int64_t ice_streamer_asf_seek_callback (void *opaque, int64_t position);
frost_int ice_streamer_asf_get_track (ice_streamer_handler_t * handle, asf_file_t *file);

b_asf_parser_packet_data_callback asf_parser_packet_data_cb;
void* asf_data_cb_context;


void b_asf_parser_register_data_cb( void* callback, void* user_data)
{
    asf_parser_packet_data_cb = (b_asf_parser_packet_data_callback) callback ;
    asf_data_cb_context = user_data;
}


ice_streamer_asf_parsed_header_t g_header ;

void ice_streamer_header_callback(char * part ,  int size)
{

}

ice_streamer_asf_parsed_header_t * ice_streamer_get_header_from_asf(void)
{
    return &g_header ;
}

frost_bool
ice_streamer_asf_plugin_setup (ice_streamer_handler_plugin_t * handle)
{
    ice_streamer_handler_methods_t methods;

    memset (&methods,0,sizeof(ice_streamer_handler_methods_t));
    methods.init = ice_streamer_asf_init;
    methods.destroy = ice_streamer_asf_destroy;
    methods.read = ice_streamer_asf_read;
    methods.seek = ice_streamer_asf_seek;
    memset (&g_header,0,sizeof(ice_streamer_asf_parsed_header_t));
    
    ice_streamer_handler_plugin_methods_set (handle, &methods);

    ice_streamer_handler_plugin_indata_add (handle,
                                  ICE_STREAM_TYPE_MIMETYPE,
                                  "video/x-ms-asf",
                                  0);

    ice_streamer_magic_add ("asf header",
                            "video/x-ms-asf",
                             "0 belong 0x3026b275",
                             NULL);
}

void
ice_streamer_asf_destroy (ice_streamer_handler_t * handle)
{
    ice_streamer_asf_data_t *data;

    frost_return_if_fail (handle);

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

    frost_string_free (data->outbuf, frost_true);
    asf_packet_destroy (data->packet);
    frost_free ((void*)data);
}

void
ice_streamer_asf_get_raw_header_from_curl(asf_file_t * file)
{
    g_header.header_size = asf_get_header_size(file);

    g_header.header = ice_streamer_curl_get_header();

    g_header.asf_data_object = asf_get_data_object(file);
}

frost_bool
ice_streamer_asf_init (ice_streamer_handler_t * handle)
{
    ice_streamer_asf_data_t *data;
    asf_iostream_t stream;
    frost_int ret;
    ice_streamer_config_property_t *prop;

    frost_return_val_if_fail (handle, frost_false);

    data = frost_new0 (ice_streamer_asf_data_t, 1);
    data->audio_track = -1 ;
    data->video_track = -1 ;

    prop = ice_streamer_config_lookup ("input.read.format.is.streaming");

    if(ice_streamer_config_property_get_int (prop) || prop == NULL)
    {
        TRACE_INFO(("Asf is using Curl Streaming \n"));
        stream.read = ice_streamer_asf_read_callback;
        stream.write = NULL;
        stream.seek = ice_streamer_asf_seek_callback;
        stream.opaque = handle;
        stream.header = ice_streamer_header_callback ;
        data->file = asf_open_cb (&stream);
    }
    else
    {
        TRACE_INFO(("Asf is using File accessing \n"));
        if((prop = ice_streamer_config_lookup ("input.file")) != NULL)
        {
            data->file = asf_open_file(ice_streamer_config_property_get_string(prop)) ;
        }
    }

    if (!data->file) {
        frost_free ((void*)data);
        return frost_false;
    }
    data->packet = asf_packet_create ();
    data->outbuf = frost_string_new (NULL);

    ice_streamer_private_data_set (handle, data);

    ret = asf_init (data->file);
    if (ret < 0) {
        TRACE_ERROR(("ASF parser failed to init with error %d", ret));
        asf_packet_destroy (data->packet);
        asf_close (data->file);

        return frost_false;
    }

    ice_streamer_asf_get_raw_header_from_curl(data->file);

    set_packet_size(asf_get_packet_size(data->file)) ;

    ice_streamer_asf_get_track (handle, data->file);
    if (data->audio_track < 0 &&
        data->video_track < 0) {
        TRACE_ERROR (("No audio or video track found"));
        asf_packet_destroy (data->packet);
        asf_close (data->file);

        return frost_false;
    }

    
    ICE_MediaPlayerFileDetails media_details;
    frost_bool media_details_present = ice_streamer_asf_get_mediainfo(handle, &media_details);
                 
    TRACE_INFO(("ASF demuxer inited successfully!  media_details->duration_sec:[%d] \n", media_details.duration_sec));
    if(media_details_present){
    ICE_MediaPlayerGetStreamerFileDetails(&media_details);}

    TRACE_INFO(("ASF demuxer inited successfully!"));
    
    return frost_true;
}

frost_int
ice_streamer_asf_read (ice_streamer_handler_t * handle, ice_sample_t *buf, frost_int len, ice_error_code_t *err)
{
    ice_streamer_asf_data_t *data;
    frost_uint size = 0;
    frost_uint copy_size = 0;
    TRACE_INFO(("================> (%s,%d) \n",__FUNCTION__,__LINE__));

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

    size = MIN (data->outbuf->len, len);
    while (size == 0) {
        frost_int i, ret;

        ret = asf_get_packet (data->file, data->packet);
        if (ret < 0) {
            return -1;
        } else if (ret == 0) {
            TRACE_ERROR (("ASF EOF"));
            return 0;
        }

        if(data->audio_track < 0 &&
                data->video_track < 0)
        {
            continue ;
        }

//        frost_string_append_len(data->outbuf,(const char *)data->packet->data,ret);
        ice_streamer_handler_auxdata_barrier (handle);
        if(ret)
        {
            if(asf_parser_packet_data_cb)
            {
                (*asf_parser_packet_data_cb)(asf_data_cb_context, data->packet->data , (int*)&ret) ;
            }
        }
        size = MIN (ret, len);
    }

    TRACE_DUMP_INFO(("===============> send_time: %d , sequence: %d , duration: %d  size: %d \n",data->packet->send_time,data->packet->sequence,data->packet->duration,size));
    memcpy (buf,data->packet->data, size);

#ifdef DUMP_DATA
    {
        frost_uint16 k = 0;
        TRACE_DUMP_INFO( ( "=================> Packet data which is come from Curl:\r\n" ) );
        for ( k = 0; k < 128; k++ )
        {
            TRACE_DUMP_INFO( ( "%02X ", *( data->packet->data + k ) ) );
            if ( 0 == ( ( k + 1 ) & 0x000F ) )
                TRACE_DUMP_INFO( ( "\r\n" ) );
        }
        TRACE_DUMP_INFO( ( "\r\n" ) );
    }
#endif
//    frost_string_erase (data->outbuf, 0, data->outbuf->len);


    return size;
}

frost_int64
ice_streamer_asf_seek (ice_streamer_handler_t * handle, frost_int64 samples, ice_seek_mode_t whence, ice_error_code_t *err)
{
    int64_t position;
    ice_streamer_asf_data_t *data;
    TRACE_INFO(("================> (%s,%d) \n",__FUNCTION__,__LINE__));

    frost_return_val_if_fail (whence == ICE_SEEK_SET, -1);
    frost_return_val_if_fail (handle, -1);

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

    /*    
    if (data->audio_data.samplerate <= 0) {
        return -1;
    }
    */
    position = asf_seek_to_msec (data->file, samples );
    
    TRACE_INFO (("Seeking returned with %d \n", position));

    if (position < 0) {
        return -1;
    }

    frost_string_erase (data->outbuf, 0, data->outbuf->len);

    return position ;
}


frost_bool
ice_streamer_asf_get_mediainfo (ice_streamer_handler_t * handle, ICE_MediaPlayerFileDetails * media_details)
{
    ice_streamer_asf_data_t *data;
    asf_metadata_t *metadata;
    uint64_t tmp;
    frost_uint8 *track = NULL;
    frost_int i;
    
    frost_return_val_if_fail(handle, frost_false);
    data = (ice_streamer_asf_data_t *)ice_streamer_private_data_get (handle);
    frost_return_val_if_fail(data, frost_false);
    
    if ((tmp = asf_get_duration (data->file)) > 0) {
        media_details->duration_sec = tmp/10000000 ;
                 
        if(tmp)
            {
        ice_streamer_handler_metadata_set_int (handle,
                                     ICE_MEDIALIB_ENTRY_PROPERTY_DURATION,
                                     tmp/10000000);
            }
        
    }

    if ((tmp = asf_get_max_bitrate (data->file)) > 0) {
        media_details->bit_rate = tmp ;
        ice_streamer_handler_metadata_set_int (handle,
                                     ICE_MEDIALIB_ENTRY_PROPERTY_BITRATE,
                                     tmp);
    }

    metadata = asf_header_get_metadata (data->file);
    if (!metadata) {
        TRACE_ERROR( ("No metadata object found in the file"));
        return frost_false;
    }

    if (metadata->title && metadata->title[0]) {
        media_details->title = metadata->title ; //TODO: should be copied
        ice_streamer_handler_metadata_set_str (handle,
                                     ICE_MEDIALIB_ENTRY_PROPERTY_TITLE,
                                     metadata->title);
    }

    if (metadata->artist && metadata->artist[0]) {
        media_details->artist = metadata->artist ; //TODO: should be copied
        ice_streamer_handler_metadata_set_str (handle,
                                     ICE_MEDIALIB_ENTRY_PROPERTY_ARTIST,
                                     metadata->artist);
    }

    if (metadata->description && metadata->description[0]) {
        media_details->description = metadata->description ; //TODO: should be copied
        ice_streamer_handler_metadata_set_str (handle,
                                     ICE_MEDIALIB_ENTRY_PROPERTY_COMMENT,
                                     metadata->description);
    }

    for (i=0; i<metadata->extended_count; i++) {
        char *key, *value;

        key = metadata->extended[i].key;
        value = metadata->extended[i].value;

        if (key == NULL || value == NULL || !strlen (value)) {
            continue;
        } else if (!strcmp (key, "WM/AlbumTitle")) {
               media_details->album = value ; //TODO: should be copied
               ice_streamer_handler_metadata_set_str (handle,
                                            ICE_MEDIALIB_ENTRY_PROPERTY_ALBUM,
                                            value);
        } else if (!strcmp (key, "WM/Year")) {
            media_details->year = value ; //TODO: should be copied
            ice_streamer_handler_metadata_set_str (handle,
                                         ICE_MEDIALIB_ENTRY_PROPERTY_YEAR,
                                         value);
        } else if (!strcmp (key, "WM/Genre")) {
            media_details->genre = value ; //TODO: should be copied
            ice_streamer_handler_metadata_set_str (handle,
                                         ICE_MEDIALIB_ENTRY_PROPERTY_GENRE,
                                         value);
        } else if ((!track && !strcmp (key, "WM/Track")) || !strcmp (key, "WM/TrackNumber")) {
            /* WM/TrackNumber overrides WM/Track value as specified in the Microsoft
             * documentation at http://msdn2.microsoft.com/en-us/library/aa392014.aspx */
            track = (frost_uint8 *)value;
        } else if (!strcmp (key, "MusicBrainz/Album Id")) {
//            media_details->album_id = value ; //TODO: should be copied
            ice_streamer_handler_metadata_set_str (handle,
                                         ICE_MEDIALIB_ENTRY_PROPERTY_ALBUM_ID,
                                         value);
        } else if (!strcmp (key, "MusicBrainz/Artist Id")) {
//            media_details->artist_id = value ; //TODO: should be copied
            ice_streamer_handler_metadata_set_str (handle,
                                         ICE_MEDIALIB_ENTRY_PROPERTY_ARTIST_ID,
                                         value);
        } else if (!strcmp (key, "MusicBrainz/Track Id")) {
//            media_details->track_id = value ; //TODO: should be copied
            ice_streamer_handler_metadata_set_str (handle,
                                         ICE_MEDIALIB_ENTRY_PROPERTY_TRACK_ID,
                                         value);
        }
    }

    if (track) {
        frost_int tracknr;
        frost_uint8 *end;

        if (end && *end == '\0') {
            media_details->number_of_streams = strtol ((char*)track, (char**)&end, 10);
            ice_streamer_handler_metadata_set_int (handle,
                                         ICE_MEDIALIB_ENTRY_PROPERTY_TRACKNR,
                                         tracknr);
        }
    }
    
    asf_metadata_destroy (metadata);
        
    return frost_true;

}


int32_t
ice_streamer_asf_read_callback (void *opaque, void *buffer, int32_t size)
{
    ice_streamer_handler_t * handle;
    ice_streamer_asf_data_t *data;
    ice_error_code_t error;
    frost_int ret;
    TRACE_INFO(("================> (%s,%d) \n",__FUNCTION__,__LINE__));

    frost_return_val_if_fail (opaque, 0);
    frost_return_val_if_fail (buffer, 0);
    handle = (ice_streamer_handler_t *)opaque;

    ice_streamer_error_reset (&error);

    data = (ice_streamer_asf_data_t *)ice_streamer_private_data_get (handle);
    frost_return_val_if_fail (data, 0);

    ret = ice_streamer_handler_read (handle, buffer, (frost_int)size, &error);

    return ret;
}

int64_t
ice_streamer_asf_seek_callback (void *opaque, int64_t position)
{
    ice_streamer_handler_t * handle;
    ice_streamer_asf_data_t *data;
    ice_error_code_t error;
    frost_int ret;

    frost_return_val_if_fail (opaque, -1);
    handle = (ice_streamer_handler_t *)opaque;

    ice_streamer_error_reset (&error);

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

    ret = ice_streamer_handler_seek(handle, position, ICE_SEEK_SET, &error);

    return ret;
}

frost_int
ice_streamer_asf_get_track (ice_streamer_handler_t * handle, asf_file_t *file)
{
    ice_streamer_asf_data_t *data;
    uint8_t stream_count;
    frost_int i;

    frost_return_val_if_fail (handle, -1);

    data = (ice_streamer_asf_data_t *)ice_streamer_private_data_get (handle);

    frost_return_val_if_fail (data, -1);

    stream_count = asf_get_stream_count (file);
    handle->streams = (ICE_MediaPlayerStreamDetails**) malloc(sizeof(ICE_MediaPlayerStreamDetails *)*stream_count);

    for (i=1; i <= stream_count; i++) {
        handle->streams[i] = (ICE_MediaPlayerStreamDetails*) malloc(sizeof(ICE_MediaPlayerStreamDetails ));
        memset(handle->streams[i],0,sizeof(ICE_MediaPlayerStreamDetails ));
        asf_stream_t *stream = asf_get_stream (file, i);
        if (stream->type == ASF_STREAM_TYPE_AUDIO &&
            !(stream->flags & ASF_STREAM_FLAG_HIDDEN)) {
            asf_waveformatex_t *wfx = (asf_waveformatex_t *)stream->properties;
            const char *mimetype;

            if (wfx->wFormatTag == 0x160)
            {
                mimetype = "audio/x-ffmpeg-wmav1";
//                handle->streams[i]->codec = E_VDP_CODEC_ID_WMAV1 ;
            }
            else if (wfx->wFormatTag == 0x161)
            {
                mimetype = "audio/x-ffmpeg-wmav2";
//                handle->streams[i]->codec = E_VDP_CODEC_ID_WMAV2 ;
            }
            else
                continue;

            data->audio_data.samplerate = wfx->nSamplesPerSec;
            data->audio_data.channels = wfx->nChannels;
            data->audio_data.bitrate = wfx->nAvgBytesPerSec * 8;

            handle->streams[i]->type = ice_media_player_stream_type_audio ;
            handle->streams[i]->index = i ;

            if(wfx->data)
            ice_streamer_handler_auxdata_set_bin (handle,
                                        "decoder_config",
                                        wfx->data,
                                        wfx->cbSize);

            ice_streamer_handler_auxdata_set_int (handle,
                                        "block_align",
                                        wfx->nBlockAlign);

            ice_streamer_handler_auxdata_set_int (handle,
                                        "bitrate",
                                        data->audio_data.bitrate);

            ice_streamer_handler_outdata_type_add (handle,
                                         ICE_STREAM_TYPE_MIMETYPE,
                                         mimetype,
                                         ICE_STREAM_TYPE_END);

            data->audio_track =  i;

        }
        else if (stream->type == ASF_STREAM_TYPE_VIDEO &&
                !(stream->flags & ASF_STREAM_FLAG_HIDDEN))
        {
            asf_bitmapinfoheader_t *bmih = (asf_bitmapinfoheader_t *)stream->properties;


            asf_waveformatex_t *wfx = (asf_waveformatex_t *)stream->properties;
            const char *mimetype;

            TRACE_INFO (("===============>            bmih->biSize :%d , " \
                        " bmih->biWidth :%d , " \
                        " bmih->biHeight :%d , " \
                        " bmih->biPlanes :%d , " \
                        " bmih->biBitCount :%d , " \
                        " bmih->biCompression :%d , " \
                        " bmih->biSizeImage :%d , " \
                        " bmih->biXPelsPerMeter :%d , " \
                        " bmih->biYPelsPerMeter :%d , " \
                        " bmih->biClrUsed :%d , " \
                        " bmih->biClrImportant :%d , " \
                        " bmih->data  \n",
                        bmih->biSize ,
                        bmih->biWidth ,
                        bmih->biHeight ,
                        bmih->biPlanes ,
                        bmih->biBitCount ,
                        bmih->biCompression ,
                        bmih->biSizeImage ,
                        bmih->biXPelsPerMeter ,
                        bmih->biYPelsPerMeter ,
                        bmih->biClrUsed ,
                        bmih->biClrImportant ,
                        bmih->data ));

            mimetype = "video/x-h264" ;

            if(bmih->data)
            ice_streamer_handler_auxdata_set_bin (handle,
                                        "decoder_config",
                                        bmih->data,
                                        bmih->biSize);

            ice_streamer_handler_outdata_type_add (handle,
                                        ICE_STREAM_TYPE_MIMETYPE,
                                         mimetype,
                                         ICE_STREAM_TYPE_END);


            data->video_track = i;
        }
    }

    return data->video_track ;
}


ICE_STREAMER_XFORM_BUILTIN (asf,
                           "ASF Demuxer",
                           ICE_STREAMER_VERSION,
                           "Advanced Systems Format demuxer",
                           ice_streamer_asf_plugin_setup);

const ice_streamer_plugin_desc_t * getAsfPluginDesc()
{
    return &ice_streamer_builtin_asf ;
}
