/*
#
# Main interface tool for GOPchop.  Implements multiple utility functions.
#
# $Id: Main.cpp 346 2009-03-02 23:26:02Z keescook $
#
# Copyright (C) 2001-2009 Kees Cook
# kees@outflux.net, http://outflux.net/
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# http://www.gnu.org/copyleft/gpl.html
#
*/
/*
 * Initial main.c file generated by Glade. Edit as required.
 * Glade will not overwrite this file.
 */

#include "GOPchop.h"

#include <stdio.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#ifdef ENABLE_NLS
# include <locale.h>
#endif

#include "Vector.h"
#include "List.h"
#include "ElementStream.h"
#include "Pack.h"
#include "GroupOfPictures.h"
#include "Picture.h"
#include "MPEG2Parser.h"
#include "mpeg2consts.h"
#include "widgets.h"
#include "FirstPack.h"

#include "GOPutils.h"

#ifdef __cplusplus
extern "C" {
#endif

/* libmpeg2 */
#include "video_out.h"
#include <mpeg2dec/mpeg2.h>

#ifdef __cplusplus
}
#endif

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <math.h>

#include "main.h"

#include "display.h"

#include <getopt.h>

/* */
/* globals for everyone to beat on */
/* */
// strings
char *GOPchop_cvsid = "$Id: Main.cpp 346 2009-03-02 23:26:02Z keescook $";

// objects
MPEG2Parser *mpeg2parser;

// command line options
char * opt_videoout    = NULL; // default to the first libvo driver
char * opt_pipe        = NULL; // default to no command
int    opt_show_states = 0;    // default to not reporting libmpeg2 states

// decode variables
int                   desired_output = GOPCHOP_OUTPUT_LIBVO;
/*
static mpeg2dec_t    *mpeg2dec = NULL;
static vo_open_t     *output_open = NULL;
static vo_instance_t *output = NULL;
*/
struct display_engine_t * engine = NULL;

// mark settings
gint main_clist_count = 0;
uint32_t mark_start = 0;
uint32_t mark_end = 0;

// pipe variables
int pipes[2];
int *client_pipe = NULL;

/* global preferences */
/*
 * add new options below.
 * If you want it saved to the rc file, add it to the "parsable_items" list.
 * Update the "handle_rc" function below for rc options.
 * Update the menu activation handler to auto-save the rc file.
 */
global_options options={
    run_loop:             0, /* don't wrap arround by default */
    run_speed:            1, /* one frame at a time by default */
    default_run_speed:    1, /* one frame at a time by default */
    auto_refresh:         1, /* show gop position changes by default */
    ignore_errors:        0, /* don't ignore errors by default */
    drop_orphaned_frames: 0, /* orphaned frame dropping can be nasty */
    adjust_timestamps:    1, /* adjust by default */
    video_driver_ptr:     NULL, /* prefered video driver */
    video_driver:         "\0",
    force_system_header:  0, /* force prepended system header pack */
    drop_trailing_pack_with_system_header: 0, /* drop final pack if it has a system header */
    ignore_endcode:       0, /* don't stop parsing when an End Code is seen */
};
rc_parse_item parsable_items[] = {
    { "run_loop",             &options.run_loop,             RC_TYPE_BOOLEAN },
    { "default_run_speed",    &options.default_run_speed,    RC_TYPE_INTEGER },
    { "auto_refresh",         &options.auto_refresh,         RC_TYPE_BOOLEAN },
    { "ignore_errors",        &options.ignore_errors,        RC_TYPE_BOOLEAN },
    { "drop_orphaned_frames", &options.drop_orphaned_frames, RC_TYPE_BOOLEAN },
    { "adjust_timestamps",    &options.adjust_timestamps,    RC_TYPE_BOOLEAN },
    { "video_driver_ptr",     &options.video_driver_ptr,     RC_TYPE_STRING  },
    { "force_system_header",  &options.force_system_header,  RC_TYPE_BOOLEAN },
    { "drop_trailing_pack_with_system_header",  &options.drop_trailing_pack_with_system_header,  RC_TYPE_BOOLEAN },
    { "ignore_endcode",       &options.ignore_endcode,       RC_TYPE_BOOLEAN },
    { NULL, NULL, 0 }
};

///// My various supporting functions
void flush()
{
    while (g_main_iteration(FALSE));
    /*
       while(gtk_events_pending())
       gtk_main_iteration();
     */
}

void status_on(const gchar * owner, char *text)
{
    guint context;

    context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar),
                                           owner);
    gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), context, text);
    flush();
}

void status_off(const gchar * owner)
{
    guint context;

    context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar),
                                           owner);
    gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), context);
    flush();
    //gtk_main_iteration_do(0);

}

const gchar *fileops = "fileops";

void progress(char *task, float done)
{
    static gchar *lasttask = NULL;
    static float lastdone = 0.0;
    float diff;

    int tasksdiffer;
    gint context;

    context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar),
                                           fileops);

    if (lasttask)
    {
        tasksdiffer = strcmp(lasttask, task);
    }
    else
    {
        tasksdiffer = 1;
    }

    if (tasksdiffer)
    {
        if (lasttask)
            gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), context);

        lasttask = (char *) g_realloc(lasttask, strlen(task) + 1);
        strcpy(lasttask, task);

        gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), context, lasttask);
        flush();
    }

    // always reset the progress bar when the progress reaches 100%
    if (done == 1.0)
    {
        done = 0.0;
    }
    else
    {
        diff = fabs(done - lastdone);

        if (!tasksdiffer && diff < 0.01)
          return;
    }

    lastdone = done;

    gtk_progress_set_percentage(GTK_PROGRESS(main_progressbar), lastdone);

    /*   sending to stderr instead...
       if (mpeg2parser->getError())
       show_error(mpeg2parser->getError());
     */

    flush();
}

void show_error(const gchar *text)
{
    GtkTextIter iter;
    GtkTextBuffer *buffer;

    buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(error_text_why));
    gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(buffer),&iter);
    gtk_text_iter_forward_to_end(&iter);
    gtk_text_buffer_insert(buffer,&iter,text,-1);

}


void get_clips_from_list(GtkTreeModel *model,
                         GtkTreeIter  *iter,
                         struct clip_list_t * clips)
{
    GValue list_GOP_first;
    GValue list_GOP_last;

    if (!model || !iter || !clips) return;

    memset(&list_GOP_first,0,sizeof(list_GOP_first));
    memset(&list_GOP_last, 0,sizeof(list_GOP_last));

    gtk_tree_model_get_value(GTK_TREE_MODEL(model), iter,
                             ITEM_GOP_FIRST, &list_GOP_first);
    gtk_tree_model_get_value(GTK_TREE_MODEL(model), iter,
                             ITEM_GOP_LAST,  &list_GOP_last);

    clips->GOP_first = g_value_get_uint(&list_GOP_first);
    clips->GOP_last =  g_value_get_uint(&list_GOP_last);

    g_value_unset(&list_GOP_first);
    g_value_unset(&list_GOP_last);
}


/* "foreach" functions used to walk the main_clist */
gboolean foreach_count_GOPs(GtkTreeModel *model,
                            GtkTreePath *path,
                            GtkTreeIter *iter,
                            gpointer data)
{
    uint32_t * count = (uint32_t *)data;
    struct clip_list_t clips;

    if (!count) return TRUE;

    get_clips_from_list(model,iter,&clips);

    *count += clips.GOP_last - clips.GOP_first + 1;

    return FALSE; /* keep traversing */
}


gboolean foreach_save_clips_to_file(GtkTreeModel *model,
                                    GtkTreePath *path,
                                    GtkTreeIter *iter,
                                    gpointer data)
{
    struct clip_list_t clips;
    uint32_t i;
    struct save_clip_info_t * clip_info = (struct save_clip_info_t*)data;

    if (!clip_info) return TRUE; /* abort */

    get_clips_from_list(model,iter,&clips);


    for (i = clips.GOP_first; i <= clips.GOP_last; i++) {
        progress(clip_info->progress_task,
                 (float)( (float) clip_info->count++ /
                          (float) clip_info->total     ) );
        
        clip_info->written_bytes +=
            write_GOP(clip_info->file, mpeg2parser, i,
                    ((clip_info->GOP_previous + 1) == i),
                    options.drop_orphaned_frames,
                    options.adjust_timestamps,
                    &clip_info->written_pictures,
                    //TI!
                    (clip_info->total - clip_info->count)==0,
                    options.drop_trailing_pack_with_system_header);
        clip_info->GOP_previous = i;

    }

    return FALSE; /* keep on steppin */
}




void save_file(char *filename)
{
    char buf[1024];
    FILE *mpeg2;
    struct save_clip_info_t clip_info;

    clip_info.progress_task = _("Saving MPEG2");

    if (!(mpeg2 = fopen(filename, "w")))
    {
        snprintf(buf, 1024, "%s: %s\n", filename, strerror(errno));
        show_error(buf);
    }
    else
    {
        time_t begin, finish;
        char report[REPORT_LENGTH];

        time(&begin);
        clip_info.total=0;

        /* count up how many GOPs there are to write */
        gtk_tree_model_foreach(GTK_TREE_MODEL(main_list_store),
                               foreach_count_GOPs,
                               &clip_info.total);

        clip_info.file=mpeg2;
        clip_info.count=0;
        clip_info.written_pictures=0;
        clip_info.written_bytes=0;
        // make sure the first GOP is closed
        clip_info.GOP_previous = (uint32_t) -2;
        
        //TI! save first dummy pack with valid system header
        if (options.force_system_header)
        {
            clip_info.written_bytes+=(mpeg2parser->getFirstPack())->saveFirstPack(mpeg2);
        }
        
        /* save each clip in the list */
        gtk_tree_model_foreach(GTK_TREE_MODEL(main_list_store),
                               foreach_save_clips_to_file,
                               &clip_info);

        progress(clip_info.progress_task, 1.0);
        status_off(fileops);

        // write an end-of-PS marker
        fwrite(eos_buf, sizeof(eos_buf), 1, mpeg2);
        fclose(mpeg2);

        time(&finish);
        finish -= begin;

        clip_info.written_bytes /= 1048576;
        if (finish > 0)
            clip_info.written_bytes /= finish;
        if (clip_info.written_bytes == 0)
            clip_info.written_bytes = 1;

        // FIXME: bad pluralization
        snprintf(report,REPORT_LENGTH,finish == 1
                ? _("Wrote %s (GOPs: %u, pictures: %u) in %d second (%%%sMB/s)\n")
                : _("Wrote %s (GOPs: %u, pictures: %u) in %d seconds (%%%sMB/s)\n"),
                filename, clip_info.total, clip_info.written_pictures, finish, OFF_T_FORMAT);

        fprintf(stderr, report, clip_info.written_bytes);
    }
}

void open_file(char *filename)
{
    time_t begin, finish;
    char buf[128];
    off_t speed;
    char report[REPORT_LENGTH];
    char * gop_cache_filename;

    time(&begin);
    if (!mpeg2parser->init(filename, progress))
    {
        show_error(mpeg2parser->getError());
    }
    else
    {
        mpeg2parser->parse(options.ignore_errors);
        if (mpeg2parser->getError())
            show_error(mpeg2parser->getError());

        // make sure we actually HAVE something...
        List *GOPs = mpeg2parser->getGOPs();
        if (GOPs && GOPs->getNum())
            file_is_loaded();
        else
            mpeg2parser->reset();
    }

    // load-time calculation
    time(&finish);
    finish -= begin;
    speed = mpeg2parser->getSize() / 1048576;   // max speed is max size
    if (finish > 0)
        speed /= finish;
    if (speed == 0)
        speed = 1;              // claim at least 1MB/s

    snprintf(report,REPORT_LENGTH,finish == 1
            ? _("Loaded %s in %d second (%%%sMB/s)\n")
            : _("Loaded %s in %d seconds (%%%sMB/s)\n"),
            filename, finish, OFF_T_FORMAT);

    fprintf(stderr, report, speed);

    progress(_("Caching"), 1.0);
    /* write out the offset map */
    if (!(gop_cache_filename=(char*)malloc(strlen(filename)+5))) {
        perror("malloc");
    }
    else {
        sprintf(gop_cache_filename,"%s.gop",filename);
        if (write_gop_cache(gop_cache_filename,mpeg2parser)) {
            printf("%s",_("Skipping offset cache writing.\n"));
        }
    }

    progress(_("Done"), 1.0);
    status_off(fileops);
}

/* gtk stupid: no way to get row count from a clist */
void update_GOP_slice_count(int count) {
    gchar buf[128];

    /* update the count of clips */
    sprintf(buf, _("Clips: %d"), count);
    gtk_label_set_text(GTK_LABEL(main_label_clips), buf);
}

void add_GOP_slice(uint32_t start, uint32_t end)
{
    GtkTreeIter iter;
    gchar *pathname;

    pathname = mpeg2parser->getFilename();
    if (!pathname)
        pathname = _("unknown pathname");

    gtk_list_store_append (main_list_store, &iter);
    gtk_list_store_set (main_list_store, &iter,
                        ITEM_FILENAME,  pathname,
                        ITEM_GOP_FIRST, start,
                        ITEM_GOP_LAST,  end,
                        -1);

    main_clist_count++;
    update_GOP_slice_count(main_clist_count);
}

void clear_GOP_slices()
{
    gchar buf[128];

    gtk_list_store_clear(main_list_store);

    main_clist_count=0;
    update_GOP_slice_count(main_clist_count);
}

void remove_GOP_slice(GtkTreeIter *iter)
{
    if (!iter) return;

    gtk_list_store_remove(main_list_store,iter);

    main_clist_count--;
    update_GOP_slice_count(main_clist_count);
}

GdkFilterReturn filter_event(GdkXEvent *xevent,
                             GdkEvent  *event,
                             gpointer   data)
{
    //report_event(event);
    printf("got event\n");
    return GDK_FILTER_CONTINUE;
}

const mpeg2_info_t *parser_info = NULL;

// decode_start
// decode_stop

void clear_GOP_info()
{
    gtk_list_store_clear(GOP_list_store);
    gtk_label_set_text(GTK_LABEL(GOP_label_filename), "");
    gtk_label_set_text(GTK_LABEL(GOP_label_GOP), _("No GOP selected"));
    gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),
        _("No Sequence Info Found") );
}

void update_GOP_info(uint32_t num)
{
    int i;
    GroupOfPictures *GOP;
    Bounds *picture_bounds;
    Bounds *packet_bounds;
    Picture *picture;
    List *GOPs;
    List *picture_list;
    List *packet_list;
    List *audio_list;
    List *video_list;
    Vector *head_vector;
    uint32_t picture_index, picture_max;
    uint32_t packet_min, packet_index, packet_max;
    uint32_t audio_index, audio_max;
    off_t gop_len, gop_start;
    off_t audio_len;
    //int   aindex, amax;
    uint8_t *head;
    int drop, hour, min, sec, pictures, closed, broken;

    GtkTreeIter iter;
    gchar datatype[128];
    gchar dataoffset[128];
    gchar datasize[128];

    gchar seqbuf[128];
    struct sequence_info info;

    /* wipe out the old list */
    gtk_list_store_clear(GOP_list_store);
    // wipe out labels
    sprintf(seqbuf,_("GOP %u information:"),num);
    gtk_label_set_text(GTK_LABEL(GOP_label_GOP), seqbuf);
    gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),
                       _("No Sequence Info Found"));

    if (!(GOPs = mpeg2parser->getGOPs()))
    {
        printf(_("no GOPs ?!\n"));
        goto failure;
    }
    if (!(packet_list = mpeg2parser->getPackets()))
    {
        printf(_("NULL picture list ?!\n"));
        goto failure;
    }
    if (!(picture_list = mpeg2parser->getPictures()))
    {
        printf(_("NULL picture list ?!\n"));
        goto failure;
    }
    if (!(audio_list = mpeg2parser->getAudio()))
    {
        printf(_("NULL audio list ?!\n"));
        goto failure;
    }
    if (!(video_list = mpeg2parser->getVideo()))
    {
        printf(_("NULL video list ?!\n"));
        goto failure;
    }
    if (!(GOP = (GroupOfPictures *) GOPs->vector(num)))
    {
        printf(_("no such GOP %d ?!\n"), num);
        goto failure;
    }
    if (!(head_vector = GOP->getHeader()))
    {
        printf(_("GOP header missing?!\n"));
        goto failure;
    }
    if (!(head = mpeg2parser->bytesAvail(head_vector->getStart(), 8)))
    {
        printf(_("GOP header not available?!\n"));
        goto failure;
    }

    // calculate GOP information
    /*
       4         5         6        7
       |         |         |        |
       7 65432 107654 3 210765 432107 6 543210
       1 11111 111111 1 111111 111111 1 1
       d hour  min    m sec    pic    c b
       r              a               l roken
       o              r               osed
       p              k
     */
    drop = ((head[4] & 0x80) > 0);
    hour = ((head[4] & 0x7C) >> 2);
    min = ((head[4] & 0x3) << 4) | ((head[5] & 0xF0) >> 4);

    // sec/picture decoding corrected, care of Scott Smith
    sec = ((head[5] & 0x7) << 3) | ((head[6] & 0xE0) >> 5);
    pictures = ((head[6] & 0x1F) << 1) | ((head[7] & 0x80) >> 7);

    closed = ((head[7] & 0x40) > 0);
    broken = ((head[7] & 0x20) > 0);

    if (!(picture_bounds = GOP->getPictureBounds()))
    {
        printf(_("NULL GOP picture bounds?!\n"));
        goto failure;
    }

    // FIXME: technically, this loop does a lot of needless
    //        reassigning of values.  However, this loop doesn't
    //        happen often, and it's the easiest way I could think
    //        of to sort by offset.

    // get picture bounds
    picture_index = picture_bounds->getFirst();
    picture_max = picture_bounds->getMax();

    for (; picture_index < picture_max; picture_index++)
    {
        Bounds *pic_bounds;
        uint32_t ves, ves_min, ves_max;
        Vector *pic_vector;
        off_t pic_start;
        off_t pic_len;

        if (!(picture = (Picture *) picture_list->vector(picture_index)))
        {
            printf(_("NULL picture?!\n"));
            goto failure;
        }

        if (!(pic_bounds = picture->getVideoBounds()))
        {
            printf(_("NULL picture bounds?!\n"));
            goto failure;
        }
        ves_min = pic_bounds->getFirst();
        ves_max = pic_bounds->getMax();
        pic_len = 0;
        for (ves = ves_min; ves < ves_max; ves++)
        {
            if (!(pic_vector = (Vector *) video_list->vector(ves)))
            {
                printf(_("NULL picture VES vector?!\n"));
                goto failure;
            }
            if (ves == ves_min)
                pic_start = pic_vector->getStart();
            pic_len += pic_vector->getLen();
        }

        snprintf(datatype,   128, _("  Picture (%s: %02d)"),
                frame_type_str(picture->getType()), picture->getTime());
        snprintf(dataoffset, 128, "%" OFF_T_FORMAT, pic_start);
        snprintf(datasize,   128, "%" OFF_T_FORMAT, pic_len);
        gtk_list_store_append(GOP_list_store, &iter);
        gtk_list_store_set(GOP_list_store, &iter,
                ITEM_DATATYPE, datatype,
                ITEM_OFFSET,   dataoffset,
                ITEM_SIZE,     datasize,
                -1);
    }

    // process GOP packets
    if (!(packet_bounds = GOP->getPacketBounds()))
    {
        printf(_("NULL GOP packet bounds?!\n"));
        goto failure;
    }
    packet_min = packet_bounds->getFirst();
    packet_max = packet_bounds->getMax();
    gop_len = 0;
    audio_len = 0;
    for (packet_index = packet_min; packet_index < packet_max; packet_index++)
    {
        Pack *packet;

        if (!(packet = (Pack *) packet_list->vector(packet_index)))
        {
            printf(_("NULL GOP packet vector?!\n"));
            goto failure;
        }
        if (packet_index == packet_min)
            gop_start = packet->getStart();
        gop_len += packet->getLen();

        audio_index = packet->getAudioFirst();
        audio_max = packet->getAudioMax();

        for (; audio_index < audio_max; audio_index++)
        {
            Bounds *audio_bounds;
            int aes, aes_min, aes_max;
            Vector *audio_vector;

            if (!(audio_vector = (Vector *) audio_list->vector(audio_index)))
            {
                printf(_("NULL GOP AES vector?!\n"));
                goto failure;
            }

            audio_len += audio_vector->getLen();

        }
    }

    // display GOP
    snprintf(datatype,   128, _("GOP (%02d:%02d:%02d.%02d%s%s%s)"),
            hour, min, sec, pictures,
            drop ? _(" drop") : _(" keep"),
            closed ? _(" closed") : _(" open"),
            broken ? _(" broken") : _(" whole"));
    snprintf(dataoffset, 128, "%" OFF_T_FORMAT, gop_start);
    snprintf(datasize,   128, "%" OFF_T_FORMAT, gop_len);
    gtk_list_store_prepend(GOP_list_store, &iter);
    gtk_list_store_set(GOP_list_store, &iter,
            ITEM_DATATYPE, datatype,
            ITEM_OFFSET,   dataoffset,
            ITEM_SIZE,     datasize,
            -1);

    // display audio info
    snprintf(datatype,   128, "%s", _("  Audio"));
    snprintf(dataoffset, 128, "%" OFF_T_FORMAT, gop_start);
    snprintf(datasize,   128, "%" OFF_T_FORMAT, audio_len);
    gtk_list_store_append(GOP_list_store, &iter);
    gtk_list_store_set(GOP_list_store, &iter,
            ITEM_DATATYPE, datatype,
            ITEM_OFFSET,   dataoffset,
            ITEM_SIZE,     datasize,
            -1);

    // update labels with info
    GOP->get_sequence_info(&info);    
    snprintf(seqbuf,128,_("%dx%d: %s aspect, %s fps, %s%s"),
        info.horizontal_size, info.vertical_size,
        aspect_str[info.aspect_ratio],
        frame_str[info.frame_rate],
        speed_str(info.bit_rate * 400),
        info.constrained ? _(", constrained") : "");
    gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),seqbuf);

  failure:
    /* perform any cleanups */
    while (0) {}; // keep compiler quiet
}

#define MAX_FRAME_TYPES 4
const char *frame_type_map[MAX_FRAME_TYPES] = {
    N_("Bad Frame"),
    N_("I-Frame"),
    N_("P-Frame"),
    N_("B-Frame")
};
const char *frame_type_str(int type)
{
    if (type >= MAX_FRAME_TYPES || type < 0)
        type = 0;
    return _(frame_type_map[type]);
}


uint32_t get_GOP_selected()
{
    return (uint32_t) gtk_range_get_value(GTK_RANGE(GOP_selector));
}

void set_GOP_selected(uint32_t num)
{
    uint32_t max;
    uint32_t old;

    max = mpeg2parser->numGOPs();
    // correct our bounds
    if (num >= max)
        num = max - 1;
    if (num < 0)
        num = 0;

    // remember where we were
    old = get_GOP_selected();

    gtk_range_set_value(GTK_RANGE(GOP_selector), (gdouble) num);
}

void handle_rc_load()
{
    if (rc_load(PACKAGE,parsable_items))
        fprintf(stderr,"%s",_("Could not load rc file -- using defaults.\n"));

    /* handle side-effects */

    /* synchronize the loaded/default values to the slider */
    options.run_speed = options.default_run_speed;
    gtk_range_set_value(GTK_RANGE(slider_run_speed), options.run_speed);

    mpeg2parser->set_ignore_endcode(options.ignore_endcode);
}

void Usage(char *title)
{
    printf("Usage: %s [OPTIONS] [FILE]\n\
\n\
  -h, --help         This help\n\
  -v, --vo DRIVER    Choose internal output driver.  Use 'help' for a list.\n\
  -p, --pipe CMD     Use external command for output.  Recommended:\n\
                        'mplayer -nocache -novm -'\n\
  -s, --states       Report libmpeg2 states\n\
  FILE               MPEG2 to load on startup\n\
\n", title);

    exit(1);
}

void handle_args(int argc, char *argv[])
{
    static struct option long_options[] = {
        { "help",   0, NULL, 'h' }, // 0
        { "vo",     1, NULL, 'v' },
        { "pipe",   1, NULL, 'p' },
        { "states", 0, NULL, 's' },
        { "version",0, NULL, 'V' },
        { 0,        0, 0,    0 }
    };

    /* do our option handling */
    while (1)
    {
        int c;
        int index;

        c = getopt_long(argc, argv, "Vhsv:p:", long_options, &index);

        if (c == -1)
            break; // done with args

        switch (c)
        {
            case 0:
                // got a matching long argument (with no short equiv)
                switch (index)
                {
                    default:
                        fprintf(stderr,
                                _("Unknown long argument index %d found!\n"),
                                index);
                }
                break;
            case 'h':
                Usage(argv[0]);
                break;
            case 'V':
                printf("GOPchop %s (%s)\n", VERSION, __DATE__);
                exit(0);
                break;
            case 's':
                opt_show_states=1;
                break;
            case 'v':
                opt_videoout=optarg;
                if (strcmp(opt_videoout,"help")==0)
                {
                    // display a list of libvo drivers
                    int i;
                    vo_driver_t const *drivers;

                    printf(_("Video output options:\n"));
                    drivers = vo_drivers();
                    for (i = 0; drivers[i].name; i++)
                        printf("\t%s\n",drivers[i].name);
                    exit(1);
                }
                else
                {
                    // verify selected libvo driver
                    if (!find_libvo_driver(opt_videoout))
                    {
                        fprintf(stderr,_("No such video driver: '%s'\n"),
                                opt_videoout);
                        exit(1);
                    }
                    desired_output=GOPCHOP_OUTPUT_LIBVO;
                }
                break;
            case 'p':
                opt_pipe=optarg;
                desired_output=GOPCHOP_OUTPUT_PIPE;
                break;
            default:
                fprintf(stderr, _("Unknown short argument '%c' found?!\n"), c);
                /* fall through to the Usage ... */
            case '?':
                Usage(argv[0]);
                break;
        }
    }
}

/*
 * There must be a way to hook up an event watcher to frame_window
 * directly.  Until I figure it out, this will have to do.  Overkill,
 * but it works.
 */
void gopchop_events(GdkEvent *event, gpointer data)
{
    GdkEventExpose *expose = (GdkEventExpose*)event;
    if (event)
        switch (event->type)
        {
            // this doesn't catch window movement :(
            case GDK_VISIBILITY_NOTIFY: // forces repaint of frame window
                // don't attempt repaint while totally hidden
                if (((GdkEventVisibility*)event)->state == GDK_VISIBILITY_FULLY_OBSCURED)
                    break;

                if (display_is_window(engine,((GdkEventAny*)event)->window))
                    display_redraw(engine);

                break;
            /*
            case GDK_EXPOSE: // forces repaint of frame window
                if (display_is_window(engine,expose->window))
                    display_redraw(engine);

                break;
                */
        }
    
    // process events normally
    gtk_main_do_event(event);
    return;
}

int main(int argc, char *argv[])
{


    /* deal with internationalization support */
#ifdef ENABLE_NLS
    setlocale(LC_ALL, "");
    bindtextdomain(GETTEXT_PACKAGE, PACKAGE_DATA_DIR "/locale");
    textdomain(GETTEXT_PACKAGE);
#endif
    gtk_set_locale();

    /* pass command line args to gtk (which may modify them) */
    gtk_init(&argc, &argv);
    /* handle our command line args */
    handle_args(argc, argv);

    /* setup the parsing object */
    mpeg2parser = new MPEG2Parser;

    /* set up all the gtk windows and widgets */
    setup_gtk_stuff();

    /* verify that we're working with largefile support
     * (report to Gtk window)
     */
    if (sizeof(off_t) < 8)
    {
        show_error(_("Type 'off_t' is less than 8 bytes.\n"
                     "Largefile support requires at least 8 bytes.\n"));
        gtk_widget_show(error_dialog);
    }

    /* load any saved options (prior to opening the first file) */
    handle_rc_load();

    /* attempt to load the filename mentioned on the command line 
     * (requires MPEG2Parser and GTK loaded
     */
    if (optind < argc)
        open_file(argv[optind]);

    /* start the event handler */
    gdk_event_handler_set(gopchop_events,NULL,NULL);
    gtk_main();
    return 0;
}


/* vi:set ai ts=4 sw=4 expandtab: */
