#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Value_Slider.H>
#include <FL/Fl_Button.H>
#include <FL/fl_file_chooser.H>
#include <FL/fl_ask.H>

#include "Fl_Marquee.H"

class Main_View {

	Fl_Window* window;
	Fl_Value_Slider* speed_value;
	Fl_Slider* font_height;
	Fl_Slider* jump_size;
	Fl_Slider* view_index;
	Fl_Box* display;
	Fl_Button* rewind_button;
	Fl_Button* stop_button;
	Fl_Button* play_button;
	Fl_Button* load_button;
	Fl_Marquee* marquee;

	char* filename;
	int fd;

	static void rewind_button_cb(Fl_Widget* button, void* data) {
		Fl_Marquee* marquee = (Fl_Marquee*)data;
		marquee->direction(-1);
		marquee->start();
	}

	static void stop_button_cb(Fl_Widget* button, void* data) {
		Fl_Marquee* marquee = (Fl_Marquee*)data;
		marquee->stop();
	}

	static void play_button_cb(Fl_Widget* button, void* data) {
		Fl_Marquee* marquee = (Fl_Marquee*)data;
		marquee->direction(1);
		marquee->start();
	}

	static void load_button_cb(Fl_Widget* button, void* data) {
		Main_View* mv = (Main_View*)data;
		char* fn =fl_file_chooser("Pick a file:", NULL, NULL);
		if (fn) {
			mv->open(fn);
		}
	}

	static void jump_size_cb(Fl_Widget* widget, void* data) {
		Fl_Marquee* marquee = (Fl_Marquee*)data;
		Fl_Slider* slider = (Fl_Slider*)widget;
		marquee->ppm((uchar)slider->value());
	}

	static void speed_value_cb(Fl_Widget* widget, void* data) {
		Fl_Marquee* marquee = (Fl_Marquee*)data;
		Fl_Slider* slider = (Fl_Slider*)widget;
		marquee->delay((int)slider->value());
	}

	static void font_height_cb(Fl_Widget* widget, void* data) {
		Fl_Marquee* marquee = (Fl_Marquee*)data;
		Fl_Slider* slider = (Fl_Slider*)widget;
		// printf("Setting fontheight to %f aka %d \n", slider->value(), (uchar)slider->value());
		marquee->labelsize((uchar)slider->value());
		marquee->redraw();
	}

	static void view_index_cb(Fl_Widget* widget, void* data) {
		Fl_Marquee* marquee = (Fl_Marquee*)data;
		Fl_Slider* slider = (Fl_Slider*)widget;
		marquee->index((long unsigned int)slider->value());
	}

	public:

	static int noreadfunc(long unsigned int o, int l, char* out, void* data) {
		char* banner = "No file selected.  Use the 'Load' option to choose a file to view.";
		if (o<0) {
			printf("illegal request for offset %ld\n", o);
			o=0;
		}
		if (o<strlen(banner)) {
			strncpy(out, &banner[o], l);
			return strlen(&banner[o]);
		} else {
			return 0;
		}
	}

	static void index_change_cb(long unsigned int i, void* data) {
		Fl_Slider* slider = (Fl_Slider*)data;
		slider->value(i);
	}

	Main_View() {
		window = new Fl_Window(160, 100);
		
		marquee = new Fl_Marquee(14, 2, 140, 50, &noreadfunc, this);
		marquee->box(FL_FLAT_BOX);
		marquee->labelsize(14);
		marquee->color(FL_WHITE);

		font_height = new Fl_Slider(2, 2, 10, 50, "fontsize");
		font_height->type(FL_VERT_NICE_SLIDER);
		font_height->step(1,1);
		font_height->range(48,8);
		font_height->value(14);
		font_height->callback(&font_height_cb, (void *)marquee);

		rewind_button = new Fl_Button(2, 56, 20, 20, "@<");
		rewind_button->labelsize(12);
		rewind_button->labeltype(FL_SYMBOL_LABEL);
		rewind_button->callback(&rewind_button_cb, (void *)marquee);

		stop_button = new Fl_Button(24, 56, 20, 20, "@square");
		stop_button->labelsize(12);
		stop_button->labeltype(FL_SYMBOL_LABEL);
		stop_button->callback(&stop_button_cb, (void *)marquee);

		play_button = new Fl_Button(46, 56, 20, 20, "@>");
		play_button->labelsize(12);
		play_button->labeltype(FL_SYMBOL_LABEL);
		play_button->callback(&play_button_cb, (void *)marquee);

		load_button = new Fl_Button(68, 56, 35, 20, "Load");
		load_button->labelsize(12);
		load_button->labelfont(FL_BOLD);
		load_button->labeltype(FL_SYMBOL_LABEL);
		load_button->callback(&load_button_cb, (void *)this);

		jump_size = new Fl_Slider(2, 78, 45, 10, "ppm");
		jump_size->type(FL_HOR_NICE_SLIDER);
		jump_size->step(1,1);
		jump_size->minimum(1);
		jump_size->maximum(10);
		jump_size->value(3);
		jump_size->callback(&jump_size_cb, (void *)marquee);

		speed_value = new Fl_Value_Slider(49, 78, 109, 10, "speed");
		speed_value->labelsize(8);
		speed_value->type(FL_HOR_NICE_SLIDER);
		speed_value->step(1,1);
		speed_value->range(100,1);
		speed_value->value(50);
		speed_value->callback(&speed_value_cb, (void *)marquee);

		view_index = new Fl_Slider(2, 90, 156, 8, "view index");
		view_index->labelsize(12);
		view_index->type(FL_HOR_NICE_SLIDER);
		view_index->step(1,1);
		view_index->range(0,63);
		view_index->value(50);
		view_index->callback(&view_index_cb, (void *)marquee);
		marquee->indexchange_cb(&index_change_cb, (void *)view_index);

		window->end();
	}

	void show() {
		window->show();
	}

	static int freadfunc(unsigned long o, int l, char* out, void* data) {
		Main_View *mv = (Main_View *)data;

		int r = 0;
		while (r < l) {
			lseek(mv->fd, o+r, SEEK_SET);
			int r1 = read(mv->fd, out+r, l-r);
			if (!r1) {
				return r;
			} else if (r1 >0) {
				r += r1;
			} else {
				fl_alert("Error reading %s: %s", mv->filename, strerror(errno));
				return 0;
			}
		}
		return r;
	};

	void open(char *fname) {
		filename = fname;
		fd = ::open(filename, O_RDONLY);
		if (fd == -1) {
			fl_alert("Error opening %s: %s", filename, strerror(errno));
			load_button_cb(NULL, this);
		}
		marquee->loadfunc(freadfunc, this);
		marquee->reset();
		struct stat buf;
		int rv = fstat(fd, &buf);
		if (rv == -1) {
			fl_alert("Error stat'ing %s: %s", filename, strerror(errno));
			load_button_cb(NULL, this);
		}
		view_index->maximum(buf.st_size);
	};

};




int main(int argc, char **argv) {
	Main_View* mainwin = new Main_View();
	Fl::visual(FL_DOUBLE|FL_INDEX); // magic from fltk man pages
//	printf("set visual flags for doublebuffering");
	mainwin->show();
	Fl::run();
}



