/*
 * Copyright (C) 2007, 2008  Miriam Ruiz <little_miry@yahoo.es>
 *
 * VersatileBrowser uses ColResizeBrowser, written by Greg Ercolano
 * ColResizeBrowser (C) Greg Ercolano <erco@seriss.com>
 * URL: http://seriss.com/people/erco/fltk/
 * GNU GPL License (version 2 or, at your option, any later version)
 *
 * PackageBrowser::item_draw and PackageBrowser::item_width
 * use code from FLTK 1.1 Fl_Browser.cxx
 * Browser widget for the Fast Light Tool Kit (FLTK).
 * Copyright 1998-2005 by Bill Spitzak and others.
 * URL: http://www.fltk.org/
 * GNU LGPL License (version 2 or, at your option, any later version)
 *
 * 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
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "Environment.h"
#include "GamesOptions.h"
#include "Engine.h"
#include "URLFetcher.h"

#include "pkgbrowser.h"
#include "ui.h"
#include "common.h"
#include "aux.h"

#include "filter.h"

#include <ept/apt/packagerecord.h>

#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>

#ifdef USE_GETTEXT
#include <libintl.h>
#include <locale.h>
#else
// Work-around until goplay is actually gettextised
#define gettext(a) (a)
#endif

#include <FL/Fl_PNG_Image.H>
#include <FL/Fl_JPEG_Image.H>
#include <FL/Fl_BMP_Image.H>

using namespace std;
using namespace ept;
using namespace ept::debtags;
using namespace ept::apt;

static const char legalchars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@_?+-,.~/%&=:*#";

PackageBrowser::PackageBrowser(int x, int y, int w, int h, const char *l)
	: VersatileBrowser(x, y, w, h, l), num_bar_images(2)
{
	showcolsep(1);
	//colsepcolor(FL_RED);
	column_char('\t'); // tabs as column delimiters
	bar_images[0] = new Fl_PNG_Image(FILE_STARS_ON);
	bar_images[1] = new Fl_PNG_Image(FILE_STARS_OFF);
	bar_images[2] = new Fl_PNG_Image(FILE_PEOPLE_ON);
	bar_images[3] = new Fl_PNG_Image(FILE_PEOPLE_OFF);
	for (unsigned int i = 0; i < num_bar_images*2; i++)
	{
		if (bar_images[i] && !bar_images[i]->count())
			printf(_("Invalid bar image %u\n"), i);
	}
}

PackageBrowser::~PackageBrowser()
{
	for (unsigned int i = 0; i < num_bar_images*2; i++)
	{
		if (bar_images[i]) delete bar_images[i];
	}
}

static void text2html(const std::string &in_text, std::string &out_html)
{
	const char *text = in_text.c_str();
	const char *src = text;
	//out_html.erase();
	while (*src)
	{
		if (!strncasecmp("http:", src, 5) || !strncasecmp("mailto:", src, 7) ||
			!strncasecmp("ftp:", src, 4) || !strncasecmp("https:", src, 6))
		{
			std::string uri, uri_text;
			for (; *src && (strchr(legalchars, *src) || (*src & 128)); src++)
			{
				uri += *src;
				uri_text += *src;
				if (*src == '/')
					uri_text += "<wbr>";
			}
			if (strchr(".?!", src[-1])) {  // Probably the end of a sentence
				--src;
				uri.resize(uri.size() - 1);
				uri_text.resize(uri_text.size() - 1);
			}
			out_html += "<a href=\"";
			out_html += uri;
			out_html += "\">";
			out_html += uri_text;
			out_html += "</a>";
		}
		else if (!strncasecmp(" .\n", src, 3))
		{
			out_html += "<br />&nbsp;<br />";
			src += 2;
		}
		else if (*src <= ' ')
		{
			out_html += " ";
			src++;
		}
		else
		{
			switch(*src)
			{
				case '<': out_html += "&lt;"; break;
				case '>': out_html += "&gt;"; break;
				default:
				out_html += *src;
			}
			src++;
		}
	}
}

static bool fileexist(const std::string path)
{
	struct stat fileinfo;
	if (stat(path.c_str(), &fileinfo) == 0)
	{
		return true;
	}
	return false;
}

Fl_Image *PackageBrowser::find_screenshot(const std::string packagename)
{
	Fl_Image *img = NULL;
	string filename;
	if (fileexist(filename = std::string(THUMBNAILSDIR) + "/" +
		      packagename + ".png"))
	{ // Portable Network Graphics (PNG) image files
	  // We were able to get the file attributes so the file obviously exists.
		img = new Fl_PNG_Image(filename.c_str());
		//printf("  PNG Screenshot : \"%s\"\n", filename);
	}
	else if (fileexist(filename = std::string(THUMBNAILSDIR) + "/" +
			   packagename + ".jpg"))
	{ // Joint Photographic Experts Group (JPEG) File Interchange Format (JFIF) images.
		img = new Fl_JPEG_Image(filename.c_str());
      //printf("  JPEG Screenshot : \"%s\"\n", filename);
	}
	else if (fileexist(filename = std::string(THUMBNAILSDIR) + "/" +
			   packagename + ".jpeg"))
	{
		img = new Fl_JPEG_Image(filename.c_str());
		//printf("  JPEG Screenshot : \"%s\"\n", filename);
	}
	else if (fileexist(filename = std::string(THUMBNAILSDIR) + "/" +
			   packagename + ".bmp"))
	{ // Windows Bitmap (BMP) image files
		img = new Fl_BMP_Image(filename.c_str());
		//printf("  BMP Screenshot : \"%s\"\n", filename);
	}
	else
	{ // TODO: Portable Anymap (PNM, PBM, PGM, PPM) image files
	  // Currently only fetching using HTTP is implemented
		URLFetcher fetcher;
		std::string url("http://screenshots.debian.net/screenshot/");
		//std::string url("http://screenshots.debian.net/thumbnail/");
		url += packagename;
		//cout << "fetching " << url << endl;
		try {
			std::string imgname = fetcher.get(url, packagename);
			//cout << "Loading image " << imgname << endl;
			Fl_Image *tmpimg = new Fl_PNG_Image(imgname.c_str());
			unlink(imgname.c_str());
			img = tmpimg->copy(320, 240);
			delete(tmpimg);
		} catch (std::exception &e) {
			cout << "URLFetcher exception: " << e.what() << endl;
			img = NULL;
		}
	}
	if (img && !img->count())
	{
		printf("  Wrong Screenshot.\n");
		delete img;
		img = NULL;
	}
	if (!img)
	{
		img = new Fl_PNG_Image(FILE_NO_SCREENSHOT);
	}
	return img;
}

void PackageBrowser::item_select(void *p, int s)
{
	VersatileBrowser::item_select(p, s);

	if (s)
	{
		int n = VersatileBrowser::lineno(p);
		void *data = VersatileBrowser::data(n);
		pkgname = (const char*) data;
		//printf("  #%d : \"%s\"\n", n, (const char *)data);
		//fflush(stdout);

		if (data)
		{
			const char *packagename = (const char *)data;
			Fl_Image *img = find_screenshot(packagename);
			Fl_Group *highest_parent=parent();
			while (highest_parent->parent()) highest_parent = highest_parent->parent();
			GamesUI *ui = highest_parent ? (GamesUI*)(highest_parent->user_data()) : NULL;
			if (ui && img) ui->Screenshot(img);

			PackageRecord rec(ui->engine->apt().rawRecord(packagename));

			std::string pkg_desc;
			if (ui)
			{
				pkg_desc = std::string ("<center><h1><a href=\"pkg:") + rec.package() + "\">" + rec.package() +
					"</a></h1></center><br />";
				pkg_desc += std::string("<center>") + rec.shortDescription() + "</center><hr /><p>";
				text2html(rec.longDescription(), pkg_desc);
				ui->InfoView->value(pkg_desc.c_str());
			}

			static int widths[] = { 150, 0 }; // widths for each column
			ui->DebTagsBrowser->clear();
			ui->DebTagsBrowser->showcolsep(1);
			ui->DebTagsBrowser->column_widths(widths);
			ui->DebTagsBrowser->add(_("@B12@C7@b@.FACET\t@B12@C7@b@.TAG"));

			set<std::string> tags = ui->engine->debtags().getTagsOfItem(packagename);
			char *tag_txt = new char[512];
			for (set<std::string>::const_iterator i = tags.begin(); i != tags.end(); ++i)
			{
				const voc::FacetData* fd = ui->engine->voc().facetData(voc::getfacet(*i));
				const voc::TagData* td = ui->engine->voc().tagData(*i);
				if (!fd || !td) continue;

				// Available Colors: FL_BLACK, FL_BLUE, FL_CYAN, FL_DARK_BLUE,
				// FL_DARK_CYAN, FL_DARK_GREEN FL_DARK_MAGENTA, FL_DARK_RED,
				// FL_DARK_YELLOW, FL_GREEN, FL_MAGENTA, FL_RED, FL_WHITE, FL_YELLOW

				Fl_Color bk(FL_WHITE);
				Fl_Color fr(FL_BLACK);
				switch (pkgfilter.TagValue(*i))
				{
					case PackageFilter::Green:
						fr = FL_BLACK; bk = FL_GREEN; break;
					case PackageFilter::Yellow:
						fr = FL_BLACK; bk = FL_YELLOW; break;
					case PackageFilter::Red:
						fr = FL_WHITE; bk = FL_RED; break;
					case PackageFilter::Black:
						fr = FL_WHITE; bk = FL_BLACK; break;
					default:
						fr = FL_BLACK; bk = FL_WHITE; break;
				}
				snprintf(tag_txt, 512, "@B%d@C%d@.%s\t@B%d@C%d@.%s",
					bk, fr,
					gettext(fd->shortDescription().c_str()),
					bk, fr,
					gettext(td->shortDescription().c_str())
				);
				ui->DebTagsBrowser->add(tag_txt);
			}
			delete [] tag_txt;
		}

		fflush(stdout);
	}
}

int PackageBrowser::item_width(void *p) const
{
	int n = VersatileBrowser::lineno(p);
	char *text = strdup(VersatileBrowser::text(n));
	char *str = text;
	const int* i = column_widths();
	int ww = 0;

	while (*i)
	{ // add up all tab-seperated fields
		char* e;
		e = strchr(str, column_char());
		if (!e) break; // last one occupied by text
		str = e+1;
		ww += *i++;
	}

	// OK, we gotta parse the string and find the string width...
	int tsize = textsize();
	Fl_Font font = textfont();
	int done = 0;

	while (*str == format_char() && str[1] && str[1] != format_char())
	{
		str ++;
		switch (*str++)
		{
			case 'l': case 'L': tsize = 24; break;
			case 'm': case 'M': tsize = 18; break;
			case 's': tsize = 11; break;
			case 'b': font = (Fl_Font)(font|FL_BOLD); break;
			case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
			case 'f': case 't': font = FL_COURIER; break;
			case 'B':
			case 'C': {long int __attribute__((unused)) unused = strtol(str, &str, 10);} break;// skip a color number
			case 'F': font = (Fl_Font)strtol(str, &str, 10); break;
			case 'S': tsize = strtol(str, &str, 10); break;
			case '.': done = 1; break;
			case '@': str--; done = 1;
		}
		if (done) break;
	}

	int last_w = 0;
	if (*str=='%' && '0'<=str[1] && str[1]<='9')
	{
		char *after;
		/* int percent = */ long int __attribute__((unused)) unused = strtol(str+1, &after, 10);
		int steps = 0;
		long int bar = 0;
		if (after && *after=='/')
			steps = strtol(after+1, &after, 10);
		if (steps < 0 ) steps = 100;
		if (after && *after=='/')
			bar = strtol(after+1, &after, 10);
		if (bar - num_bar_images >= 0 ) bar = num_bar_images - 1;
		if (bar < 0 ) bar = 0;

		if (bar_images[bar*2] && bar_images[bar*2]->w() > last_w) last_w = bar_images[bar*2]->w();
		if (bar_images[bar*2+1] && bar_images[bar*2+1]->w() > last_w) last_w = bar_images[bar*2+1]->w();
	}
	else
	{
		if (*str == format_char() && str[1]) str ++;
		fl_font(font, tsize);
		last_w = int(fl_width(str));
	}

	free (text);
	return ww + last_w + 6;
}

void PackageBrowser::item_draw(void *p, int x, int y, int w, int h) const
{
	//printf("PackageBrowser::item_draw(p=0x%lX, x=%d, y=%d, w=%d, h=%d)\n", (unsigned long)p, x, y, w, h);
	int n = VersatileBrowser::lineno(p);
	//void *data = VersatileBrowser::data(n);
	//printf("   data=\"%s\"\n", (char *)data);
	char *text = strdup(VersatileBrowser::text(n));
	char *str = text;
	const int* i = column_widths();

	while (w > 6)
	{	// do each tab-seperated field
		int w1 = w;	// width for this field
		char* e = 0; // pointer to end of field or null if none
		if (*i)
		{ // find end of field and temporarily replace with 0
			e = strchr(str, column_char());
			if (e) {*e = 0; w1 = *i++;}
		}
		int tsize = textsize();
		Fl_Font font = textfont();
		Fl_Color lcol = textcolor();
		Fl_Align talign = FL_ALIGN_LEFT;
		// check for all the @-lines recognized by XForms:
		while (*str == format_char() && *++str && *str != format_char())
		{
			switch (*str++)
			{
				case 'l': case 'L': tsize = 24; break;
				case 'm': case 'M': tsize = 18; break;
				case 's': tsize = 11; break;
				case 'b': font = (Fl_Font)(font|FL_BOLD); break;
				case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
				case 'f': case 't': font = FL_COURIER; break;
				case 'c': talign = FL_ALIGN_CENTER; break;
				case 'r': talign = FL_ALIGN_RIGHT; break;
				case 'B': 
					if (!item_selected(p))
					{
						fl_color((Fl_Color)strtol(str, &str, 10));
						fl_rectf(x, y, w1, h);
					}
					else
						long int __attribute__((unused)) unused = strtol(str, &str, 10);
					break;
				case 'C': lcol = (Fl_Color)strtol(str, &str, 10); break;
				case 'F': font = (Fl_Font)strtol(str, &str, 10); break;
				case 'N': lcol = FL_INACTIVE_COLOR; break;
				case 'S': tsize = strtol(str, &str, 10); break;
				case '-':
					fl_color(FL_DARK3);
					fl_line(x + 3, y + h/2, x + w1 - 3, y + h/2);
					fl_color(FL_LIGHT3);
					fl_line(x + 3, y + h/2 + 1, x + w1 - 3, y + h/2 + 1);
					break;
				case 'u':
				case '_':
					fl_color(lcol);
					fl_line(x + 3, y + h - 1, x + w1 - 3, y + h - 1);
					break;
				case '.': goto BREAK;
				case '@': str--; goto BREAK;
			}
		}

	BREAK:
		fl_font(font, tsize);
		if (item_selected(p))
			lcol = fl_contrast(lcol, selection_color());
		if (!active_r()) lcol = fl_inactive(lcol);
		fl_color(lcol);

		if (*str=='%' && '0'<=str[1] && str[1]<='9')
		{
			char *after;
			int percent = strtol(str+1, &after, 10);
			int steps = 0;
			long int bar = 0;
			if (after && *after=='/')
				steps = strtol(after+1, &after, 10);
			if (steps < 0 ) steps = 100;
			if (after && *after=='/')
				bar = strtol(after+1, &after, 10);
			if (bar - num_bar_images >= 0) bar = num_bar_images - 1;
			if (bar < 0 ) bar = 0;
			int n = (percent + (50/steps))* steps / 100; // convert to num of steps, round instead of just floor
			if (n < 0) n = 0;
			if (n > steps) n = steps;
			Fl_Image *bar_off = bar_images[bar*2+1];
			Fl_Image *bar_on = bar_images[bar*2];
			int active = bar_on->w()*n/steps;

			if (bar_off)
				bar_off->draw(x + active + 3, y, min(bar_off->w() - active, w1 - active - 6), h, active, 0);
			if (bar_on)
				bar_on->draw(x + 3, y, min(active, w1 - 6), h, 0, 0);

			char tmp[32];
			snprintf(tmp, sizeof(tmp), "%u%%", percent);
			//fl_draw(tmp, x + 3, y, min(bar_off->w(), w1) - 6, h, Fl_Align(FL_ALIGN_CENTER|FL_ALIGN_CLIP), 0, 0);
			//fl_draw(tmp, x + bar_on->w() + 3, y, w1 - 6, h, Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_CLIP), 0, 0);
		}
		else
		{
			fl_draw(str, x + 3, y, w1 - 6, h, e ? Fl_Align(talign|FL_ALIGN_CLIP) : talign, 0, 0);
		}

		if (!e) break; // no more fields...
		*e = column_char(); // put the seperator back
		x += w1;
		w -= w1;
		str = e+1;
	}

	free (text);
}

int PackageBrowser::handle(int e)
{
//	printf("PackageBrowser::handle(int e = 0x%)\n", e);
//	fflush(stdout);

	int ret = VersatileBrowser::handle(e);

	switch(e)
	{
		case FL_PUSH:
			break;
		case FL_RELEASE:
			break;
		case FL_ENTER:
			break;
		case FL_LEAVE:
			break;
		case FL_DRAG:
			break;
		case FL_FOCUS:
			break;
		case FL_UNFOCUS:
			break;
		case FL_KEYDOWN:
			break;
		case FL_KEYUP:
			break;
		case FL_CLOSE:
			break;
		case FL_MOVE:
			break;
		case FL_SHORTCUT:
			break;
		case FL_DEACTIVATE:
			break;
		case FL_ACTIVATE:
			break;
		case FL_HIDE:
			break;
		case FL_SHOW:
			break;
		case FL_PASTE:
			break;
		case  FL_SELECTIONCLEAR:
			break;
		case  FL_MOUSEWHEEL:
			break;
		case  FL_NO_EVENT:
			break;
	}
	return ret;
}

const std::string PackageBrowser::pkg_selected(void) const
{
	return pkgname;
}
