/*
 *  Copyright (C) 2001 Philip Langdale
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 *  2003-10-13 Takuro Ashie <ashie@homa.ne.jp>
 *
 *      Picked from Epiphany and adapt to Kazehakase.
 */

/* Things to be aware of:
 *
 * This filepicker, like the mozilla one, does not make an attempt
 * to verify the validity of the initial directory you pass it.
 * It does check that the user doesn't give it a garbage path
 * during use, but it is the caller's responsibility to give a
 * sensible initial path.
 *
 * At the current moment, we instantiate the filepicker directly
 * in our contenthandler where there is path verification code
 * and else where through our C wrapper, which also does verification.
 * If, at a future date, you need to instantiate filepicker without
 * using the C wrapper, please verify the initial path. See
 * ContentHandler for a way to do this.
 */

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

#include <glib/gconvert.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include <gtk/gtkmenu.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkcheckbutton.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtkfilesel.h>
#include <gtk/gtkhbbox.h>
#include <gtk/gtkoptionmenu.h>
#include <gtk/gtkmessagedialog.h>
#include <glib/gi18n.h>

#include "nsIFilePicker.h"

#include "nsCRT.h"
#include "nsCOMPtr.h"
#include "nsISupportsArray.h"
#include "nsIServiceManager.h"

#define MOZILLA_STRICT_API
#include <nsEmbedString.h>
#undef MOZILLA_STRICT_API
#include "nsIPrefService.h"
#include "nsIURI.h"
#include "nsIFileURL.h"
#include "nsIChannel.h"
#include "nsIFileChannel.h"
#include "nsNetCID.h"
#include "nsILocalFile.h"
#include "nsIPromptService.h"

#include "kz-mozfilepicker.h"
#include "kz-mozutils.h"

/* Implementation file */
NS_IMPL_ISUPPORTS1(KzFilePicker, nsIFilePicker)

nsEmbedCString KzFilePicker::mPrevDirectory;

KzFilePicker::KzFilePicker()
{
	NS_INIT_ISUPPORTS();

	/* member initializers and constructor code */
	mFile = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID);
	mDisplayDirectory = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID);
	if (!mPrevDirectory.Length())
	{
		mPrevDirectory = nsEmbedCString(g_get_home_dir());
	}
	mDisplayDirectory->InitWithNativePath(mPrevDirectory);
}

KzFilePicker::~KzFilePicker()
{
	/* destructor code */
}

////////////////////////////////////////////////////////////////////////////////
// begin nsIFilePicker impl
////////////////////////////////////////////////////////////////////////////////

/* void init (in nsIDOMWindowInternal parent, in wstring title, in short mode); */
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
NS_IMETHODIMP KzFilePicker::Init(nsIDOMWindow *aParent,
				 const nsAString &aTitle,
				 PRInt16 aMode)
#else
NS_IMETHODIMP KzFilePicker::Init(nsIDOMWindowInternal *aParent, 
				 const PRUnichar *aTitle, PRInt16 aMode)
#endif
{
	mParent = do_QueryInterface(aParent);
	mParentWidget = GetGtkWindowForDOMWindow(mParent);
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
	NS_UTF16ToCString(aTitle, NS_CSTRING_ENCODING_UTF8, mTitle);
#else
	NS_UTF16ToCString(nsEmbedString(aTitle), NS_CSTRING_ENCODING_UTF8, mTitle);
#endif
	mMode = aMode;
	
	return NS_OK;
}

/* void appendFilters (in long filterMask); */
NS_IMETHODIMP KzFilePicker::AppendFilters(PRInt32 aFilterMask)
{
	//This function cannot be implemented due to the crippled
	//nature of GtkFileSelection, but NS_ERROR_NOT_IMPLEMENTED
	//is interpreted as a terminal error by some callers.
	return NS_OK;
}

/* void appendFilter (in wstring title, in wstring filter); */
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
NS_IMETHODIMP KzFilePicker::AppendFilter(const nsAString &aTitle,
					 const nsAString &aFilter)
#else
NS_IMETHODIMP KzFilePicker::AppendFilter(const PRUnichar *aTitle,
					 const PRUnichar *aFilter)
#endif
{
	//GtkFileSelection is crippled, so we can't provide a short-list
	//of filters to choose from. We provide minimal functionality
	//by using the most recent AppendFilter call as the active filter.
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
	NS_UTF16ToCString(aFilter, NS_CSTRING_ENCODING_UTF8, mFilter);
#else
	NS_UTF16ToCString(nsEmbedString(aFilter), NS_CSTRING_ENCODING_UTF8, mFilter);
#endif
	return NS_OK;
}

/* attribute long filterIndex; */
NS_IMETHODIMP KzFilePicker::GetFilterIndex(PRInt32 *aFilterIndex)
{
	return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP KzFilePicker::SetFilterIndex(PRInt32 aFilterIndex)
{
	return NS_OK;
}

/* attribute wstring defaultString; */
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
NS_IMETHODIMP KzFilePicker::GetDefaultString(nsAString &aDefaultString)
#else
NS_IMETHODIMP KzFilePicker::GetDefaultString(PRUnichar * *aDefaultString)
#endif
{
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
	aDefaultString = mDefaultString;
#else
	*aDefaultString = NS_StringCloneData(mDefaultString);
#endif
	return NS_OK;
}
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
NS_IMETHODIMP KzFilePicker::SetDefaultString(const nsAString &aDefaultString)
#else
NS_IMETHODIMP KzFilePicker::SetDefaultString(const PRUnichar *aDefaultString)
#endif
{
	mDefaultString.Assign(aDefaultString);

	return NS_OK;
}

/* attribute wstring defaultExtension; */
// Again, due to the crippled file selector, we can't really
// do anything here.
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
NS_IMETHODIMP KzFilePicker::GetDefaultExtension(nsAString &aDefaultExtension)
#else
NS_IMETHODIMP KzFilePicker::GetDefaultExtension(PRUnichar * *aDefaultExtension)
#endif
{
    return NS_ERROR_NOT_IMPLEMENTED;
}
#ifdef MOZ_NSIFILEPICKER_NSASTRING_
NS_IMETHODIMP KzFilePicker::SetDefaultExtension(const nsAString &aDefaultExtension)
#else
NS_IMETHODIMP KzFilePicker::SetDefaultExtension(const PRUnichar *aDefaultExtension)
#endif
{
    return NS_OK;
}

/* attribute nsILocalFile displayDirectory; */
NS_IMETHODIMP KzFilePicker::GetDisplayDirectory(nsILocalFile * *aDisplayDirectory)
{
	NS_IF_ADDREF(*aDisplayDirectory = mDisplayDirectory);
	return NS_OK;
}
NS_IMETHODIMP KzFilePicker::SetDisplayDirectory(nsILocalFile * aDisplayDirectory)
{
	mDisplayDirectory = aDisplayDirectory;
	return NS_OK;
}

/* readonly attribute nsILocalFile file; */
NS_IMETHODIMP KzFilePicker::GetFile(nsILocalFile * *aFile)
{
	NS_IF_ADDREF(*aFile = mFile);
	return NS_OK;
}

/* readonly attribute nsIFileURL fileURL; */
NS_IMETHODIMP KzFilePicker::GetFileURL(nsIFileURL * *aFileURL)
{
	nsCOMPtr<nsIFileURL> fileURL = 
		do_CreateInstance(NS_STANDARDURL_CONTRACTID);
	fileURL->SetFile(mFile);
	NS_IF_ADDREF(*aFileURL = fileURL);
	return NS_OK;
}

/* readonly attribute nsISimpleEnumerator files; */
NS_IMETHODIMP KzFilePicker::GetFiles(nsISimpleEnumerator * *aFiles)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

/* short show (); */
NS_IMETHODIMP KzFilePicker::Show(PRInt16 *_retval)
{
	GtkFileChooserAction action;
	GtkFileFilter *filter = NULL;
	const gchar *stock;

	switch (mMode)
	{
	 case nsIFilePicker::modeSave:
		action = GTK_FILE_CHOOSER_ACTION_SAVE;
		stock = GTK_STOCK_SAVE;
		break;
	 case nsIFilePicker::modeGetFolder:
		action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
		stock = GTK_STOCK_OPEN;
		break;
	 case nsIFilePicker::modeOpenMultiple:
		action = GTK_FILE_CHOOSER_ACTION_OPEN;
		stock = GTK_STOCK_OPEN;
		break;
	 case nsIFilePicker::modeOpen:
	 default:
		action = GTK_FILE_CHOOSER_ACTION_OPEN;
		stock = GTK_STOCK_OPEN;
		break;
	}
			
	mFileDialog = gtk_file_chooser_dialog_new(mTitle.get(),
						  NULL,
						  action,
						  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
						  stock, GTK_RESPONSE_ACCEPT,
						  NULL);

	if(mMode == nsIFilePicker::modeSave)
	{
		nsEmbedCString cFileName;
		NS_UTF16ToCString(mDefaultString,
				  NS_CSTRING_ENCODING_UTF8, cFileName);

		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(mFileDialog),
				 		  cFileName.get());
		gtk_dialog_set_default_response(GTK_DIALOG(mFileDialog), GTK_RESPONSE_ACCEPT);
	}

	nsEmbedCString dir;
	
	mDisplayDirectory->GetNativePath (dir);

	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(mFileDialog),
					    dir.get());

	if (mFilter.Length())
	{
		filter = gtk_file_filter_new();
		gtk_file_filter_add_pattern(filter, mFilter.get());
	}

	if (mParentWidget)
		gtk_window_set_transient_for(GTK_WINDOW(mFileDialog),
					     GTK_WINDOW(mParentWidget));

	if (mMode == nsIFilePicker::modeOpenMultiple)
	{
		gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(mFileDialog)
					 	     , TRUE);
	}

	gtk_window_set_modal(GTK_WINDOW(mFileDialog), TRUE);

	gint retVal = gtk_dialog_run(GTK_DIALOG(mFileDialog));
	
	HandleFilePickerResult();

	if (retVal != GTK_RESPONSE_ACCEPT)
	{
		*_retval = returnCancel;
	}
	else
	{	
		ValidateFilePickerResult(_retval);
	}

	if (filter)
		g_object_unref(filter);

	gtk_widget_destroy(mFileDialog);

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// begin local public methods impl
////////////////////////////////////////////////////////////////////////////////

NS_METHOD KzFilePicker::InitWithGtkWidget (GtkWidget *aParentWidget, 
					   const char *aTitle, PRInt16 aMode)
{
	mParentWidget = aParentWidget;

	mTitle = nsEmbedCString(aTitle);

	mMode = aMode;

	mFile = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID);

	return NS_OK;
}

NS_METHOD KzFilePicker::SanityCheck (PRBool *retIsSane)
{
	*retIsSane = PR_TRUE;
	
	nsresult rv;
	PRBool dirExists, fileExists = PR_TRUE;

	if (mDisplayDirectory)
	{
		rv = mDisplayDirectory->Exists (&dirExists);
		g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
	}
	else
	{
		dirExists = PR_FALSE;
	}

	if (mMode != nsIFilePicker::modeGetFolder)
	{
		rv = mFile->Exists (&fileExists);
		g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
	}

	if (mMode == nsIFilePicker::modeSave && !fileExists)
	{
		return NS_OK;
	}
	
	if (!dirExists || !fileExists)
	{
		GtkWidget *errorDialog = gtk_message_dialog_new (
				NULL,
				GTK_DIALOG_MODAL,
				GTK_MESSAGE_ERROR,
				GTK_BUTTONS_OK,
				_("The specified path does not exist."));

		if (mParentWidget)
			gtk_window_set_transient_for(GTK_WINDOW(errorDialog),
						     GTK_WINDOW(mFileDialog));

		gtk_window_set_modal (GTK_WINDOW(errorDialog), TRUE);
		gtk_dialog_run (GTK_DIALOG(errorDialog));
		gtk_widget_destroy (errorDialog);
		*retIsSane = PR_FALSE;
		return NS_OK;
	}

	PRBool correctType;
	char *errorText;
	if (mMode == nsIFilePicker::modeGetFolder)
	{
		rv = mDisplayDirectory->IsDirectory (&correctType);
		g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
		errorText = g_strdup (_("A file was selected when a "
					"folder was expected."));
	}
	else
	{
		rv = mFile->IsFile (&correctType);
		g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
		errorText = g_strdup (_("A folder was selected when a "
				        "file was expected."));
	}
	
	if(!correctType)
	{
		GtkWidget *errorDialog = gtk_message_dialog_new (
				NULL,
				GTK_DIALOG_MODAL,
				GTK_MESSAGE_ERROR,
				GTK_BUTTONS_OK,
				errorText);

		if (mParentWidget)
			gtk_window_set_transient_for(GTK_WINDOW(errorDialog),
						     GTK_WINDOW(mFileDialog));

		gtk_window_set_modal (GTK_WINDOW(errorDialog), TRUE);
		gtk_dialog_run (GTK_DIALOG(errorDialog));
		gtk_widget_destroy (errorDialog);
		*retIsSane = PR_FALSE;
	}
	g_free (errorText);

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// begin local private methods impl
////////////////////////////////////////////////////////////////////////////////

NS_METHOD KzFilePicker::HandleFilePickerResult()
{
	char *fileName = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(mFileDialog));

	if (!fileName || strlen(fileName) == 0) return NS_ERROR_FAILURE;

	nsEmbedCString cFileName(fileName);
	mFile->InitWithNativePath(cFileName);

	if (mMode == nsIFilePicker::modeGetFolder)
	{
		mDisplayDirectory->InitWithNativePath(cFileName);
		
		const PRUnichar tmp[] = { '\0' };
		mDefaultString = nsEmbedString(tmp);
	}
	else
	{
		nsCOMPtr<nsIFile> directory;
		mFile->GetParent(getter_AddRefs(directory));
		mDisplayDirectory = do_QueryInterface(directory);
		mDisplayDirectory->GetNativePath(mPrevDirectory);
		mFile->GetLeafName(mDefaultString);
	}

	g_free (fileName);
	
	return NS_OK;
}

NS_METHOD KzFilePicker::ValidateFilePickerResult(PRInt16 *retval)
{
	nsresult rv;

	*retval = returnCancel;

	PRBool passesSanityCheck;
	rv = SanityCheck(&passesSanityCheck);
	if (NS_SUCCEEDED(rv) && !passesSanityCheck) return NS_ERROR_FAILURE;

	if (mMode == nsIFilePicker::modeSave)
	{
#if 0
		const char *fileName = gtk_file_selection_get_filename(GTK_FILE_SELECTION(mFileSelector));
		if (!ephy_gui_confirm_overwrite_file (mFileSelector,
					              fileName))
		{
			return NS_OK;
		}
#endif
	}

	*retval = returnOK;

	return NS_OK;
}
