// Copyright (C) 2019 Jaslo Ziska

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <gtk/gtk.h>
#include <glib.h>

#include <mipea/gpio.h>
#include <curl/curl.h>
#include <qrencode.h>

#include "photobox_photo.h"
#include "apikey.h" // are you looking for this? :)

// missing in glib???
#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f))

void pb_exit(void);

void pb_show_main(void);
void pb_show_send(void);
void pb_show_qr(void);

gboolean pb_poll_buttton(void);
gboolean pb_countdown(void);
void pb_takepic(void);

int pb_cp_dp(char *id);

static GtkWidget *img;
static GtkWidget *button_take;
static GtkWidget *label_countdown;

static GtkWidget *button_upload;
static GtkWidget *button_cancel;

static GtkWidget *qr_img;
static GtkWidget *qr_label;
static GtkWidget *qr_button_back;

static const unsigned int IMG_WIDTH = 512;
static const unsigned int IMG_HEIGHT = 384;
static const unsigned int QR_SIZE = 350;

static unsigned int button_pin = 26;

int main(int argc, char *argv[])
{
        if (pb_ph_init() != 0) {
                fprintf(stderr, "ERROR in pb_ph_init\n");
                return EXIT_FAILURE;
        }
        if (gpio_map() == NULL) {
                fprintf(stderr, "ERROR in gpio_map\n");
                return EXIT_FAILURE;
        }
        gtk_init(&argc, &argv);

        signal(SIGCHLD, SIG_IGN);

        // GPIO
        gpio_inp(button_pin);
        gpio_pud(button_pin, PUD_UP);

        // WINDOW
        GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(window), "Photobox");
        gtk_window_fullscreen(GTK_WINDOW(window));
        //g_signal_connect(window, "delete-event", G_CALLBACK(pb_gui_on_delete_main), NULL);
        g_signal_connect(window, "destroy", G_CALLBACK(pb_exit), NULL);
        // hide cursor
        gtk_widget_realize(window);
        GdkCursor* cursor_blank = gdk_cursor_new(GDK_BLANK_CURSOR);
        gdk_window_set_cursor(gtk_widget_get_window(window), cursor_blank);

        // BOX VERTICAL
        GtkWidget *box_v = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
        gtk_container_add(GTK_CONTAINER(window), box_v);
        gtk_widget_show(box_v);

        // BOX HORIZONTAL
        GtkWidget *box_h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
        gtk_box_pack_end(GTK_BOX(box_v), box_h, TRUE, FALSE, 0);
        gtk_widget_show(box_h);

        // IMAGE
        img = gtk_image_new_from_file("no_signal.png");
        gtk_box_pack_end(GTK_BOX(box_v), img, TRUE, FALSE, 0);

        // BUTTON: TAKE PICTURE
        button_take = gtk_button_new();
        // label
        GtkWidget *button_take_label = gtk_label_new(NULL);
        gtk_label_set_markup(GTK_LABEL(button_take_label), "<span font='50'>Bild aufnehmen</span>");
        gtk_container_add(GTK_CONTAINER(button_take), button_take_label);
        gtk_widget_show(button_take_label);
        // /label
        gtk_box_pack_start(GTK_BOX(box_h), button_take, TRUE, FALSE, 0);
        g_signal_connect(button_take, "clicked", G_CALLBACK(pb_countdown), NULL);

        // COUNTDOWN LABEL
        label_countdown = gtk_label_new(NULL);
        gtk_box_pack_end(GTK_BOX(box_v), label_countdown, TRUE, FALSE, 0);

        // BUTTON: UPLOAD
        button_upload = gtk_button_new_with_label("Hochladen");
        //g_signal_connect(button_upload, "clicked", G_CALLBACK(pb_exit), NULL);
        g_signal_connect(button_upload, "clicked", G_CALLBACK(pb_show_qr), NULL);
        gtk_box_pack_start(GTK_BOX(box_h), button_upload, TRUE, FALSE, 0);

        // BUTTON: CANCEL
        button_cancel = gtk_button_new_with_label("Abbrechen");
        //g_signal_connect(button_cancel, "clicked", G_CALLBACK(pb_exit), NULL);
        g_signal_connect(button_cancel, "clicked", G_CALLBACK(pb_show_main), NULL);
        gtk_box_pack_start(GTK_BOX(box_h), button_cancel, TRUE, FALSE, 0);

        // QR CODE IMAGE
        qr_img = gtk_image_new_from_file("no_signal.png");
        gtk_box_pack_end(GTK_BOX(box_v), qr_img, TRUE, FALSE, 0);

        // QR CODE LABEL
        qr_label = gtk_label_new(NULL);
        gtk_label_set_justify(GTK_LABEL(qr_label), GTK_JUSTIFY_CENTER);
        gtk_label_set_line_wrap(GTK_LABEL(qr_label), TRUE);
        gtk_box_pack_end(GTK_BOX(box_v), qr_label, TRUE, FALSE, 0);

        // QR BUTTON BACK
        qr_button_back = gtk_button_new_with_label("Zurück");
        g_signal_connect(qr_button_back, "clicked", G_CALLBACK(pb_show_main), NULL);
        //g_signal_connect(qr_button_back, "clicked", G_CALLBACK(pb_exit), NULL);
        gtk_box_pack_start(GTK_BOX(box_h), qr_button_back, TRUE, FALSE, 0);

        pb_show_main();
        gtk_widget_show(window);

        gtk_main();

        return EXIT_SUCCESS;
}

void pb_exit(void)
{
        gpio_unmap();
        pb_ph_uninit();
        gtk_main_quit();
}

void pb_show_main(void)
{
        gtk_widget_hide(img);
        gtk_widget_hide(button_upload);
        gtk_widget_hide(button_cancel);
        gtk_widget_hide(qr_img);
        gtk_widget_hide(qr_label);
        gtk_widget_hide(qr_button_back);

        gtk_widget_show(button_take);

        // take picture with button (ugly)
        g_idle_add(G_SOURCE_FUNC(pb_poll_buttton), NULL);
}

void pb_show_send(void)
{
        gtk_widget_show(img);
        gtk_widget_show(button_upload);
        gtk_widget_show(button_cancel);

        gtk_widget_hide(label_countdown);
}

void pb_show_qr(void)
{
        gtk_widget_hide(img);
        gtk_widget_hide(button_upload);
        gtk_widget_hide(button_cancel);

        gtk_widget_show(qr_img);
        gtk_widget_show(qr_label);
        gtk_widget_show(qr_button_back);

        // id from time
        char id[7];
        strftime(id, 7, "%H%M%S", localtime(&(time_t) {time(NULL)}));

        pid_t pid = fork();
        if (pid == 0) {
                // child -> copy and upload
                if (pb_cp_dp(id) != 0) {
                        fprintf(stderr, "ERROR in pb_cp_dp\n");
                        exit(EXIT_FAILURE);
                } else {
                        exit(EXIT_SUCCESS);
                }
        } else if (pid < 0) {
                fprintf(stderr, "ERROR in fork (big problem)\n");

                // try to at least save the picture (the ugly way)
                char *cmd = "cp tmp.jpg bilder/xxxxxx.jpg";
                for (unsigned int i = 0; i < 6; ++i) {
                        cmd[i + 18] = id[i];
                }
                if (system(cmd) != 0)
                        perror("ERROR in system(), picture could not be saved");

                // go back to start, dont show qr
                pb_show_main();
                return;
        }

        // parent -> continue GUI

        // url qr should point to
        // example: https://www.dropbox.com/sh/3pq0pwednyuig86/AACx0_vjn-liY5_pP_C3nJD8a?dl=0&preview=c1b052c5.jpg
        char url[] = "https://www.dropbox.com/sh/3pq0pwednyuig86/AACx0_vjn-liY5_pP_C3nJD8a?dl=0&preview=xxxxxx.jpg";
        for (unsigned int i = 0; i < 6; ++i) {
                url[i + 82] = id[i];
        }

        // generate qr code
        QRcode *qr = QRcode_encodeString(url, 0, QR_ECLEVEL_H, QR_MODE_8, 1);
        if (qr == NULL) {
                perror("Failed to generate QR-Code");
                pb_show_main();
                return;
        }

        unsigned int size = qr->width;
        char values[10];
        sprintf(values, "%d %d 2 1", size, size);

        const char *xface[3 + size];
        xface[0] = values;
        xface[1] = "a c #ffffff";
        xface[2] = "b c #000000";

        char qrcode_char[size][size + 1];
        for (unsigned int i = 0; i < size; ++i) {
                qrcode_char[i][size] = '\0';
                for (unsigned int j = 0; j < size; ++j) {
                        qrcode_char[i][j] = qr->data[j + i * size] & 1 ? 'b' : 'a';
                }
                xface[3 + i] = &qrcode_char[i][0];
        }
        QRcode_free(qr);

        GdkPixbuf *pb = gdk_pixbuf_new_from_xpm_data(xface);

        pb = gdk_pixbuf_scale_simple(pb, QR_SIZE, QR_SIZE, GDK_INTERP_NEAREST);
        gtk_image_set_from_pixbuf((GtkImage *)qr_img, pb);
        g_clear_object(&pb);

        char text[] = "<span font='12'>QR-Code scannen oder auf <b>https://www.dropbox.com/sh/3pq0pwednyuig86/AACx0_vjn-liY5_pP_C3nJD8a</b> gehen. <small>Der Upload kann einige Minuten dauern.</small></span>";
        gtk_label_set_markup(GTK_LABEL(qr_label), text);
}

// not proud of this
gboolean pb_poll_buttton(void)
{
        if (gpio_tst(button_pin) == 0) {
                pb_countdown();
                return G_SOURCE_REMOVE;
        } else {
                return G_SOURCE_CONTINUE;
        }
}

// neither of this
gboolean pb_countdown(void)
{
        static int num = 0;

        switch(num) {
                case 0:
                        num = 5;

                        g_idle_remove_by_data(NULL);

                        gtk_label_set_markup(GTK_LABEL(label_countdown),
                                "<span font='300' color='red'>5</span>");
                        gtk_widget_hide(button_take);
                        gtk_widget_show(label_countdown);

                        g_timeout_add(1000, G_SOURCE_FUNC(pb_countdown), NULL);
                        return G_SOURCE_CONTINUE;
                case 5:
                        num = 4;
                        gtk_label_set_markup(GTK_LABEL(label_countdown),
                                "<span font='300' color='red'>4</span>");
                        return G_SOURCE_CONTINUE;
                case 4:
                        num = 3;
                        gtk_label_set_markup(GTK_LABEL(label_countdown),
                                "<span font='300' color='red'>3</span>");
                        return G_SOURCE_CONTINUE;
                case 3:
                        num = 2;
                        gtk_label_set_markup(GTK_LABEL(label_countdown),
                                "<span font='300' color='red'>2</span>");
                        return G_SOURCE_CONTINUE;
                case 2:
                        num = 1;
                        gtk_label_set_markup(GTK_LABEL(label_countdown),
                                "<span font='300' color='red'>1</span>");
                        return G_SOURCE_CONTINUE;
                case 1:
                        num = 0;
                        gtk_label_set_markup(GTK_LABEL(label_countdown),
                                "<span font='300' color='red'>0</span>");

                        // do all pendfing events (show 0) before taking picture
                        while (gtk_events_pending()) {
                                gtk_main_iteration();
                        }

                        // take pic
                        pb_takepic();
                        pb_show_send();
                        return G_SOURCE_REMOVE;
        }

        printf("ERROR: this should never happen?\n");
        return G_SOURCE_REMOVE;
}

void pb_takepic(void)
{
        char *fn = "tmp.jpg";
        if (pb_ph_capture_file(fn) != 0) {
                fprintf(stderr, "ERROR in pb_ph_capture_file\n");
                gtk_main_quit();
                return;
        }

        GdkPixbuf *pb = gdk_pixbuf_new_from_file(fn, NULL);
        pb = gdk_pixbuf_scale_simple(pb, IMG_WIDTH, IMG_HEIGHT, GDK_INTERP_BILINEAR);
        gtk_image_set_from_pixbuf(GTK_IMAGE(img), pb);
        g_clear_object(&pb);
}

int pb_cp_dp(char *id)
{
        char buffer[4096];
        size_t num_read;
        size_t dest_fsize;

        char src[] = "tmp.jpg";
        char dest[] = "bilder/xxxxxx.jpg";
        char dropbox_arg[] = "Dropbox-API-Arg: {\"path\": \"/bilder/xxxxxx.jpg\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}";

        for (unsigned int i = 0; i < 6; ++i) {
                dest[i + 7] = id[i];
                dropbox_arg[i + 35] = id[i];
        }

        FILE *src_file = fopen(src, "rb");
        if (src_file == NULL) {
                perror("Error opening tmp.jpg");
                return -1;
        }
        FILE *dest_file = fopen(dest, "wb+");
        if (dest_file == NULL) {
                fclose(src_file);
                perror("Error opening destination file");
                return -1;
        }

        // copy file
        do {
                num_read = fread(buffer, 1, 4096, src_file);
                dest_fsize += num_read;
                if (fwrite(buffer, 1, num_read, dest_file) != num_read && ferror(dest_file) != 0) {
                        fprintf(stderr, "Error writing to destination file");
                        fclose(src_file);
                        goto error_file;
                }
        } while(num_read == 4096);

        if (ferror(src_file) != 0) {
                fclose(src_file);
                fprintf(stderr, "Error reading from tmp.jpg\n");
                perror("^");
                goto error_file;
        }

        fclose(src_file);

        rewind(dest_file); // from beginning for upload

        // upload
        curl_global_init(CURL_GLOBAL_ALL);
        CURL *curl = curl_easy_init();
        if (curl == NULL) {
                fprintf(stderr, "ERROR in curl_easy_init\n");
                curl_global_cleanup();
                goto error_file;
        }

        struct curl_slist *header = NULL;
        header = curl_slist_append(header, "Authorization: Bearer " APIKEY);
        header = curl_slist_append(header, dropbox_arg);
        header = curl_slist_append(header, "Content-Type: application/octet-stream");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);

        curl_easy_setopt(curl, CURLOPT_URL, "https://content.dropboxapi.com/2/files/upload");

        curl_easy_setopt(curl, CURLOPT_POST, 1L); // post request
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, dest_fsize); // file size
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); // use read callback
        curl_easy_setopt(curl, CURLOPT_READDATA, dest_file); // file pointer
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); // default callback

        curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL); // disable output

        CURLcode ret = curl_easy_perform(curl);
        if (ret != CURLE_OK) {
                fprintf(stderr, "ERROR in curl_easy_perform: %s\n", curl_easy_strerror(ret));
                goto error_curl;
        }

        error_curl:
        curl_slist_free_all(header);
        curl_easy_cleanup(curl);

        curl_global_cleanup();

        error_file:
        fclose(dest_file);

        return ret;
}
