/*
 *  MediaMVP Output Device for VDR
 *
 *  $Id: mvpprog.c,v 1.4 2004/05/27 19:05:44 dom Exp $ 
 *  $Date: 2004/05/27 19:05:44 $
 *
 *  The MVP application
 *
 */



#include "mvpprog.h"

#include "setup.h"

#include <signal.h>

static cMvpProgram   *Instance = NULL;  /* Used by some callbacks */

/* Handles the callback events - passes everything through to the 
   library - ensure that gui events aren't spliced together - osdcalls just
   draw onto the bitmap directly
*/
static void  EventProcess_cb(int fd, short event, void *arg);

/* Callbacks from the libmvp.a */
static int        NewProgram(dongle_t *dongle, application_t *application);
static void       DeleteProgram(application_t *application);
static void       KeyPress(application_t *application, int keycode);
static int        Authorise(char *mac_address, uint32_t ipaddr, int connections);
static void       Ack(application_t *applcation, int ackcode, unsigned char*buf, int buflen);
static uint8_t   *GetSurface(application_t *application, int *size);



cMvpProgram::cMvpProgram()
{
    Instance = this;

    if ( pipe(&pipes[0]) == -1 ) {
        printf("Couldn't create pipes\n");
        exit(1);
    }

    dongle = NULL;

    printf("%d %d\n",pipes[0],pipes[1]);
    /* Set up the event loop */
    event_init();


    /* Set up some parameters */
    Running = false;
    display = false;

    MediamvpSetup.open = NewProgram;
    MediamvpSetup.authorise = Authorise;

    mediamvp_server_init(&MediamvpSetup);


    /* Add up a reading event for sending events about the place */
    event_set(&event,pipes[0],EV_READ,EventProcess_cb,(void *)this);
    event_add(&event,NULL);

    surface = surface_alloc(c_gui_width,c_gui_depth,8);
    surface_clear(surface,0);
    surface_set_pixel(surface,100,100,0x00FFFFFF);

    Start();

    ringy = new cRingBufferLinear(3000 * 1024, 0, false);
    url = new cMvpLiveURL(ringy);


}

cMvpProgram::~cMvpProgram()
{
    delete url;
    delete ringy;
    surface_dealloc(surface);
    Cancel(3);
}

void cMvpProgram::Connect(dongle_t *dgl)
{
    dongle = dgl;
    /* Set up some parameters */
    Running = false;
    display = false;
}

void cMvpProgram::Disconnect()
{
    dongle = NULL;
}

void cMvpProgram::Remote(int keycode)
{
    remote.SendEvent(keycode);
}

int cMvpProgram::PlayVideo(const uchar *Data, int Length)
{
    ringy->Put(Data,Length);
    return Length;
}


/* Called on the mvp thread */
uint8_t  *cMvpProgram::Surface(int *size)
{
    if ( surface_get_dirty(surface)  ) {
        surface_set_dirty(surface,FALSE);
        return (uint8_t *) surface_map(surface,MAP_YVU,size);
    }
  
    return NULL;
}


bool cMvpProgram::SetPlayMode(ePlayMode PlayMode)
{
    lastmode = playmode;
    playmode = PlayMode;

    switch ( PlayMode ) {
    case pmNone:
    case pmExtern_THIS_SHOULD_BE_AVOIDED:
        // dongle_send_message(dongle,RDC_STOP);
        break;

    case pmAudioVideo:
        if ( lastmode != PlayMode && dongle ) {
            dongle_send_play(dongle,"mvplive://");
        }
        break;
    case pmAudioOnly:
    case pmAudioOnlyBlack:    
        if ( lastmode != PlayMode && dongle ) {
            dongle_send_play(dongle,"mvplive2://");
        }
        break;
    }

    return true;
}


/* cMvpWrap calls Program->NewOsd() i.e. this.. */
#if VDRVERSNUM < 10307
cOsdBase *cMvpProgram::cMvpProgram::NewOsd(int x, int y)
{
    SendEvent(MEVT_MENU_ON);
    return new cMvpOsd(this,surface,x,y);
}
#else
void cMvpProgram::NewOsdProvider()
{
    new cMvpOsdProvider(surface,this);
}
#endif

bool cMvpProgram::Poll(cPoller &Poller, int TimeoutMs)
{
    return url->Poll(Poller,TimeoutMs);
}



void cMvpProgram::SendEvent(mvpevent_t event)
{
    /* Fix the pipe, this is probably called on the wrong thread... */
    safe_write(pipes[1],&event,sizeof(mvpevent_t));
}


/* Thread that starts up the event loop */
void cMvpProgram::Action(void)
{
    signal(SIGPIPE,SIG_IGN);
    /* A very extensive action loop */
    event_dispatch();
}


/* Callbacks */

static void  EventProcess_cb(int fd, short event, void *arg)
{
    mvpevent_t   c;
    event_add(&Instance->event,NULL);

    read(fd,&c,sizeof(c));

    if ( Instance->dongle ) {
        switch ( c ) {
        case MEVT_STOP:
        case MEVT_PLAY:
        case MEVT_PAUSE:
        case MEVT_MUTE:
            break;
        case MEVT_MENU_ON:
            if ( Instance->display == false )
                dongle_send_message(Instance->dongle,RDC_DISPLAY_ON);
            Instance->display = true;
            break;
        case MEVT_MENU_OFF:
            if ( Instance->display == true )
                dongle_send_message(Instance->dongle,RDC_DISPLAY_OFF);
            Instance->display = false;
            break;
        case MEVT_UPDATE:
            dongle_update_screen(Instance->dongle);
            break;
        }
    }
}

static int NewProgram(dongle_t *dongle, application_t *application)
{
    Instance->Connect(dongle);

    application->close = DeleteProgram;
    application->get_yvuy_surface = GetSurface;
    application->keypress = KeyPress;
    application->ack = Ack;

    printf("%d %d x %d %d x %d\n",MediamvpSetup.dongle_version,c_gui_width,c_gui_depth,c_screen_width,c_screen_depth);
    application->gui_width = c_gui_width;
    application->gui_depth = c_gui_depth;
    application->screen_width = c_screen_width;
    application->screen_depth = c_screen_depth;

    return 0;
}

static void  DeleteProgram(application_t *application)
{
    Instance->Disconnect();    
}

static void  KeyPress(application_t *application, int keycode)
{
    Instance->Remote(keycode);
}


/* Authorise a new dongle - we only like having one connection at a time */
static int Authorise(char *mac_address, uint32_t ipaddr, int connections)
{
    if ( connections != 0 )
        return 0;

    return 1;
}

static void  Ack(application_t *applcation, int ackcode, unsigned char*buf, int buflen)
{

}

static uint8_t *GetSurface(application_t *application, int *size)
{
    uint8_t *ret = Instance->Surface(size);

    return ret;
}
