#include <hildon/hildon.h>
#include <hildon/hildon-file-chooser-dialog.h>
#include <libosso.h>
#include <cv.h>
#include <highgui.h>
#define SERVICE "es.linux-magazne.n900"

char *cascade_home = "/home/user/ocv";
char *selected_cascade = NULL;

static gchar *select_file(GtkWidget *win) {
  gchar *file_name = NULL;

  GtkWidget *file_dialog =
    hildon_file_chooser_dialog_new(GTK_WINDOW(win),
				   GTK_FILE_CHOOSER_ACTION_OPEN);

  gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_dialog),
				      "/home/user/MyDocs");

  if (gtk_dialog_run(GTK_DIALOG(file_dialog)) == GTK_RESPONSE_OK) {
    file_name =
      gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_dialog));
  }

  gtk_widget_destroy(file_dialog);

  return file_name;
}

static GtkImage *detect_and_convert(IplImage *img,
				    CvHaarClassifierCascade *cascade) {
  int scale = MAX(cvRound(img->width/800), cvRound(img->height/480));
  IplImage *img_small = cvCreateImage(cvSize(img->width/scale,
					     img->height/scale),
				      IPL_DEPTH_8U, 3);
  cvResize(img, img_small, CV_INTER_LINEAR);

  IplImage *gray_img = cvCreateImage(cvSize(img_small->width,
					    img_small->height),
				     IPL_DEPTH_8U, 1);
  cvCvtColor(img_small, gray_img, CV_RGB2GRAY);
  cvEqualizeHist(gray_img, gray_img);

  CvMemStorage *storage = cvCreateMemStorage(0);
  CvSeq *faces = cvHaarDetectObjects(gray_img, cascade, storage, 1.2, 2,
				     CV_HAAR_SCALE_IMAGE, cvSize(30, 30));

  for (int i = 0; i < faces->total; i++) {
    CvRect *rect = (CvRect*)cvGetSeqElem(faces, i);

    CvPoint tl = cvPoint(rect->x, rect->y);
    CvPoint br = cvPoint(rect->x+rect->width, rect->y+rect->height);

    cvRectangle(img_small, tl, br, CV_RGB(255, 0, 0), 3, 8, 0);
  }

  cvReleaseImage(&img);
  cvReleaseImage(&gray_img);

  IplImage *img_rgb = cvCreateImage(cvSize(img_small->width, img_small->height),
				    img_small->depth, 3);
  cvCvtColor(img_small, img_rgb, CV_BGR2RGB);
  cvReleaseImage(&img_small);

  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data((guchar*)img_rgb->imageData,
					       GDK_COLORSPACE_RGB, FALSE,
					       img_rgb->depth,
					       img_rgb->width,
					       img_rgb->height,
					       img_rgb->widthStep,
					       NULL, NULL);

  GtkImage *gimg = (GtkImage*)gtk_image_new_from_pixbuf(pixbuf);

  return gimg;
}

static void show_opencv_window(void) {
  GtkWidget *win = hildon_stackable_window_new();
  gtk_window_set_title(GTK_WINDOW(win), "OpenCV");

  gchar *img_file = select_file(win);

  IplImage *img = NULL;
  if (img_file) {
    img = cvLoadImage(img_file, CV_LOAD_IMAGE_UNCHANGED);
  }

  if (!img) {
    hildon_banner_show_information(win, NULL, "Fichero no encontrado");
  } else {
    gchar *cascade_file = g_strdup_printf("%s/%s", cascade_home,
					  selected_cascade);

    CvHaarClassifierCascade *cascade =
      (CvHaarClassifierCascade*)cvLoad(cascade_file, 0, 0, 0);

    g_free(cascade_file);

    if (!cascade) {
      hildon_banner_show_information(win, NULL,
				     "Clasificador no encontrado.");
    } else {
      GtkImage *gimg = detect_and_convert(img, cascade);
      cvReleaseHaarClassifierCascade(&cascade);

      gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(gimg));
    }
  }

  gtk_widget_show_all(win);
}

static void show_about_window(void) {
  GtkWidget *win = hildon_stackable_window_new();
  gtk_window_set_title(GTK_WINDOW(win), "About");

  gchar *text =
    "Esta aplicacion es para Linux-Magazine.\n"
    "Muestra el uso de Maemo5 y OpenCV para un N900.";
  GtkWidget *label = gtk_label_new(text);
  gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);

  gtk_container_add(GTK_CONTAINER(win), label);
  gtk_widget_show_all(win);
}

static HildonAppMenu *create_menu(void) {
  HildonAppMenu *menu = HILDON_APP_MENU(hildon_app_menu_new());

  GtkWidget *button = hildon_gtk_button_new(HILDON_SIZE_AUTO);
  gtk_button_set_label(GTK_BUTTON(button), "About");
  g_signal_connect(button, "clicked", G_CALLBACK(show_about_window),
		   NULL);

  hildon_app_menu_append(menu, GTK_BUTTON(button));

  gtk_widget_show_all(GTK_WIDGET(menu));

  return menu;
}

static void selection_changed(HildonTouchSelector *selector, gint column,
			      gpointer user_data) {
  selected_cascade = hildon_touch_selector_get_current_text(selector);
}

static GtkWidget *create_simple_selector() {
  GtkWidget *selector = hildon_touch_selector_new_text();
  g_signal_connect(G_OBJECT(selector), "changed",
		   G_CALLBACK(selection_changed), NULL);

  char *cascades[] = {
    "haarcascade_frontalface_default.xml",
    "haarcascade_frontalface_alt.xml",
    "haarcascade_frontalface_alt2.xml",
    "haarcascade_frontalface_alt_tree.xml",
    NULL
  };

  for (char **cascade_name = cascades; *cascade_name; cascade_name++) {
    hildon_touch_selector_append_text(HILDON_TOUCH_SELECTOR(selector),
				      *cascade_name);
  }

  hildon_touch_selector_set_column_selection_mode(
			    HILDON_TOUCH_SELECTOR(selector),
			    HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE);
  hildon_touch_selector_set_active(HILDON_TOUCH_SELECTOR(selector), 0, 0);

  return selector;
}

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

  osso_context_t *osso_context = osso_initialize(SERVICE, "0.0.1", TRUE, NULL);
  if (osso_context == NULL) {
    return OSSO_ERROR;
  }

  HildonProgram *program = hildon_program_get_instance();
  g_set_application_name("N900-OpenCV");

  GtkWidget *win = hildon_stackable_window_new();
  hildon_program_add_window(program, HILDON_WINDOW(win));
  gtk_window_set_title(GTK_WINDOW(win), "N900 y OpenCV");

  GtkWidget *button_ocv =
    hildon_button_new_with_text(HILDON_SIZE_HALFSCREEN_WIDTH
				| HILDON_SIZE_THUMB_HEIGHT,
				HILDON_BUTTON_ARRANGEMENT_VERTICAL,
				"Abrir Imagen", NULL);
  g_signal_connect(button_ocv, "clicked",
		   G_CALLBACK(show_opencv_window), NULL);

  GtkWidget *button_cascade =
    hildon_picker_button_new(HILDON_SIZE_AUTO_WIDTH
			    | HILDON_SIZE_FINGER_HEIGHT,
			    HILDON_BUTTON_ARRANGEMENT_VERTICAL);
  hildon_button_set_title(HILDON_BUTTON(button_cascade),
			  "Tipo Clasificador");

  GtkWidget *selector = create_simple_selector();
  hildon_picker_button_set_selector(HILDON_PICKER_BUTTON(button_cascade),
				    HILDON_TOUCH_SELECTOR(selector));

  GtkBox *vbox = GTK_BOX(gtk_vbox_new(TRUE, 10));
  gtk_box_pack_start(vbox, button_ocv, FALSE, FALSE, 0);
  gtk_box_pack_start(vbox, button_cascade, FALSE, FALSE, 0);

  GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0);
  gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(vbox));

  gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(align));

  HildonAppMenu *menu = create_menu();
  hildon_window_set_app_menu(HILDON_WINDOW(win), menu);

  g_signal_connect(G_OBJECT(win),
		   "delete_event", G_CALLBACK(gtk_main_quit),
		   NULL);

  gtk_widget_show_all(GTK_WIDGET(win));
  gtk_main();

  osso_deinitialize(osso_context);

  return 0;
}
