/* cqcam - Color Quickcam capture programs
 * Copyright (C) 1996-1998 by Patrick Reynolds
 *
 * 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 library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include <gtk/gtkalignment.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkcontainer.h>
#include <gtk/gtkdrawingarea.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkmain.h>
#include <gtk/gtktooltips.h>
#include <gtk/gtksignal.h>
#include <gtk/gtkvbox.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtkwindow.h>

#include <gdk/gdk.h>
#include <gdk/gdktypes.h>

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>

#include "options.h"
#include "dimensions.h"
#include "saveas.h"
#include "gcam.h"
#include "ximages.h"
#include <cqcsrv.h>
#include <imager.h>

static GtkWidget *options_dialog = 0;
static GtkWidget *dimensions_dialog = 0;
static GtkWidget *fps_label;

static GdkImage *img = 0;
static unsigned char *img_buf = 0;
static int image_width, image_height, image_bpp;

static int input_tag = 0;

static void prepare_saveas() {
  do_saveas(img_buf, image_width, image_height);
}

static void destroy(GtkWidget *, GtkWidget **foo) {
  *foo = 0;
}

#define CHK(a) if (a == -1) goto fail;
static void camera_setup() {
  CHK(send_command(out_fd, CQCSRV_SET_BPP, options_32bpp ? 32 : 24));
  CHK(send_command(out_fd, CQCSRV_SET_BRIGHTNESS, options_brightness));
  CHK(send_command(out_fd, CQCSRV_SET_DECIMATION, dimensions_decimation));
  CHK(send_command(out_fd, CQCSRV_SET_WIDTH, dimensions_width));
  CHK(send_command(out_fd, CQCSRV_SET_HEIGHT, dimensions_height));
  CHK(send_command(out_fd, CQCSRV_SET_TOP, dimensions_top));
  CHK(send_command(out_fd, CQCSRV_SET_LEFT, dimensions_left));
  CHK(send_get_frame(out_fd));
	return;
fail:
	fprintf(stderr, "Send failed.  Child seems to have gone away.\n");
	exit(1);
}

static void image_draw(GtkWidget *wid, GdkEventExpose *ev) {
  if (!img) return;

  GdkWindow *wn = wid->window;
  GdkGC *gc = gdk_gc_new(wn);
  gdk_gc_set_exposures(gc, TRUE);  // ?? from gimp
  gdk_draw_image(wn, gc, img, ev->area.x, ev->area.y, ev->area.x, ev->area.y,
    ev->area.width, ev->area.height);
  gdk_gc_destroy(gc);
}

static void update_fps() {
  static struct timeval old = { 0, 0 }, last_update = { 0, 0 };
  static int frames;
  struct timeval now;
  int update_diff;

  gettimeofday(&now, NULL);
  if (old.tv_sec == 0) { old = now; return; }
  update_diff = 1000000*(now.tv_sec - last_update.tv_sec) +
    (now.tv_usec - last_update.tv_usec);
  frames++;
  if (last_update.tv_sec == 0 || update_diff >= 3000000) {
    int time_diff = (1000000*(now.tv_sec - old.tv_sec) + (now.tv_usec - old.tv_usec));
    float rate = 1000000.0/time_diff*frames;
    char buf[16];
    snprintf(buf, 16, "%0.2f fps", rate);
#ifdef HAVE_GTK_1_0
    gtk_label_set(GTK_LABEL(fps_label), buf);
#else
    gtk_label_set_text(GTK_LABEL(fps_label), buf);
#endif
    last_update = old = now;
    frames = 0;
  }
}

static void handle_image(void *wid, int fd, GdkInputCondition) {
  // !! these first two checks are ugly, because the function doesn't read
  //    from the fd, so the fd will still fire, looping tightly back to here
  if (saveas_in_progress) return;
  if (fd != in_fd) {
    fprintf(stderr,
      "Warning: input received on mystery fd %d (instead of %d)\n", fd, in_fd);
    return;
  }

  int t = 0, n = 0, sz;
  read(fd, &image_bpp,            sizeof(image_bpp));
  read(fd, &image_width,          sizeof(image_width));
  read(fd, &image_height,         sizeof(image_height));
  sz = image_width*image_height*((image_bpp==32)?1:3);
  
  GdkVisual *visual = gdk_visual_get_system();  
  if (img_buf) free(img_buf);
  img_buf = (unsigned char *)malloc(sz);
  if (!img_buf) return;

  if (img) gdk_image_destroy(img);
  img = gdk_image_new(GDK_IMAGE_FASTEST, visual, image_width, image_height);

  gtk_widget_set_usize(((GtkWidget*)wid), image_width, image_height);
  GdkWindow *wn = ((GtkWidget*)wid)->window;

#ifdef DEBUG
  fprintf(stderr, "Reading %dx%d image @ %d bpp...", image_width, image_height,
    image_bpp);
#endif
  while (t < sz && (n = read(fd, img_buf+t, sz-t)) > 0)
    t += n;
	if (n == 0) {
		fprintf(stderr, "EOF from child.\n");
		exit(1);
	}
	if (n == -1) {
		perror("read");
		exit(1);
	}
#ifdef DEBUG
  fprintf(stderr, " done\n");
#endif

  update_fps();

  if (image_bpp == 32)
    img_buf = raw32_to_24(img_buf, image_width, image_height,
      options_despeckle);
  else
    if (options_despeckle) img_buf = despeckle(img_buf, image_width,
      image_height);

  if (options_auto_b) {
    int btemp;
    if (!get_brightness_adj(img_buf, image_width*image_height, btemp)) {
      options_brightness += btemp;
      if (options_brightness < 1) options_brightness = 1;
      if (options_brightness > 254) options_brightness = 254;
      send_command(out_fd, CQCSRV_SET_BRIGHTNESS, options_brightness);
    }
  }
  if (options_auto_rgb)
    get_rgb_adj(img_buf, image_width*image_height, options_red, options_green,
      options_blue);
  if (options_dialog && (options_auto_rgb || options_auto_b))
    options_feedback();
  do_rgb_adj(img_buf, image_width*image_height, options_red, options_green,
    options_blue);    
//  splut_buf_into_img(img_buf, img, image_width*image_height);
  prepare_ximage((unsigned char*)img->mem, img_buf, image_width, image_height,
    img->bpp*8, (void*)img->visual, 1);

  GdkGC *gc = gdk_gc_new(wn);
  gdk_gc_set_exposures(gc, TRUE);  // ?? from gimp
  gdk_draw_image(wn, gc, img, 0, 0, 0, 0, image_width, image_height);
  gdk_gc_destroy(gc);

  send_command(out_fd, CQCSRV_SET_BPP, options_32bpp ? 32 : 24);
  send_get_frame(out_fd);
}

static void show_options() {
  if (options_dialog) return;
  options_dialog = options_create_window();
  gtk_signal_connect(GTK_OBJECT(options_dialog), "destroy",
    GTK_SIGNAL_FUNC(destroy), &options_dialog);
  gtk_widget_show_all(options_dialog);
}

static void show_dimensions() {
  if (dimensions_dialog) return;
  dimensions_dialog = dimensions_create_window();
  gtk_signal_connect(GTK_OBJECT(dimensions_dialog), "destroy",
    GTK_SIGNAL_FUNC(destroy), &dimensions_dialog);
  gtk_widget_show_all(dimensions_dialog);
}

GtkWidget *create_main_window() {
	signal(SIGPIPE, SIG_IGN);

  GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(w), "Gcam");
  gtk_window_set_policy(GTK_WINDOW(w), FALSE, FALSE, TRUE);
#ifdef HAVE_GTK_1_0
  gtk_container_border_width(GTK_CONTAINER(w), 8);
#else
  gtk_container_set_border_width(GTK_CONTAINER(w), 8);
#endif
  gtk_signal_connect(GTK_OBJECT(w), "destroy",
    GTK_SIGNAL_FUNC(gtk_main_quit), 0);

  GtkWidget *vbox = gtk_vbox_new(FALSE, 10);
  GtkWidget *hbox = gtk_hbox_new(TRUE, 12);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  GtkTooltips *tip;

  GtkWidget *quit = gtk_button_new_with_label("Quit");
  gtk_signal_connect(GTK_OBJECT(quit), "clicked",
    GTK_SIGNAL_FUNC(gtk_main_quit), 0);
  gtk_box_pack_start(GTK_BOX(hbox), quit, TRUE, TRUE, 0);
  tip = gtk_tooltips_new();
  gtk_tooltips_set_tip(tip, quit, "Quit gcam", 0);

  GtkWidget *saveas = gtk_button_new_with_label("Take picture");
  gtk_box_pack_start(GTK_BOX(hbox), saveas, TRUE, TRUE, 0);
  gtk_signal_connect(GTK_OBJECT(saveas), "clicked",
    GTK_SIGNAL_FUNC(prepare_saveas), 0);
  tip = gtk_tooltips_new();
  gtk_tooltips_set_tip(tip, saveas, "Store the current picture to disk", 0);

  GtkWidget *opt = gtk_button_new_with_label("Options...");
  gtk_box_pack_start(GTK_BOX(hbox), opt, TRUE, TRUE, 0);
  gtk_signal_connect(GTK_OBJECT(opt), "clicked", 
    GTK_SIGNAL_FUNC(show_options), 0);
  tip = gtk_tooltips_new();
  gtk_tooltips_set_tip(tip, opt, "Change camera and image options", 0);

  GtkWidget *dim = gtk_button_new_with_label("Dimensions...");
  gtk_box_pack_start(GTK_BOX(hbox), dim, TRUE, TRUE, 0);
  gtk_signal_connect(GTK_OBJECT(dim), "clicked", 
    GTK_SIGNAL_FUNC(show_dimensions), 0);
  tip = gtk_tooltips_new();
  gtk_tooltips_set_tip(tip, dim, "Change frame size, zoom, and position", 0);

  GtkWidget *center_it = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);  
  GtkWidget *target = gtk_drawing_area_new();
  gtk_drawing_area_size(GTK_DRAWING_AREA(target), dimensions_width,
    dimensions_height);
  gtk_container_add(GTK_CONTAINER(center_it), target);
  gtk_box_pack_start(GTK_BOX(vbox), center_it, TRUE, TRUE, 0);
  input_tag = gdk_input_add(in_fd, GDK_INPUT_READ, handle_image, target);
  camera_setup();
  gtk_widget_set_events(target, GDK_EXPOSURE_MASK);
#ifdef HAVE_GTK_1_0
  gtk_signal_connect(GTK_OBJECT(target), "expose_event",
    GTK_SIGNAL_FUNC(image_draw), 0);
#endif

  fps_label = gtk_label_new("0.00 fps");
  gtk_box_pack_start(GTK_BOX(vbox), fps_label, FALSE, FALSE, 0);
  
  gtk_container_add(GTK_CONTAINER(w), vbox);

  return w;
}
