/*
 *  Copyright © 2001 Philip Langdale
 *  Copyright © 2003, 2004 Christian Persch
 *
 *  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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *  $Id$
 */

#include "mozilla-config.h"
#include "config.h"

#include <glib/gconvert.h>
#include <glib/gi18n.h>
#include <gtk/gtkfilefilter.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkstock.h>

#include <nsStringAPI.h>

#include <nsCOMPtr.h>
#include <nsIDOMWindow.h>
#include <nsIFileURL.h>
#include <nsILocalFile.h>
#include <nsIPromptService.h>
#include <nsIServiceManager.h>
#include <nsIURI.h>
#include <nsNetCID.h>
#include <nsComponentManagerUtils.h>
#include <nsServiceManagerUtils.h>
#include <nsXPCOMCID.h>

#include "ephy-debug.h"
#include "ephy-gui.h"
#include "ephy-prefs.h"

#include "AutoModalDialog.h"
#include "EphyUtils.h"

#include "FilePicker.h"

NS_IMPL_ISUPPORTS1(GFilePicker, nsIFilePicker)

GFilePicker::GFilePicker()
: mDialog(nsnull)
, mMode(nsIFilePicker::modeOpen)
{
	LOG ("GFilePicker ctor (%p)", this);
}

GFilePicker::~GFilePicker()
{
	LOG ("GFilePicker dtor (%p)", this);

	if (mDialog)
	{
		g_object_remove_weak_pointer (G_OBJECT (mDialog), (gpointer *) &mDialog);
		gtk_widget_destroy (GTK_WIDGET (mDialog));
	}
}

/* void init (in nsIDOMWindow parent, in AString title, in short mode); */
NS_IMETHODIMP GFilePicker::Init(nsIDOMWindow *parent, const nsAString& title, PRInt16 mode)
{
	LOG ("GFilePicker::Init");

	mParent = do_QueryInterface (parent);

	GtkWidget *gtkparent = EphyUtils::FindGtkParent (parent);

	NS_ENSURE_TRUE (gtkparent, NS_ERROR_FAILURE);


	nsCString cTitle;

	NS_UTF16ToCString (title, NS_CSTRING_ENCODING_UTF8, cTitle);

	mMode = mode;

	GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
	switch (mode)
	{
		case nsIFilePicker::modeGetFolder:
			action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
			break;
		case nsIFilePicker::modeOpenMultiple:
		case nsIFilePicker::modeOpen:
			action = GTK_FILE_CHOOSER_ACTION_OPEN;
			break;
		case nsIFilePicker::modeSave:
			action = GTK_FILE_CHOOSER_ACTION_SAVE;
			break;
		default:
			g_assert_not_reached ();
			break;
	}

	mDialog = ephy_file_chooser_new (cTitle.get(), gtkparent, action,
					 CONF_STATE_UPLOAD_DIR,
					 EPHY_FILE_FILTER_NONE);

	if (parent)
	{
		gtk_window_group_add_window (ephy_gui_ensure_window_group (GTK_WINDOW (gtkparent)),
					     GTK_WINDOW (mDialog));
	}

	if (mode == nsIFilePicker::modeOpenMultiple)
	{
		gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (mDialog), TRUE);
	}
	if (mMode == nsIFilePicker::modeSave)
	{
		gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (mDialog), TRUE);
	}

	g_object_add_weak_pointer (G_OBJECT (mDialog), (gpointer *) &mDialog);

	return NS_OK;
}

/* void appendFilters (in long filterMask); */
NS_IMETHODIMP GFilePicker::AppendFilters(PRInt32 filterMask)
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	LOG ("GFilePicker::AppendFilters mask=%d", filterMask);

	// http://lxr.mozilla.org/seamonkey/source/xpfe/components/filepicker/res/locale/en-US/filepicker.properties
	// http://lxr.mozilla.org/seamonkey/source/xpfe/components/filepicker/src/nsFilePicker.js line 131 ff

	if (filterMask & nsIFilePicker::filterAll)
	{
		ephy_file_chooser_add_pattern_filter (mDialog, _("All files"),
						      "*", (char*) NULL);
	}
	if (filterMask & nsIFilePicker::filterHTML)
	{
		ephy_file_chooser_add_mime_filter (mDialog, _("Web pages"),
						   "text/html",
						   "application/xhtml+xml",
						   "text/xml",
						   (char *) NULL);
	}
	if (filterMask & nsIFilePicker::filterText)
	{
		ephy_file_chooser_add_pattern_filter (mDialog, _("Text files"),
						      "*.txt", "*.text", NULL);
	}
	if (filterMask & nsIFilePicker::filterImages)
	{
		ephy_file_chooser_add_mime_filter (mDialog, _("Images"),
						   "image/png",
						   "image/jpeg",
						   "image/gif",
						   (char *) NULL);
	}
	if (filterMask & nsIFilePicker::filterXML)
	{
		ephy_file_chooser_add_pattern_filter (mDialog, _("XML files"),
						      "*.xml", (char *) NULL);
	}
	if (filterMask & nsIFilePicker::filterXUL)
	{
		ephy_file_chooser_add_pattern_filter (mDialog, _("XUL files"),
						      "*.xul", (char *) NULL);
	}

	return NS_OK;
}

/* void appendFilter (in AString title, in AString filter); */

NS_IMETHODIMP GFilePicker::AppendFilter(const nsAString& title, const nsAString& filter)
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	LOG ("GFilePicker::AppendFilter");

	if (!filter.Length()) return NS_ERROR_FAILURE;

	nsCString pattern;
	NS_UTF16ToCString (filter, NS_CSTRING_ENCODING_UTF8, pattern);

	char **patterns;
	patterns = g_strsplit (pattern.get(), ";", -1);
	if (!patterns) return NS_ERROR_FAILURE;

	GtkFileFilter *filth;
	filth = gtk_file_filter_new ();

	for (int i = 0; patterns[i] != NULL; i++)
	{
		gtk_file_filter_add_pattern (filth, g_strstrip (patterns[i]));
	}

	nsCString cTitle;
	NS_UTF16ToCString (title, NS_CSTRING_ENCODING_UTF8, cTitle);

	gtk_file_filter_set_name (filth, cTitle.get());

	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (mDialog), filth);

	g_strfreev (patterns);

	return NS_OK;
}

/* attribute AString defaultString; */
NS_IMETHODIMP GFilePicker::GetDefaultString(nsAString& aDefaultString)
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	LOG ("GFilePicker::GetDefaultString");

	aDefaultString = mDefaultString;

	return NS_OK;
}

NS_IMETHODIMP GFilePicker::SetDefaultString(const nsAString& aDefaultString)
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	mDefaultString.Assign (aDefaultString);

	if (mMode == nsIFilePicker::modeSave)
	{
		nsCString defaultString;
		NS_UTF16ToCString (mDefaultString, NS_CSTRING_ENCODING_UTF8,
				   defaultString);

		LOG ("GFilePicker::SetDefaultString %s", defaultString.get());

		if (!defaultString.Length()) return NS_ERROR_FAILURE;

		/* set_current_name takes UTF-8, not a filename */
		gtk_file_chooser_set_current_name
			(GTK_FILE_CHOOSER (mDialog), defaultString.get());
	}

	return NS_OK;
}

/* attribute AString defaultExtension; */
NS_IMETHODIMP GFilePicker::GetDefaultExtension(nsAString& aDefaultExtension)
{
	LOG ("GFilePicker::GetDefaultExtension");

	return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP GFilePicker::SetDefaultExtension(const nsAString& aDefaultExtension)
{
	LOG ("GFilePicker::SetDefaultExtension");

	return NS_ERROR_NOT_IMPLEMENTED;
}

/* attribute long filterIndex; */
NS_IMETHODIMP GFilePicker::GetFilterIndex(PRInt32 *aFilterIndex)
{
	LOG ("GFilePicker::GetFilterIndex");

	return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP GFilePicker::SetFilterIndex(PRInt32 aFilterIndex)
{
	LOG ("GFilePicker::SetFilterIndex index=%d", aFilterIndex);

	return NS_ERROR_NOT_IMPLEMENTED;
}

/* attribute nsILocalFile displayDirectory; */
NS_IMETHODIMP GFilePicker::GetDisplayDirectory(nsILocalFile **aDisplayDirectory)
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	LOG ("GFilePicker::GetDisplayDirectory");

	char *dir = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (mDialog));

	if (dir != NULL)
	{
		nsCOMPtr<nsILocalFile> file = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID);
		file->InitWithNativePath (nsCString (dir));
		NS_IF_ADDREF (*aDisplayDirectory = file);
	
		g_free (dir);
	}

	return NS_OK;
}

NS_IMETHODIMP GFilePicker::SetDisplayDirectory(nsILocalFile *aDisplayDirectory)
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	nsCString dir;
	aDisplayDirectory->GetNativePath (dir);

	LOG ("GFilePicker::SetDisplayDirectory to %s", dir.get());

	if (mDefaultString.Length() && mMode != nsIFilePicker::modeSave)
	{
		nsCString defaultString;
		NS_UTF16ToCString (mDefaultString, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM,
				   defaultString);

		char *filename = g_build_filename (dir.get(), defaultString.get(), (char *) NULL);
		LOG ("Setting filename to %s", filename);
		gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (mDialog), filename);
		g_free (filename);
	}
	else
	{
		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (mDialog),
						     dir.get());
	}

	return NS_OK;
}

/* readonly attribute nsILocalFile file; */
NS_IMETHODIMP GFilePicker::GetFile(nsILocalFile **aFile)
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	char *filename;

	LOG ("GFilePicker::GetFile");

	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (mDialog));

	if (filename != NULL)
	{
		nsCOMPtr<nsILocalFile> file = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID);
		file->InitWithNativePath (nsCString (filename));
		NS_IF_ADDREF (*aFile = file);
	
		g_free (filename);
	}

	return NS_OK;
}

#ifdef HAVE_GECKO_1_9
/* readonly attribute nsIURI fileURL; */
NS_IMETHODIMP GFilePicker::GetFileURL(nsIURI **aFileURL)
#else
/* readonly attribute nsIFileURL fileURL; */
NS_IMETHODIMP GFilePicker::GetFileURL(nsIFileURL **aFileURL)
#endif
{
	NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE);

	LOG ("GFilePicker::GetFileURL");

	nsCOMPtr<nsILocalFile> file;
	GetFile (getter_AddRefs(file));
	NS_ENSURE_TRUE (file, NS_ERROR_FAILURE);

	nsCOMPtr<nsIFileURL> fileURL = do_CreateInstance (NS_STANDARDURL_CONTRACTID);
	fileURL->SetFile(file);
	NS_IF_ADDREF(*aFileURL = fileURL);

	return NS_OK;
}

/* readonly attribute nsISimpleEnumerator files; */
NS_IMETHODIMP GFilePicker::GetFiles(nsISimpleEnumerator * *aFiles)
{
	// Not sure if we need to implement it at all, it's used nowhere
	// in mozilla, but I guess a javascript might call it?

	LOG ("GFilePicker::GetFiles");

	return NS_ERROR_NOT_IMPLEMENTED;
}

/* short show (); */
NS_IMETHODIMP GFilePicker::Show(PRInt16 *_retval)
{
        AutoModalDialog modalDialog (mParent, PR_FALSE);
        if (!modalDialog.ShouldShow ()) {
          *_retval = nsIFilePicker::returnCancel;
          return NS_OK;
        }
          
	mParent = nsnull;

	LOG ("GFilePicker::Show");

	gtk_window_set_modal (GTK_WINDOW (mDialog), TRUE);
	gtk_window_set_destroy_with_parent (GTK_WINDOW (mDialog), FALSE);

	/* If there's just the "ALL" filter, it's no use showing the filters! */
	GSList *filters = gtk_file_chooser_list_filters (GTK_FILE_CHOOSER (mDialog));
	if (g_slist_length (filters) == 1)
	{
		GtkFileFilter *filter = GTK_FILE_FILTER (filters->data);
		const char *name = gtk_file_filter_get_name (filter);

		if (!name || strcmp (name, _("All files")) == 0)
		{
			gtk_file_chooser_remove_filter (GTK_FILE_CHOOSER (mDialog),
							filter);
		}
	}
	g_slist_free (filters);

	gtk_widget_show (GTK_WIDGET (mDialog));

	int response;
	char *filename = NULL;

	do
	{
		response = modalDialog.Run (GTK_DIALOG (mDialog));

		g_free (filename);
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (mDialog));

		LOG ("GFilePicker::Show response=%d, filename=%s", response, filename);
	}
	while (response == GTK_RESPONSE_ACCEPT &&
	       mMode == nsIFilePicker::modeSave &&
	       !ephy_gui_check_location_writable (GTK_WIDGET (mDialog), filename));

	gtk_widget_hide (GTK_WIDGET (mDialog));

	if (response == GTK_RESPONSE_ACCEPT && filename != NULL)
	{
		*_retval = nsIFilePicker::returnOK;
	}
	else
	{
		*_retval = nsIFilePicker::returnCancel;
	}

	g_free (filename);

	return NS_OK;
}
