/*
 * ice_streamer_mstar_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_list.h"
#include "frost/frost_time.h"
#include "frost_str.h"

#include "MsTypes.h"
#include "mapi_mmap.h"
#include "mbrg_videoplayer_T4.h"
#include "mapi_streaming_player.h"
#include "asfint.h"
#include <pthread.h>

TRACE_IMPLEMENT("ice/ice_streamer_mstar_output_plugin");

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

//pthread_mutex_t schedule_mutex = PTHREAD_MUTEX_INITIALIZER ;
static frost_bool g_flush_working = frost_false;

//#define ICE_STREAMER_MSTAR_DBG

#define STREAMER_MSTAR_ERROR(fmt...)  do {printf("ICE_STREAMER ERROR: %s(), %d", __FUNCTION__, __LINE__); printf(fmt);} while (0)

#ifdef ICE_STREAMER_MSTAR_DBG
#define STREAMER_MSTAR_TRACE(fmt...)  do {printf("ICE_STREAMER TRACE: %s(), %d", __FUNCTION__, __LINE__); printf(fmt);} while (0)
#define STREAMER_MSTAR_INFO(fmt...)  do {printf("ICE_STREAMER INFO: %s(), %d ", __FUNCTION__, __LINE__); printf(fmt);} while (0)
#else
#define STREAMER_MSTAR_TRACE(fmt...)
#define STREAMER_MSTAR_INFO(fmt...)
#endif
#define ASF_DBUF_SIZE       6000*1024
DBUF *is_ibuff = NULL; //it will be linked from mbrg_videoplayer_T4.cpp
extern char *Is_MediaCharBuf;
extern volatile frost_bool gb_flush_start;
extern volatile frost_bool gb_flush_stop;

/*
 * Plugin header
 */

typedef struct ice_streamer_mstar_data_t
{
#ifdef DUMP_DATA_TO_FILE
    FILE *fp;
    char destdir[ICE_STREAMER_PATH_MAX];
#endif
    //TODO: this part should be implemented by mstar
} ice_streamer_mstar_data_t;

static frost_bool ice_streamer_mstar_plugin_setup (ice_streamer_output_plugin_t *plugin);
static frost_bool ice_streamer_mstar_new (ice_streamer_output_t *output);
static void ice_streamer_mstar_destroy (ice_streamer_output_t *output);
static frost_bool ice_streamer_mstar_open (ice_streamer_output_t *output);
static void ice_streamer_mstar_close (ice_streamer_output_t *output);
static void ice_streamer_mstar_write (ice_streamer_output_t *output, frost_pointer buffer,
                                  frost_int len, ice_error_code_t *error);
static void ice_streamer_mstar_flush (ice_streamer_output_t *output);
static void
ice_streamer_mstar_header (ice_streamer_output_t *output, frost_pointer buffer, ice_error_code_t *error);
static frost_bool
ice_streamer_mstar_check_progress(ice_streamer_output_t *output,ice_streamer_playback_status_t wanted_status );

MS_BOOL MApp_HeaderModification(unsigned char *buffer, int size)
{
    MS_U32 counter1 = 0, counter2 = 0;
    MS_U32 codec_offset = 0, real_codec_offset = 0;
    frost_bool bfound = frost_false;

    do
    {
        if ((buffer[counter1] == 'P') && (buffer[counter1+1] == 'R') && (buffer[counter1+2] == 'D') && (buffer[counter1+3] == 'Y'))
        {
            printf("Found PRDY Keyword\n");
            codec_offset = counter1 - 16;
            //printf("codec_offset = %X\n", codec_offset);
            real_codec_offset = (MS_U32) buffer[codec_offset] +(((MS_U32) buffer[codec_offset+1])<<8)+(((MS_U32) buffer[codec_offset+2])<<16)+(((MS_U32) buffer[codec_offset+3])<<24) + 7 - 11 + codec_offset;
            //printf("real_codec_offset = %X\n", real_codec_offset);
            buffer[counter1] = buffer[real_codec_offset];
            buffer[counter1+1] = buffer[real_codec_offset+1];
            buffer[counter1+2] = buffer[real_codec_offset+2];
            buffer[counter1+3] = buffer[real_codec_offset+3];
            //printf("buffer[codec_offset] = %X\n", buffer[real_codec_offset]);
            //printf("buffer[codec_offset+1] = %X\n", buffer[real_codec_offset+1]);
            //printf("buffer[codec_offset+2] = %X\n", buffer[real_codec_offset+2]);
            //printf("buffer[codec_offset+3] = %X\n", buffer[real_codec_offset+3]);

            do
            {

                if ((buffer[counter2] == 0x79) && (buffer[counter2+1] == 0xF0) && (buffer[counter2+2] == 0x04) && (buffer[counter2+3] == 0x9A) && (buffer[counter2+4] == 0x40) &&
                    (buffer[counter2+5] == 0x98) && (buffer[counter2+6] == 0x86) && (buffer[counter2+7] == 0x42) && (buffer[counter2+8] == 0xAB) && (buffer[counter2+9] == 0x92) &&
                    (buffer[counter2+10] == 0xE6) && (buffer[counter2+11] == 0x5B) && (buffer[counter2+12] == 0xE0) && (buffer[counter2+13] == 0x88) &&
                    (buffer[counter2+14] == 0x5F) && (buffer[counter2+15] == 0x95))
                {
                    buffer[counter2] = 0; buffer[counter2+1] = 0; buffer[counter2+2] = 0; buffer[counter2+3] = 0; buffer[counter2+4] = 0; buffer[counter2+5] = 0;
                    buffer[counter2+6] = 0; buffer[counter2+7] = 0; buffer[counter2+8] = 0; buffer[counter2+9] = 0; buffer[counter2+10] = 0; buffer[counter2+11] = 0;
                    buffer[counter2+12] = 0; buffer[counter2+13] = 0; buffer[counter2+14] = 0; buffer[counter2+15] = 0;
                    printf("clear specify header section\n");
                    break;
                }
                //printf("%d:[0x%x]\n", counter2, buffer[counter2]);
                counter2 += 1;
            }while ((counter2+16)<size);

            bfound = frost_true;
            break;
        }
        else
        {
            bfound = frost_false;
        }
        counter1 += 1;
    }while ((counter1+4)<size);

    return bfound;
}

static frost_bool
ice_streamer_mstar_plugin_setup (ice_streamer_output_plugin_t *plugin)
{
    ice_streamer_output_methods_t methods;
    STREAMER_MSTAR_TRACE("[ice_streamer_mstar_plugin_setup]\n");
    memset (&methods,0,sizeof(ice_streamer_output_methods_t));

    methods.create = ice_streamer_mstar_new;
    methods.destroy = ice_streamer_mstar_destroy;

    methods.open = ice_streamer_mstar_open;
    methods.close = ice_streamer_mstar_close;

    methods.flush = ice_streamer_mstar_flush;

    methods.write = ice_streamer_mstar_write;
    methods.header = ice_streamer_mstar_header ;

    methods.set_status = ice_streamer_mstar_check_progress;

    ice_streamer_output_plugin_methods_set (plugin, &methods);

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

/*
 * Member functions
 */

/**
     * Initiate the decoder.
     *
     * This function should setup everything that is required to function
     * once the output device is opened for playback. This may for example
     * include probing for usable video formats (#ice_streamer_output_format_add)
     * and setting up the mixer. Blocking calls in this function are to be
     * avoided as far as possible as this would freeze the startup of ice_streamer.
     *
     * @param output an output object
     * @return TRUE on successful init, otherwise frost_false
     */
static frost_bool
ice_streamer_mstar_new (ice_streamer_output_t *output)
{
    ice_streamer_mstar_data_t *data;
    const char *tmp;
    STREAMER_MSTAR_TRACE("[ice_streamer_mstar_new]\n");
    frost_return_val_if_fail (output, frost_false);

    data = frost_new (ice_streamer_mstar_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);

#ifdef DUMP_DATA_TO_FILE
    frost_snprintf (data->destdir, sizeof (data->destdir), "%s/out.bin", "/mnt/sda1");
#endif

    return frost_true;
}

/**
     * Destroy the output plugin.
     *
     * Tear down the data initialized in new.
     *
     * @param output an output object
     */
static void
ice_streamer_mstar_destroy (ice_streamer_output_t *output)
{
    frost_pointer data;
    STREAMER_MSTAR_TRACE("[ice_streamer_mstar_destroy]\n");
    frost_return_if_fail (output);


    //data = ice_streamer_output_private_data_get (output);
    if (NULL != is_ibuff)
    {
        DBUF_ForceFlush(is_ibuff);
        DBUF_EndFlush(is_ibuff);
        DBUF_Destory(is_ibuff);
        is_ibuff = NULL;
    }

    if(NULL != Is_MediaCharBuf)
    {
        mosi_free(Is_MediaCharBuf);
        Is_MediaCharBuf = NULL;
    }

}

/**
     * Open the output device.
     *
     * Blocking calls in this function are to be avoided as far as
     * possible as this would freeze ice streamer. This function cannot coexist
     * with #status.
     *
     * @param output an output object
     * @return TRUE on successful opening of the output device, otherwise frost_false
     */
static frost_bool
ice_streamer_mstar_open (ice_streamer_output_t *output)
{
    ice_streamer_mstar_data_t *data;
    frost_int ret;
    STREAMER_MSTAR_TRACE("[ice_streamer_mstar_open]\n");
    frost_return_val_if_fail (output, frost_false);

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

#ifdef DUMP_DATA_TO_FILE
    if (data->fp = fopen(data->destdir, "wb"))
    {
        TRACE_INFO(("fopen successfully \n"));
    }
#endif
    if(NULL == Is_MediaCharBuf)
    {
      Is_MediaCharBuf = (char*)mosi_malloc(ASF_DBUF_SIZE);
      if(NULL == Is_MediaCharBuf)
      {
        STREAMER_MSTAR_ERROR("Cannot allocate space for Is_MediaCharBuf...\n");
        return frost_false;
      }
    }

    if(NULL != is_ibuff)
    {
      DBUF_ForceFlush(is_ibuff);
      DBUF_EndFlush(is_ibuff);
      DBUF_Destory(is_ibuff);
    }
    is_ibuff = DBUF_CREATE_PTR(Is_MediaCharBuf, ASF_DBUF_SIZE);
    if(NULL == is_ibuff)
    {
      STREAMER_MSTAR_ERROR("Cannot allocate space for is_ibuff...\n");
      return frost_false;
    }
    STREAMER_MSTAR_TRACE("is_ibuff created\n");

    return frost_true;
}

/**
     * Close the output device.
     *
     * This function cannot coexist with #status.
     *
     * @param output an output object
     */
static void
ice_streamer_mstar_close (ice_streamer_output_t *output)
{
    ice_streamer_mstar_data_t *data;
    STREAMER_MSTAR_TRACE("[ice_streamer_mstar_close]\n");
    frost_return_if_fail (output);

    data = (ice_streamer_mstar_data_t *)ice_streamer_output_private_data_get (output);
#ifdef DUMP_DATA_TO_FILE
    if (data->fp) {
//        finalize (data);
        fclose (data->fp);
        data->fp = NULL;
    }
#endif
    frost_return_if_fail (data);

}

/**
     * Write data to the output device.
     *
     * This function is called from a separate thread and should block until
     * the input buffer has been written to the decoder. This function cannot
     * coexist with #status.
     *
     * @param output an output object
     * @param buffer a buffer with asf packet data to write to the decoder
     * @param size the number of bytes in the buffer
     * @param err an error struct
     */
static void
ice_streamer_mstar_write (ice_streamer_output_t *output, frost_pointer buffer, frost_int len,
                      ice_error_code_t *error)
{
    ice_streamer_mstar_data_t *data;
    int LWr;

    if(g_flush_working)
    {
        printf("Now flush is working. So write function should be prevented.Ref: Roy \n");
        return ;
    }

//    pthread_mutex_lock(&schedule_mutex);
    frost_return_if_fail (output);
    frost_return_if_fail (buffer);
    frost_return_if_fail (len > 0);
    data = (ice_streamer_mstar_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_if_fail (data);
    //TRACE_INFO(("==============> buffer:%d ,len:%d \n",buffer,len));
    //STREAMER_MSTAR_TRACE("[ice_streamer_mstar_write] buffer:%d ,len:%d \n",buffer,len);
    if(NULL != is_ibuff)
    {
        while (DBUF_QueryFreeSpace(is_ibuff) < (len+512*1024))    //the dbuf is full
        {
            STREAMER_MSTAR_TRACE("is_ibuff is almost full\n");
            FROST_Sleep(100);
            if(NULL == is_ibuff)
            {
                printf("\33[0;33m[%s::%d]is_ibuff became NULL\n\33[m",__PRETTY_FUNCTION__,__LINE__); //yellow
//                pthread_mutex_unlock(&schedule_mutex);
                return;
            }
        }
        LWr=DBUF_LockWrite(is_ibuff, &buffer, len);
        if (LWr == 0)
        {
            DBUF_UnlockWrite(is_ibuff, len);
        }
    }
    else
    {
        STREAMER_MSTAR_ERROR("\33[22;32m\n is_ibuff is NULL\033[0m\n");
    }
#ifdef DUMP_DATA_TO_FILE
    if(len)
    {
        fwrite (buffer, sizeof (char), len, data->fp);
        sync();
    }
#endif
#ifdef DUMP_DATA
    {
        frost_uint16 k = 0;
        char* buf = (char *)buffer;
        TRACE_DUMP_INFO( ( "=================> Packet data which is written to Mstar Buffer :\r\n" ) );
        for ( k = 0; k < 128; k++ )
        {
            TRACE_DUMP_INFO( ( "%02X ", *( buf + k ) ) );
            if ( 0 == ( ( k + 1 ) & 0x000F ) )
                TRACE_DUMP_INFO( ( "\r\n" ) );
        }
        TRACE_DUMP_INFO( ( "\r\n" ) );
    }
#endif

//    pthread_mutex_unlock(&schedule_mutex);
}

extern "C" void ice_media_player_is_stop() ;

static void
ice_streamer_mstar_header (ice_streamer_output_t *output, frost_pointer buffer, ice_error_code_t *error)
{
    ice_streamer_mstar_data_t *data;
    frost_uint i;
    int LWr;
    unsigned char *temp_buffer;


    frost_return_if_fail (output);
    frost_return_if_fail (buffer);
    data = (ice_streamer_mstar_data_t *)ice_streamer_output_private_data_get (output);
    frost_return_if_fail (data);

    ice_streamer_asf_parsed_header_t  * header = (ice_streamer_asf_parsed_header_t  *) buffer;

    TRACE_INFO(("==============> header buffer:%d ,len:%d \n",header->header,header->header_size));
    STREAMER_MSTAR_TRACE("[ice_streamer_mstar_header] buffer:%d ,len:%d \n",header->header,header->header_size);
    asf_object_data_t * asf_data_object = (asf_object_data_t *)header->asf_data_object;
    temp_buffer = (unsigned char*)mosi_malloc(header->header_size);
    memcpy(temp_buffer, header->header, header->header_size);
    if (MApp_HeaderModification(temp_buffer, header->header_size))
        printf("Header Modification Done\n");
    else
        printf("No Need To Do Modification\n");

    if(NULL != is_ibuff && header->header != NULL)
    {
        while (DBUF_QueryFreeSpace(is_ibuff) < (header->header_size+512*1024))    //the dbuf is full
        {
            STREAMER_MSTAR_TRACE("is_ibuff is almost full\n");
            FROST_Sleep(100);
            if(NULL == is_ibuff)
            {
                printf("\33[0;33m[%s::%d]is_ibuff became NULL\n\33[m",__PRETTY_FUNCTION__,__LINE__); //yellow
                return;
            }
        }
        LWr=DBUF_LockWrite(is_ibuff, (void **)&temp_buffer, header->header_size);
        if (LWr == 0)
        {
            DBUF_UnlockWrite(is_ibuff, header->header_size);
        }
    }
    else
    {
        STREAMER_MSTAR_ERROR("\33[22;32m\n is_ibuff is NULL\033[0m\n");
    }

#ifdef DUMP_DATA_TO_FILE
    if(header->header_size)
    {
        fwrite (temp_buffer, sizeof (char), header->header_size, data->fp);
        sync();
    }
#endif
    mosi_free(temp_buffer);
}

frost_bool g_eos_signalled = frost_false ;

static frost_bool
ice_streamer_mstar_check_progress(ice_streamer_output_t *output,ice_streamer_playback_status_t wanted_status )
{
    STREAMER_MSTAR_TRACE("[ice_streamer_mstar_check_progress]\n");
    if(wanted_status == ICE_PLAYBACK_STATUS_STOP)
    {
      g_eos_signalled = frost_false;
      printf("\33[0;33m[%s::%d]ICE_PLAYBACK_STATUS_STOP - g_eos_signalled = %d\n\33[m",__FUNCTION__,__LINE__, g_eos_signalled); //yellow

      ice_media_player_is_stop();
    }
    else if(wanted_status == ICE_PLAYBACK_STATUS_EOS)
    {
      g_eos_signalled = frost_true; //=frost_false //@@++-- mod for test | ilters
      printf("\33[0;33m[%s::%d]ICE_PLAYBACK_STATUS_EOS - g_eos_signalled = %d\n\33[m",__FUNCTION__,__LINE__, g_eos_signalled); //yellow
    }
    else if(wanted_status == ICE_PLAYBACK_STATUS_PLAY)
    {
      g_eos_signalled = frost_false ;
      printf("\33[0;33m[%s::%d]ICE_PLAYBACK_STATUS_PLAY - g_eos_signalled = %d\n\33[m",__FUNCTION__,__LINE__, g_eos_signalled); //yellow
    }
    return frost_true ;
}
/**
     * Flush the audio decoder buffer.
     *
     * This should tell the decoder to drop whatever it is doing and
     * empty the buffer.
     *
     * @param output an output object
     */
extern "C" MS_BOOL MApp_VDPlayer_DemuxReset(MS_U8 u8DmxRstCtrl);// for FR
extern frost_uint8 gu8FlushCtrl;// for FR

static void
ice_streamer_mstar_flush (ice_streamer_output_t *output)
{
    ice_streamer_mstar_data_t *data;

    g_flush_working = frost_true ;

    printf("ice_streamer_mstar_flush start\n");

//    pthread_mutex_lock(&schedule_mutex);
    frost_return_if_fail (output);
    gb_flush_start = frost_true;
   
    MApp_VDPlayer_DemuxReset(gu8FlushCtrl);// for FR
//    pthread_mutex_unlock(&schedule_mutex);

    printf("ice_streamer_mstar_flush end\n");

    g_flush_working = frost_false ;
}

ICE_STREAMER_OUTPUT_BUILTIN (mstar,
                    "MSTAR Output",
                    ICE_STREAMER_VERSION,
                    "Mstar output plugin",
                    ice_streamer_mstar_plugin_setup);

const ice_streamer_plugin_desc_t * getMstarPluginDesc()
{
    STREAMER_MSTAR_TRACE("getMstarPluginDesc\n");
    return &ice_streamer_builtin_mstar ;
}


