
/**
 * @file
 * Output plugin helper
 */
#include "ice_streamer.h"

#include <string.h>
#include <unistd.h>

#include "utilities_debug/cabot_assert.h"
#include "utilities_debug/trace.h"
#include "frost_mem.h"
#include "frost_list.h"
#include "frost/frost_condvar.h"
#include "frost/frost_mutex.h"
#include "mbrg_videoplayer_T4.h"

extern frost_uint8 gu8FlushCtrl;// for FR
extern frost_bool gBoolTrickmodeFlag;
frost_bool gPlayPressedDuringTrickMode = frost_false;
frost_bool g_play_pressed_after_pause = frost_false;
frost_bool g_pause_pressed = frost_false;
frost_bool g_jump_to_where_left_in_case_curl_error = frost_false;


TRACE_IMPLEMENT_LEVEL("ice_streamer_downloader", TRACE_LEVEL_INFO);

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

static void set_flush_ctrl(ice_streamer_output_t* output, frost_uint8 flush_ctrl);
static void set_trick_mode_flag(ice_streamer_output_t* output, frost_bool flag);
static void set_play_pressed_during_trick_mode(ice_streamer_output_t* output, frost_bool play_pressed);
static void set_play_pressed_after_pause(ice_streamer_output_t* output, frost_bool play_pressed);
static void set_pause_pressed(ice_streamer_output_t* output, frost_bool pause_pressed);
static void set_jump_to_where_left_in_case_curl_error(ice_streamer_output_t* output, frost_bool jump);


/*
 *
 * locking order: status_mutex > write_mutex
 *                filler_mutex
 *                playtime_mutex is leaflock.
 */


/** @ */

/*
 * Public functions
 */

frost_pointer
ice_streamer_output_private_data_get (ice_streamer_output_t *output)
{
    frost_return_val_if_fail (output, NULL);
    frost_return_val_if_fail (output->plugin, NULL);

    return output->plugin_data;
}

void
ice_streamer_output_private_data_set (ice_streamer_output_t *output, frost_pointer data)
{
    frost_return_if_fail (output);
    frost_return_if_fail (output->plugin);

    output->plugin_data = data;
}

void
ice_streamer_output_stream_type_add (ice_streamer_output_t *output, ...)
{
    ice_streamer_stream_type_t *f;
    va_list ap;

    va_start (ap, output);
    f = ice_streamer_stream_type_parse (ap);
    va_end (ap);

    frost_return_if_fail (f);

    output->format_list = frost_list_append (output->format_list, f);
}

void
ice_streamer_output_format_list_free_elem (frost_pointer data, frost_pointer user_data)
{
    ice_streamer_stream_type_t *f;

    frost_return_if_fail (data);

    f = (ice_streamer_stream_type_t *)data;

    ice_streamer_object_unref (f);
}

void
ice_streamer_output_format_list_clear(ice_streamer_output_t *output)
{
    if (output->format_list == NULL)
        return;

    frost_list_foreach (output->format_list,
                    ice_streamer_output_format_list_free_elem,
                    NULL);

    frost_list_free (output->format_list);
    output->format_list = NULL;
}

void
update_playtime (ice_streamer_output_t *output, int advance)
{
    FROST_MutexLock (output->playtime_mutex);

    frost_uint32 playback_time = MApp_VDPlayer_GetInfo(E_VDPLAYER_INFO_CUR_TIME)/1000;
    output->played_time  = 1000 * playback_time;


    FROST_MutexUnlock (output->playtime_mutex);

}

void
ice_streamer_output_set_error (ice_streamer_output_t *output, ice_error_code_t *error)
{
    frost_return_if_fail (output);

    ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_STOP);

    if (error) {
        TRACE_ERROR(("Output plugin %s reported error, '%s'",
                        ice_streamer_plugin_shortname_get ((ice_streamer_plugin_t *)output->plugin),
                        __FUNCTION__));
    }
}

typedef struct {
    ice_streamer_output_t *output;
    ice_streamer_asf_parsed_header_t  * header;
} ice_streamer_output_header_changed_arg_t;

void
header_changed_arg_free (void *data)
{
//    ice_streamer_output_header_changed_arg_t *arg = (ice_streamer_output_header_changed_arg_t *)data;
//    frost_string_free (arg->header, frost_true);
}

frost_bool
header_changed (void *data)
{
    /* executes in the output thread; NOT the filler thread */
    ice_streamer_output_header_changed_arg_t *arg = (ice_streamer_output_header_changed_arg_t *)data;
    ice_error_code_t  err = ICE_ERROR_NONE ;

    TRACE_INFO(("Running hotspot! Header Passed!! "));

    if(arg->output->plugin && arg->output->plugin->methods.header)
    {
        arg->output->plugin->methods.header(arg->output ,(frost_pointer)arg->header ,&err);
        return frost_true;
    }

    return frost_false;

//    ice_streamer_object_emit_f (ice_streamer_OBJECT (arg->output),
//                        ice_streamer_IPC_SIGNAL_PLAYBACK_CURRENTID,
//                        ICE_STREAMER_V_TYPE_INT32,
//                        entry);
}

extern volatile frost_bool gb_flush_start;
extern volatile frost_bool gb_flush_stop;

frost_bool
trick_mode_step_done (void *data)
{
    ice_streamer_output_t *output = (ice_streamer_output_t *)data;

    FROST_MutexLock (output->playtime_mutex);
    output->played = output->filler_seek * ice_streamer_sample_frame_size_get (output->format);
    output->toskip = output->filler_skip * ice_streamer_sample_frame_size_get (output->format);
    FROST_MutexUnlock (output->playtime_mutex);
    if((gb_flush_start==frost_false) && (gb_flush_stop==frost_false))
    {
        TRACE_INFO(("[%s %d] flush start = %d, flush stop = %d \n", __FUNCTION__, __LINE__, (int)gb_flush_start, (int)gb_flush_stop));
        ice_streamer_output_flush (output);
    }
    else 
    { 
        TRACE_INFO(("--------------- previous flush in progress...skip flush\n"));
    }
    ice_streamer_curl_seek_enable(frost_false);

    TRACE_INFO(("=================>trick_mode_step_done End \n"));

    return frost_true;
}

void
ice_streamer_output_filler_state_nolock (ice_streamer_output_t *output, ice_streamer_output_filler_state_t state)
{
    output->filler_state = state;
    FROST_CondVarSignal (output->filler_state_cond);
    if (state == FILLER_QUIT || state == FILLER_STOP || state == FILLER_KILL) {
        ice_streamer_ringbuf_clear (output->filler_buffer);
    }
    if (state != FILLER_STOP) {
        ice_streamer_ringbuf_set_eos (output->filler_buffer, frost_false);
    }
}

void
ice_streamer_output_filler_state (ice_streamer_output_t *output, ice_streamer_output_filler_state_t state)
{
    FROST_MutexLock (output->filler_mutex);
    ice_streamer_output_filler_state_nolock (output, state);
    FROST_MutexUnlock (output->filler_mutex);
}

void ice_streamer_output_filler_trick_mode_no_lock(ice_streamer_output_t *output,frost_int step, frost_uint32 samples,ice_streamer_output_filler_state_t state)
{

    TRACE_INFO(("[%s %d] \n", __FUNCTION__, __LINE__));

    if ((output->filler_state == FILLER_TRICK_MODE_WAIT_NEXT_IFRAME) || (output->filler_state == FILLER_TRICK_MODE))
    // trick mode step is changed. User pressed 4x in case of 2x for example
    {
        if (step != output->filler_step)
        {
            TRACE_INFO(("==========>output->filler_step_changed = frost_true current state is %d step= %d output->filler_step = %d \n", output->filler_state, step, output->filler_step));
        }
    }
    else
    {
    output->filler_state = state;
    output->filler_seek = samples;
    }
    output->filler_step = step;
    TRACE_INFO(("======================> ice_streamer_output_filler_trick_mode step=%d  samples=%d state=%d\n", step, samples, state));
    TRACE_INFO(("=======================>ice_streamer_output_filler_trick_mode  output->filler_step=%d  output->filler_seek=%d output->filler_state =%d\n",  output->filler_step, output->filler_seek, output->filler_state));

}
void
ice_streamer_output_filler_trick_mode (ice_streamer_output_t *output,frost_int step, frost_uint32 samples,ice_streamer_output_filler_state_t state)
{
    FROST_MutexLock (output->filler_mutex);
    ice_streamer_output_filler_trick_mode_no_lock(output, step, samples, state);

    FROST_CondVarSignal (output->filler_state_cond);
    FROST_MutexUnlock (output->filler_mutex);
}

void
ice_streamer_output_filler (void *arg)
{
    ice_streamer_output_t *output = (ice_streamer_output_t *)arg;
    ice_streamer_handler_t *chain = NULL;
    frost_bool last_was_kill = frost_false;
    char buf[CURRENT_MAX_PACKET_SIZE];
    ice_error_code_t err;
    frost_int ret;

    ice_streamer_error_reset (&err);

    FROST_MutexLock (output->filler_mutex);
    while (output->filler_state != FILLER_QUIT) {
        if (output->filler_state == FILLER_STOP) {
            TRACE_INFO(("[%s %d] output->filler_state = %d output->status =%d \n", __FUNCTION__, __LINE__, output->filler_state, output->status)); 
            set_trick_mode_flag(output, frost_false);
            if (chain) {
                ice_streamer_object_unref (chain);
                chain = NULL;
            }
            ice_streamer_ringbuf_set_eos (output->filler_buffer, frost_true);
            TRACE_INFO(("Before FROST_CondVarWait \n"));
            FROST_CondVarWait (output->filler_state_cond, output->filler_mutex);
            TRACE_INFO(("After FROST_CondVarWait \n"));
            last_was_kill = frost_false;
            continue;
        }

        if (output->filler_state == FILLER_TRICK_MODE)
        {
           int play_mode = MApp_VDPlayer_GetInfo(E_VDPLAYER_INFO_PLAY_MODE) ;
           TRACE_INFO(("[%s %d] output->filler_state = %d play_mode = %d output->status =%d \n", __FUNCTION__, __LINE__, output->filler_state, play_mode,output->status));
          
          if (!chain) {
              ice_streamer_medialib_entry_t entry;
              ice_streamer_output_header_changed_arg_t *hsarg;
          
              FROST_MutexUnlock (output->filler_mutex);
          
              entry = 1;// ice_streamer_playlist_current_entry (output->playlist);
              if (!entry) {
                  TRACE_INFO(("No entry from playlist!\n"));
                  output->filler_state = FILLER_STOP;
                  FROST_MutexLock (output->filler_mutex);
                  continue;
              }
          
              chain = ice_streamer_handler_chain_setup_url (entry, output->playlist->url, output->format_list, frost_false);
              if (!chain) {
                  FROST_MutexLock (output->filler_mutex);
                  continue;
              }
          
              hsarg = frost_new0 (ice_streamer_output_header_changed_arg_t, 1);
              hsarg->output = output;
              hsarg->header = ice_streamer_get_header_from_asf();
          
              last_was_kill = frost_false;
          
              FROST_MutexLock (output->filler_mutex);
              ice_streamer_ringbuf_hotspot_set (output->filler_buffer, header_changed, header_changed_arg_free, hsarg);
          }
      
            if (g_jump_to_where_left_in_case_curl_error == frost_true)
            {
                set_jump_to_where_left_in_case_curl_error(output, frost_false);
            }

            if ((output->filler_step < 0) && (output->filler_seek < abs(output->filler_step))) // Rewind check
            {
                TRACE_INFO(("FILLER_TRICK_MODE_STOP 2 \n"));
                output->filler_state = FILLER_TRICK_MODE_STOP;
                continue;
            }   // TODO ADD CHECK CONDITION FOR FAST FORWORD. Check the end of movie
            else
            {
                output->filler_seek += output->filler_step ;
            }

            ice_streamer_curl_seek_enable(frost_true);
            TRACE_INFO(("\n \n =====================>WILL SEEK NOW output->filler_seek %d \n", output->filler_seek));
            play_mode = MApp_VDPlayer_GetInfo(E_VDPLAYER_INFO_PLAY_MODE) ;
            TRACE_INFO(("[%s %d] output->filler_state = %d play_mode = %d output->status =%d \n", __FUNCTION__, __LINE__, output->filler_state, play_mode,output->status ));

            ret = ice_streamer_handler_this_seek(chain, output->filler_seek, ICE_SEEK_SET, &err);
            if (ret == -1)
            {
                TRACE_ERROR(("[%s %d] Seeking failed:  \n", __FUNCTION__, __LINE__));
                output->filler_state = FILLER_TRICK_MODE_STOP;
                continue;
            }
            else
            {
                TRACE_INFO(("Seek ok! ice_streamer_handler_this_seek returned %d \n", ret));
                output->filler_skip = output->filler_seek - ret;
                if (output->filler_skip < 0)
                {
                    TRACE_INFO(("Seeked %d samples too far! Updating position... \n", -output->filler_skip));
                    output->filler_skip = 0;
                    output->filler_seek = ret;
                }

                ice_streamer_ringbuf_clear (output->filler_buffer);
                ice_streamer_ringbuf_hotspot_set (output->filler_buffer, trick_mode_step_done, NULL, output);
                if(MApp_VDPlayer_GetInfo(E_VDPLAYER_INFO_PLAY_MODE) == E_VDP_PLAY_MODE_PAUSE)
                {
                    MApp_MPlayer_MovieChangePlayMode(E_MPLAYER_MOVIE_NORMAL);
                }
                output->filler_state = FILLER_TRICK_MODE_WAIT_NEXT_IFRAME;
                TRACE_INFO(("\n \n =====================>SEEK END @ FILLER_TRICK_MODE output->filler_seek %d output->filler_step = %d\n", output->filler_seek, output->filler_step));
                continue;
            }
        }

        if (output->filler_state == FILLER_TRICK_MODE_WAIT_NEXT_IFRAME)
        {
            if(MApp_VDPlayer_GetInfo(E_VDPLAYER_INFO_PLAY_MODE) == E_VDP_PLAY_MODE_PAUSE)
            {
                int current_time = 1000 * (MApp_VDPlayer_GetInfo(E_VDPLAYER_INFO_CUR_TIME)/1000);
                TRACE_INFO(("##### output->filler_seek %d output->filler_state = FILLER_TRICK_MODE_WAIT_NEXT_IFRAME E_VDP_PLAY_MODE_PAUSE current_time = %d \n", output->filler_seek, current_time));
                 if (g_jump_to_where_left_in_case_curl_error == frost_true)
                {
                     TRACE_INFO(("========>[%s %d] g_jump_to_where_left_in_case_curl_error true \n", __FUNCTION__, __LINE__));
                     output->filler_state = FILLER_TRICK_MODE;
                }
                else if (gPlayPressedDuringTrickMode == frost_true)
                {
                    TRACE_INFO(("========>[%s %d] gPlayPressedDuringTrickMode true \n", __FUNCTION__, __LINE__));
                    output->filler_state = FILLER_TRICK_MODE_STOP;
                    set_play_pressed_during_trick_mode(output, frost_false);
                }
                else if (output->filler_step == 0)
                {
                    TRACE_INFO(("========>[%s %d] Jump done output->filler_step == 0 \n", __FUNCTION__, __LINE__));
                    output->filler_state = FILLER_TRICK_MODE_STOP;
                }
                else
                {
                    output->filler_state = FILLER_TRICK_MODE; 
                    // continue to next step of trick mode
                   //MApp_MPlayer_MovieChangePlayMode(E_MPLAYER_MOVIE_NORMAL);
                } 

                continue;
            } 
            if (g_jump_to_where_left_in_case_curl_error == frost_true)
            {
                continue; // do not read data as curl is broken no, but before jumping to old position
                    //wait frame decode done
            }
            
        }

        if (output->filler_state == FILLER_TRICK_MODE_STOP)
        {
            TRACE_INFO(("==========================>ice_streamer_output_filler FILLER_TRICK_MODE_STOP \n"));
            int play_mode = MApp_VDPlayer_GetInfo(E_VDPLAYER_INFO_PLAY_MODE);
            TRACE_INFO(("[%s %d] output->filler_state = %d play_mode = %d output->status = %d \n", __FUNCTION__, __LINE__, output->filler_state, play_mode, output->status));
            set_flush_ctrl(output, E_DEMUX_RESET_FILE_SIZE);
            set_trick_mode_flag(output, frost_false);
            ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_PLAY); 
            MApi_XC_FreezeImg(FALSE,MAIN_WINDOW);
            (frost_bool)MApp_MPlayer_MovieChangePlayMode(MApp_MPlayer_MoviePlayModeMapping(1000));
            output->filler_state = FILLER_RUN;
            continue;
        }

        if (output->filler_state == FILLER_KILL) {
            if (chain) {
                ice_streamer_object_unref (chain);
                chain = NULL;
                output->filler_state = FILLER_RUN;
                last_was_kill = frost_true;
            } else {
                output->filler_state = FILLER_STOP;
            }
            continue;
        }

        if (!chain) {
            ice_streamer_medialib_entry_t entry;
            ice_streamer_output_header_changed_arg_t *hsarg;

            FROST_MutexUnlock (output->filler_mutex);

            entry = 1;// ice_streamer_playlist_current_entry (output->playlist);
            if (!entry) {
                TRACE_INFO(("No entry from playlist!\n"));
                output->filler_state = FILLER_STOP;
                FROST_MutexLock (output->filler_mutex);
                continue;
            }

            chain = ice_streamer_handler_chain_setup_url (entry, output->playlist->url, output->format_list, frost_false);
            if (!chain) {
                FROST_MutexLock (output->filler_mutex);
                continue;
            }

            hsarg = frost_new0 (ice_streamer_output_header_changed_arg_t, 1);
            hsarg->output = output;
            hsarg->header = ice_streamer_get_header_from_asf();

            last_was_kill = frost_false;

            FROST_MutexLock (output->filler_mutex);
            ice_streamer_ringbuf_hotspot_set (output->filler_buffer, header_changed, header_changed_arg_free, hsarg);
        }

        ice_streamer_ringbuf_wait_free (output->filler_buffer, CURRENT_PACKET_SIZE, output->filler_mutex);

        if ((output->filler_state != FILLER_RUN) && (output->filler_state !=  FILLER_TRICK_MODE_WAIT_NEXT_IFRAME)) {
            TRACE_INFO((" State changed while waiting.. output->filler_state = %d \n", output->filler_state));
            continue;
        }
        if (gb_flush_start == frost_true)
        {
            TRACE_INFO(("==========> gb_flush_start == frost_true do NOT READ DATA filler_state= %d \n", output->filler_state));
            continue;
        }
        FROST_MutexUnlock (output->filler_mutex);

        ret = ice_streamer_handler_this_read (chain, buf, CURRENT_PACKET_SIZE, &err);

        FROST_MutexLock (output->filler_mutex);

        if (ret > 0) {
            frost_int skip = 0; 

            output->toskip -= skip;
#ifdef DUMP_DATA
            {
                frost_uint16 k = 0;
                TRACE_DUMP_INFO( ( "=================> Packet data which is written to ring buffer skip: %d \r\n" ,skip) );
                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
            if (ret > skip) {
                ice_streamer_ringbuf_write_wait (output->filler_buffer,
                                         buf + skip,
                                         ret - skip,
                                         output->filler_mutex);
            }
        } 
        else 
        {
            TRACE_ERROR(("[%s %d] ice_streamer_handler_this_read returned  %d output_filler_state = %d \n", __FUNCTION__, __LINE__, ret,output->filler_state));
            if (ret == -1) {
                /* print error */
                TRACE_INFO(("[%s %d] ice_streamer_handler_this_read returned -1 output_filler_state = %d \n", __FUNCTION__, __LINE__, output->filler_state));
                ice_streamer_error_reset (&err);
                if (g_play_pressed_after_pause == frost_true)
                {   
                    TRACE_INFO(("[%s %d] g_play_pressed_after_pause = true WILL CALL  ice_streamer_object_unref \n", __FUNCTION__, __LINE__));
                    ice_streamer_object_unref (chain);
                    chain = NULL;
                    set_jump_to_where_left_in_case_curl_error(output, frost_true);
                    set_trick_mode_flag(output, frost_true);
                    set_flush_ctrl(output, E_DEMUX_RESET_DISABLE_AVSYNC | E_DEMUX_RESET_DISPLAY_ONE_FRAME| E_DEMUX_RESET_FILE_SIZE);
                    FROST_MutexLock (output->playtime_mutex);
                    ice_streamer_output_filler_trick_mode_no_lock(output, 0, output->played_time, FILLER_TRICK_MODE);
                    FROST_MutexUnlock (output->playtime_mutex);
                    set_play_pressed_after_pause(output, frost_false);
                    continue;         
                }
            }
            ice_streamer_object_unref (chain);
            chain = NULL;
            TRACE_INFO(("End of playlist\n"));
            output->filler_state = FILLER_STOP;
        }

    }
    TRACE_INFO(("[%s %d] output->filler_state = %d output->status =%d \n", __FUNCTION__, __LINE__, output->filler_state, output->status));
    set_trick_mode_flag(output, frost_false);

    FROST_MutexUnlock (output->filler_mutex);
}

frost_int
ice_streamer_output_read (ice_streamer_output_t *output, char *buffer, frost_int len)
{
    frost_int ret;
    ice_error_code_t err;
    TRACE_INFO(("================> (%s,%d) \n",__FUNCTION__,__LINE__));
    ice_streamer_error_reset (&err);

    frost_return_val_if_fail (output, -1);
    frost_return_val_if_fail (buffer, -1);

    FROST_MutexLock (output->filler_mutex);
    ice_streamer_ringbuf_wait_used (output->filler_buffer, len, output->filler_mutex);
    ret = ice_streamer_ringbuf_read (output->filler_buffer, buffer, len);
    if (ret == 0 && ice_streamer_ringbuf_iseos (output->filler_buffer)) {
        TRACE_INFO(("[%s %d ] Before setting output status to ICE_PLAYBACK_STATUS_EOS", __FUNCTION__, __LINE__));
        ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_EOS);
        FROST_MutexUnlock (output->filler_mutex);
        return -1;
    }
    FROST_MutexUnlock (output->filler_mutex);

    update_playtime (output, ret);

    if (ret < len) {
        TRACE_INFO(("Underrun %d of %d (%d)", ret, len, ice_streamer_sample_frame_size_get (output->format)));

        if ((ret % ice_streamer_sample_frame_size_get (output->format)) != 0) {
            TRACE_ERROR(("***********************************\n"));
            TRACE_ERROR(("* Read non-multiple of sample size,\n"));
            TRACE_ERROR(("*  you probably hear noise now :)\n"));
            TRACE_ERROR(("***********************************\n"));
        }
        output->buffer_underruns++;
    }

    output->bytes_written += ret;

    return ret;
}

frost_int
ice_streamer_output_bytes_available (ice_streamer_output_t *output)
{
    return ice_streamer_ringbuf_bytes_used (output->filler_buffer);
}

ice_streamer_config_property_t *
ice_streamer_output_config_property_register (ice_streamer_output_t *output, const char *name, const char *default_value, ice_streamer_object_handler_t cb, frost_pointer userdata)
{
    frost_return_val_if_fail (output->plugin, NULL);
    return ice_streamer_plugin_config_property_register ((ice_streamer_plugin_t *)output->plugin, name, default_value, cb, userdata);
}

ice_streamer_config_property_t *
ice_streamer_output_config_lookup (ice_streamer_output_t *output, const char *path)
{
    frost_return_val_if_fail (output->plugin, NULL);
    return ice_streamer_plugin_config_lookup ((ice_streamer_plugin_t *)output->plugin, path);
}

ice_streamer_medialib_entry_t
ice_streamer_output_current_id (ice_streamer_output_t *output)
{
    frost_return_val_if_fail (output, 0);
    return output->current_entry;
}


/** @addtogroup Output
 * @{
 */
/** Methods */
void
ice_playback_client_tickle (ice_streamer_output_t *output, ice_error_code_t *error)
{
    ice_streamer_output_filler_state (output, FILLER_KILL);
}

void
ice_playback_client_trick_mode(ice_streamer_output_t *output, frost_int32 step, frost_int32 samples, ice_streamer_output_filler_state_t state)
{
    /* "just" tell filler */
    frost_return_if_fail (output);
    TRACE_VERBOSE(("=================> ice_playback_client_trick_mode step=%d  samples=%d state=%d\n", step, samples, state));
  
    ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_PLAY); 
    ice_streamer_output_filler_trick_mode (output, step, samples, state);
}

void
ice_playback_client_start (ice_streamer_output_t *output, ice_error_code_t *err)
{
    frost_return_if_fail (output);

    ice_streamer_output_filler_state (output, FILLER_RUN);
    if (!ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_PLAY)) {
        ice_streamer_output_filler_state (output, FILLER_STOP);
        ice_streamer_error_set (err, ICE_ERROR_GENERIC, "Could not start playback");
    }
}

void
ice_playback_client_stop (ice_streamer_output_t *output, ice_error_code_t *err)
{
    frost_return_if_fail (output);

    ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_STOP);

    ice_streamer_output_filler_state (output, FILLER_STOP);
}

void
ice_playback_client_pause (ice_streamer_output_t *output, ice_error_code_t *err)
{
    frost_return_if_fail (output);

    ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_PAUSE);
}

void
ice_playback_client_play (ice_streamer_output_t *output, ice_error_code_t *err)
{
    frost_return_if_fail (output);

    ice_streamer_output_status_set (output, ICE_PLAYBACK_STATUS_PLAY);


    if (gBoolTrickmodeFlag == frost_true)
    {
        TRACE_INFO(("==========>[%s %d] Play pressed during trick mode  output->filler_state = %d \n", __FUNCTION__, __LINE__, output->filler_state));
        set_play_pressed_during_trick_mode(output, frost_true);
    }  
}


frost_int32
ice_playback_client_status (ice_streamer_output_t *output, ice_error_code_t *error)
{
    frost_int32 ret;
    frost_return_val_if_fail (output, ICE_PLAYBACK_STATUS_STOP);

    FROST_MutexLock (output->status_mutex);
    ret = output->status;
    FROST_MutexUnlock (output->status_mutex);
    return ret;
}

frost_int
ice_playback_client_current_id (ice_streamer_output_t *output, ice_error_code_t *error)
{
    return output->current_entry;
}


/**
 * Get the current playtime in milliseconds.
 */
frost_int32
ice_playback_client_playtime (ice_streamer_output_t *output, ice_error_code_t *error)
{
    frost_uint32 ret;
    frost_return_val_if_fail (output, 0);

    FROST_MutexLock (output->playtime_mutex);
    ret = output->played_time;
    FROST_MutexUnlock (output->playtime_mutex);

    return ret;
}

/* returns the current latency: time left in ms until the data currently read
 *                              from the latest xform in the chain will actually be played
 */
frost_uint32
ice_streamer_output_latency (ice_streamer_output_t *output)
{
    frost_uint ret = 0;
    frost_uint buffersize = 0;

    if (output->format) {
        /* data already waiting in the ringbuffer */
        buffersize += ice_streamer_ringbuf_bytes_used (output->filler_buffer);

        /* latency of the soundcard */
        buffersize += ice_streamer_output_plugin_method_latency_get (output->plugin, output);

        ret = ice_streamer_sample_bytes_to_ms (output->format, buffersize);
    }

    return ret;
}

/**
 * @internal
 */

frost_bool
ice_streamer_output_status_set (ice_streamer_output_t *output, frost_int status)
{
    frost_bool ret = frost_true;

    if (!output->plugin) {
        TRACE_INFO(("No plugin to set status on..\n"));
        return frost_false;
    }

    FROST_MutexLock (output->status_mutex);

    if (output->status != status) {
        if (status == ICE_PLAYBACK_STATUS_PAUSE &&
            output->status != ICE_PLAYBACK_STATUS_PLAY) {
            TRACE_INFO(("Can only pause from play.\n"));
            ret = frost_false;
        } else {
            if (status == ICE_PLAYBACK_STATUS_PAUSE)
            {
                TRACE_INFO(("%s %d set g_pause_pressed to true \n", __FUNCTION__, __LINE__));
                set_pause_pressed(output, frost_true);
            }
            if (status == ICE_PLAYBACK_STATUS_PLAY)
            {
                if (g_pause_pressed == frost_true)
                { 
                    TRACE_INFO(("%s %d set g_pause_pressed to false \n", __FUNCTION__, __LINE__));
                    set_play_pressed_after_pause(output, frost_true);
                    set_pause_pressed(output, frost_false);
                }
            }
            output->status = status;

            if (status == ICE_PLAYBACK_STATUS_STOP) {
                ice_streamer_object_unref (output->format);
                output->format = NULL;
            }
            if (!ice_streamer_output_plugin_method_status (output->plugin, output, status)) {
                TRACE_ERROR(("Status method returned an error!\n"));
                output->status = ICE_PLAYBACK_STATUS_STOP;
                ret = frost_false;
            }

//            ice_streamer_object_emit_f (ice_streamer_OBJECT (output),
//                                ice_streamer_IPC_SIGNAL_PLAYBACK_STATUS,
//                                ICE_STREAMER_V_TYPE_INT32,
//                                output->status);
        }
    }

    FROST_MutexUnlock (output->status_mutex);

    return ret;
}

void
ice_streamer_output_destroy (ice_streamer_object_t *object)
{
    TRACE_INFO(("[%s %d] \n", __FUNCTION__, __LINE__));

    ice_streamer_output_t *output = (ice_streamer_output_t *)object;

    output->monitor_volume_running = frost_false;
    if (output->monitor_volume_thread) {
        FROST_ThreadDelete (output->monitor_volume_thread);
        output->monitor_volume_thread = NULL;
    }

    ice_streamer_output_filler_state (output, FILLER_QUIT);
    FROST_ThreadDelete (output->filler_thread);

    if (output->plugin) {
        ice_streamer_output_plugin_method_destroy (output->plugin, output);
        ice_streamer_object_unref (output->plugin);
    }
    ice_streamer_output_format_list_clear (output);

    ice_streamer_object_unref (output->playlist);

    FROST_MutexDestroy (output->status_mutex);
    FROST_MutexDestroy (output->playtime_mutex);
    FROST_MutexDestroy (output->filler_mutex);
    FROST_CondVarDestroy(output->filler_state_cond);
    ice_streamer_ringbuf_destroy (output->filler_buffer);

//    ICE_PLAYBACK_unregister_ipc_commands ();
}

static frost_bool
set_plugin (ice_streamer_output_t *output, ice_streamer_output_plugin_t *plugin)
{
    frost_bool ret;

    CABOT_ASSERT (output);
    CABOT_ASSERT (plugin);

    output->monitor_volume_running = frost_false;
    if (output->monitor_volume_thread) {
        FROST_ThreadDelete (output->monitor_volume_thread);
        output->monitor_volume_thread = NULL;
    }

    if (output->plugin) {
        ice_streamer_output_plugin_method_destroy (output->plugin, output);
        output->plugin = NULL;
    }
    ice_streamer_output_format_list_clear (output);

    /* output->plugin needs to be set before we can call the
     * NEW method
     */
    output->plugin = plugin;
    ret = ice_streamer_output_plugin_method_new (output->plugin, output);

    if (!ret) {
        output->plugin = NULL;
    }

    return ret;
}

/**
 * Allocate a new #ice_streamer_output_t
 */
ice_streamer_output_t *
ice_streamer_output_new ( ice_streamer_output_plugin_t * plugin, ice_streamer_playlist_t *playlist, frost_uint8 source_id)
{
    ice_streamer_output_t *output;
    frost_int size = OUTPUT_BUFFERSIZE ;

    frost_return_val_if_fail (playlist, NULL);

    TRACE_INFO(("Trying to open output\n"));

    output = ice_streamer_object_new (ice_streamer_output_t, ice_streamer_output_destroy);

    output->playlist = playlist;

    output->status_mutex = FROST_MutexCreate ();
    output->playtime_mutex = FROST_MutexCreate ();

    TRACE_INFO(("Using buffersize %d", size));

    output->source_id = source_id;
    output->first_in_list = frost_false;
    output->filler_mutex = FROST_MutexCreate ();
    output->filler_state = FILLER_STOP;
    output->filler_state_cond = FROST_CondVarCreate ();
    output->filler_buffer = ice_streamer_ringbuf_new (size);
    output->filler_thread = FROST_ThreadCreate(ice_streamer_output_filler, (void*)output, 2048 + 3*CURRENT_MAX_PACKET_SIZE ,FROST_high_priority, "x2 output filler");

//    ice_streamer_config_property_register ("output.flush_on_pause", "1", NULL, NULL);
//
//    ICE_PLAYBACK_register_ipc_commands (ice_streamer_OBJECT (output));

    output->status = ICE_PLAYBACK_STATUS_STOP;

    if (plugin) {
        if (!set_plugin (output, plugin)) {
            TRACE_ERROR (("Could not initialize output plugin\n"));
        }
        else
        {
            ((ice_streamer_plugin_t *)plugin)->in_use = frost_true;
        }
    } else {
        TRACE_ERROR(("initalized output without a plugin, please fix!\n"));
    }

    return output;
}

/**
 * Flush the buffers in soundcard.
 */
void
ice_streamer_output_flush (ice_streamer_output_t *output)
{
    frost_return_if_fail (output);

    ice_streamer_output_plugin_method_flush (output->plugin, output);
}

/**
 * @internal
 */
frost_bool
ice_streamer_output_format_set (ice_streamer_output_t *output, ice_streamer_stream_type_t *fmt)
{
    frost_return_val_if_fail (output, frost_false);
    frost_return_val_if_fail (fmt, frost_false);

    TRACE_INFO(("Setting format!\n"));

    if (!ice_streamer_output_plugin_format_set_always (output->plugin)) {
        frost_bool ret;

        if (output->format && ice_streamer_stream_type_match (output->format, fmt)) {
            TRACE_INFO(("audio formats are equal, not updating\n"));
            return frost_true;
        }

        ret = ice_streamer_output_plugin_method_format_set (output->plugin, output, fmt);
        if (ret) {
            ice_streamer_object_unref (output->format);
            ice_streamer_object_ref (fmt);
            output->format = fmt;
        }
        return ret;
    } else {
        if (output->format && !ice_streamer_stream_type_match (output->format, fmt)) {
            ice_streamer_object_unref (output->format);
            ice_streamer_object_ref (fmt);
            output->format = fmt;
        }
        if (!output->format) {
            ice_streamer_object_unref (output->format);
            ice_streamer_object_ref (fmt);
            output->format = fmt;
        }
        return ice_streamer_output_plugin_method_format_set (output->plugin, output, output->format);
    }
}

/*
 * After supporting multi output (output_filler thread is created multiple times),
 * globals shared by ice_media_player, ice_streamer and mbrg_videoplayer_T4 
 * should be handled properly in order not to break the previous behaviour.
 * Therefore, setter functions are defined for global variables and only one output (first in the list)
 * will be allowed to set their values. 
 */
static void
set_flush_ctrl(ice_streamer_output_t* output, frost_uint8 flush_ctrl)
{
    if(output->first_in_list)
    {
        gu8FlushCtrl = flush_ctrl;
    }
}

static void
set_trick_mode_flag(ice_streamer_output_t* output, frost_bool flag)
{
    if(output->first_in_list)
    {
        gBoolTrickmodeFlag = flag;
    }
}

static void
set_play_pressed_during_trick_mode(ice_streamer_output_t* output, frost_bool play_pressed)
{
    if(output->first_in_list)
    {
        gPlayPressedDuringTrickMode = play_pressed;
    }
}

static void
set_play_pressed_after_pause(ice_streamer_output_t* output, frost_bool play_pressed)
{
    if(output->first_in_list)
    {
        g_play_pressed_after_pause = play_pressed;
    }
}

static void
set_pause_pressed(ice_streamer_output_t* output, frost_bool pause_pressed)
{
    if(output->first_in_list)
    {
        g_pause_pressed = pause_pressed;
    }
}

static void
set_jump_to_where_left_in_case_curl_error(ice_streamer_output_t* output, frost_bool jump)
{
    if(output->first_in_list)
    {
        g_jump_to_where_left_in_case_curl_error = jump;
    }
}
