/*
 *  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"

#include "nsString.h"
#include "nsXPIDLString.h"
#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 "nsReadableUtils.h"

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

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

nsCString 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.IsEmpty()) {
		mPrevDirectory = nsDependentCString(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); */
#if MOZILLA_SNAPSHOT > 15
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);
	mTitle = NS_ConvertUCS2toUTF8(aTitle);
	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); */
#if MOZILLA_SNAPSHOT > 15
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.
	mFilter = NS_ConvertUCS2toUTF8(aFilter);
	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; */
#if MOZILLA_SNAPSHOT > 15
NS_IMETHODIMP KzFilePicker::GetDefaultString(nsAString &aDefaultString)
#else
NS_IMETHODIMP KzFilePicker::GetDefaultString(PRUnichar * *aDefaultString)
#endif
{
	gsize bytesWritten;
	gchar *utf8DefaultString = g_filename_to_utf8(mDefaultString.get(), -1,
						      NULL,
						      &bytesWritten, NULL);
#if MOZILLA_SNAPSHOT > 15
	aDefaultString.Assign(NS_ConvertUTF8toUCS2(utf8DefaultString));
#else
	*aDefaultString = ToNewUnicode(NS_ConvertUTF8toUCS2(utf8DefaultString));
#endif
	g_free(utf8DefaultString);
	return NS_OK;
}
#if MOZILLA_SNAPSHOT > 15
NS_IMETHODIMP KzFilePicker::SetDefaultString(const nsAString &aDefaultString)
#else
NS_IMETHODIMP KzFilePicker::SetDefaultString(const PRUnichar *aDefaultString)
#endif
{
#if MOZILLA_SNAPSHOT > 15
	if (aDefaultString.Length())
#else
	if (aDefaultString)
#endif
	{
		gsize bytesWritten;
		gchar *localeDefaultString =
			g_filename_from_utf8(NS_ConvertUCS2toUTF8(aDefaultString).get(),
					     -1, NULL,
					     &bytesWritten, NULL);
		mDefaultString = localeDefaultString;					     
		g_free(localeDefaultString);
	}
	else
		mDefaultString = "";
	return NS_OK;
}

/* attribute wstring defaultExtension; */
// Again, due to the crippled file selector, we can't really
// do anything here.
#if MOZILLA_SNAPSHOT > 15
NS_IMETHODIMP KzFilePicker::GetDefaultExtension(nsAString &aDefaultExtension)
#else
NS_IMETHODIMP KzFilePicker::GetDefaultExtension(PRUnichar * *aDefaultExtension)
#endif
{
    return NS_ERROR_NOT_IMPLEMENTED;
}
#if MOZILLA_SNAPSHOT > 15
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)
{
	mFileSelector = gtk_file_selection_new(mTitle.get());

	nsCAutoString cFileName;
	if(mMode == nsIFilePicker::modeGetFolder)
		cFileName.Assign("");
	else
		cFileName = mDefaultString;

	nsCAutoString cDirName;
	mDisplayDirectory->GetNativePath(cDirName);

	nsCAutoString cFullPath;
	cFullPath.Assign(cDirName + NS_LITERAL_CSTRING("/") + cFileName);
	gtk_file_selection_set_filename(GTK_FILE_SELECTION(mFileSelector),
				 	cFullPath.get());

	if (!mFilter.IsEmpty())
	{
		gtk_file_selection_complete(GTK_FILE_SELECTION(mFileSelector),
					    mFilter.get());
	}

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

	if (mMode == nsIFilePicker::modeGetFolder)
	{
		gtk_widget_set_sensitive(GTK_FILE_SELECTION(mFileSelector)
					 ->file_list, FALSE);
	}

	gtk_window_set_modal(GTK_WINDOW(mFileSelector), TRUE);

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

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

	gtk_widget_destroy(mFileSelector);

	return NS_OK;
}

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

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

	mTitle = nsDependentCString(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(mFileSelector));

		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(mFileSelector));

		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()
{
	const char *fileName = gtk_file_selection_get_filename(GTK_FILE_SELECTION(mFileSelector));

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

	const nsACString &cFileName = nsDependentCString(fileName);
	mFile->InitWithNativePath(cFileName);

	if (mMode == nsIFilePicker::modeGetFolder)
	{
		mDisplayDirectory->InitWithNativePath(cFileName);
		mDefaultString = "";
	}
	else
	{
		nsCOMPtr<nsIFile> directory;
		mFile->GetParent(getter_AddRefs(directory));
		mDisplayDirectory = do_QueryInterface(directory);
		mDisplayDirectory->GetNativePath(mPrevDirectory);
		mFile->GetNativeLeafName(mDefaultString);
	}

	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;
}
