/*
** 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- December 1997
 */

/*
 * PromptFiles.cpp --- class to pick set of files and folder
 */

#include "stdafx.h"

#ifdef TARGET_OS_MAC
#	include "GUSIInternal.h"
#	include "GUSIFileSpec.h"
#	include <string.h>
#	include <TextUtils.h>
#	include <LowMem.h>
#	include "stdfolder.h"
#	include "stdmfiles.h"
#	include "VolsPaths.h"
#endif /* TARGET_OS_MAC */

#if qMacPP
#	include <UConditionalDialogs.h>
#	include <UNavServicesDialogs.h>
#endif

#include "PromptFiles.h"
#include "MultiFiles.h"
#include "AppConsole.h"
#include "CvsCommands.h"

#ifdef qQT
#	include "qcvsapp.h"
#	include "CPStr.h"
#	include "TextBinary.h"
#	include <qfiledialog.h>
#	include <vector>
#	include <stdlib.h>
#endif /* qQT */

#ifdef qGTK
#	include <gtk/gtk.h>
#	include <sys/stat.h>
#	include "TextBinary.h"
#endif /* qGTK */

#ifdef WIN32
#	ifdef _DEBUG
#	define new DEBUG_NEW
#	undef THIS_FILE
	static char THIS_FILE[] = __FILE__;
#	endif
#endif /* WIN32 */

#ifdef TARGET_OS_MAC   //{ Mac - Part	
	const char *
	BrowserGetDirectory(const char *prompt)
	{
#	if qMacPP
#		if !qCarbon
		if(UConditionalDialogs::UseNavServices())
#		else
		if(1)
#		endif
		{
			UNavServicesDialogs::LFileChooser chooser;
			
			FSSpec folderReply;
			SInt32 outFolderDirID;
			if(!chooser.AskChooseFolder(folderReply, outFolderDirID))
				return 0L;
			
			static char cres[256];
			GUSIFileSpec dirSpec(folderReply);
			strcpy(cres, dirSpec.FullPath());
			return cres;
		}
#	endif
#	if !qCarbon
		FSSpec folderReply;
		Point apt = {-1, -1};
		Str255 pprompt;
		
		strcpy((char *)pprompt, prompt);
		c2pstr((char *)pprompt);
		if(!SF_PromptForFolder(apt, pprompt, &folderReply))
			return NULL;

		GUSIFileSpec spec = folderReply;
		
		static char aPath[512];
		strcpy(aPath, spec.FullPath());
		if(spec.Error() != noErr)
		{
			cvs_err("MacCvs: Unable to convert path name\n");
			return NULL;
		}
		
		return aPath;
#	else
		return 0L;
#	endif
	}

	// enumerate the multiple files
	extern "C" {
		static void
		MacEnumFiles_hook(UFSSpec *newFile, MultiFiles *mf)
		{
#if TARGET_RT_MAC_CFM
			char cname[64];
			p2cstrcpy(cname, newFile->name);
			mf->newfile(cname, newFile);
#else
			UInt8 utf8path[PATH_MAX];
			if(FSRefMakePath(newFile, utf8path, sizeof(utf8path)) == noErr)
			{
				UStr uppath, cname;
				SplitPath(utf8_to_posix(utf8path), uppath, cname);
				mf->newfile(cname, newFile);
			}
#endif
		}
	}

	// prompt for multiple files
	bool
	BrowserGetMultiFiles(const char *prompt, MultiFiles & mf)
	{
		Point apt = {-1, -1};
		Str255 pprompt;
		
		c2pstrcpy(pprompt, prompt);
		
		if(!SF_PromptForMFiles(apt, pprompt))
			return false;
		
		SF_PFMF_INFO info;
		UFSSpec dirSpec;
		
		SF_PFMF_BeginTraversal(&info);
		while(1)
		{
			if(!SF_PFMF_GetCurrentDir(&info, &dirSpec))
				break;
			
			char aPath[255];
			GUSIFileSpec spec = dirSpec;
			strcpy(aPath, spec.FullPath());
			if(spec.Error() != noErr)
			{
				cvs_err("MacCvs: Unable to convert path name\n");
				return false;
			}
			mf.newdir(aPath);
			
			if(!SF_PFMF_DoTraversal(&info, true, (SF_PFMF_HOOK)MacEnumFiles_hook, &mf))
				return false;
		}
		SF_PFMF_EndTraversal(&info);

		return true;
	}
#endif  //{ TARGET_OS_MAC 

#ifdef WIN32  //{ Win32 - Part
#	if oldWay	

	static BOOL CALLBACK APIENTRY
	MyDirOpen(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
	{
		switch (message)
		{
			case WM_INITDIALOG:
                {
                    LPOPENFILENAME pofn = (LPOPENFILENAME)lParam;
				    if(pofn && pofn->lpstrTitle)
                    {
					    SetDlgItemText (hDlg, 1090, pofn->lpstrTitle);
                    }
				    return TRUE;
                }
			case WM_COMMAND:
				{
					if (LOWORD(wParam) == IDOK)
					{
						EndDialog(hDlg, wParam);
						return TRUE;
					}
				}
		}
		return FALSE;
	}

    bool BrowserGetDirectory(const char * prompt, char* fileName, int size)
	{
		OPENFILENAME ofn;

		// set default informations
		memset(&ofn, 0, sizeof(OPENFILENAME));
		ofn.lStructSize     = sizeof(OPENFILENAME);
		ofn.hwndOwner       = *AfxGetMainWnd();
		ofn.lpstrFilter     = NULL;
		ofn.nFilterIndex    = 0;
		ofn.lpstrFile       = fileName;
		ofn.nMaxFile        = size;
		ofn.lpstrFileTitle  = NULL;
		ofn.nMaxFileTitle   = 0;
		ofn.lpstrInitialDir = (fileName[0]) ? fileName : NULL;
		ofn.lpstrTitle      = prompt;
		ofn.lpTemplateName  = "MYFOLDEROPENORD";
		ofn.hInstance       = AfxGetResourceHandle();
		ofn.lpfnHook        =  (LPOFNHOOKPROC)MyDirOpen;        
		ofn.Flags = OFN_ENABLEHOOK | OFN_ENABLETEMPLATE;

		if (GetOpenFileName( &ofn ))
		{
			GetCurrentDirectory(size, fileName);
			return true;
		}
		else
		{
			// LM
			TRACE("Retrun error of GetOpenFileName() = %0x\n", CommDlgExtendedError());
			// CDERR_DIALOGFAILURE
		}

		return false;
	}
#	else // !oldWay

    bool BrowserGetDirectory(const char* prompt, char* fileName, int size, CWnd* pParentWnd)
	{
		CCJShell m_shell;
		m_shell.m_strTitle = (prompt) ? prompt : "";

		if (fileName[0])
		{
			m_shell.m_strSelDir = fileName;
		}

		if( m_shell.BrowseForFolder(pParentWnd != 0L ? pParentWnd : AfxGetMainWnd()) )
		{
			lstrcpyn(fileName, m_shell.m_strPath, size);			
			return true;
		}
		
		return false;
	}
#	endif // !oldWay

    static char sDefaultPath[MAX_PATH];

    const char *
	BrowserGetDirectory(const char *prompt, CWnd* pParentWnd)
    {        
        BOOL selected = BrowserGetDirectory(prompt, sDefaultPath, MAX_PATH, pParentWnd);
        return (selected) ? sDefaultPath : NULL;
    }
    

	bool BrowserGetMultiFiles(const char *prompt, MultiFiles & mf, const char* lpszFilter)
	{
		bool result = false;
		CFileDialog fd(true, NULL, NULL,
			OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ALLOWMULTISELECT |
			OFN_FILEMUSTEXIST,
			lpszFilter );

		fd.m_ofn.nMaxFile = 38000 * sizeof(char);
		fd.m_ofn.lpstrFile = (char *)malloc(fd.m_ofn.nMaxFile * sizeof(char));
		fd.m_ofn.lpstrTitle = prompt;
		fd.m_ofn.lpstrInitialDir = sDefaultPath[0] != '\0' ? sDefaultPath : NULL;
		fd.m_ofn.nMaxFileTitle   = 38000;
		fd.m_ofn.lpstrFileTitle  = (char *)malloc(fd.m_ofn.nMaxFileTitle * sizeof(char));
		if(fd.m_ofn.lpstrFile == NULL || fd.m_ofn.lpstrFileTitle == NULL)
		{
			cvs_err("Impossible to allocate %d bytes !\n", 38000 * sizeof(char));
			exit(1);
		}

		// add the directory and the files
		fd.m_ofn.lpstrFile[0] = '\0';
		fd.m_ofn.lpstrFileTitle[0] = '\0';
		if(fd.DoModal() == IDOK)
		{
			GetCurrentDirectory(sizeof(sDefaultPath), sDefaultPath);
			const char *tmp = fd.m_ofn.lpstrFile;
			if(fd.m_ofn.lpstrFileTitle[0] == '\0')
			{
				mf.newdir(tmp);
				tmp += strlen(tmp) + 1;
				while(tmp[0] != '\0')
				{
					mf.newfile(tmp);
					tmp += strlen(tmp) + 1;
				}
			}
			else
			{
				mf.newdir(sDefaultPath);
				mf.newfile(tmp + fd.m_ofn.nFileOffset);
			}
			result = true;
		}

		free(fd.m_ofn.lpstrFile);
		free(fd.m_ofn.lpstrFileTitle);
		return result;
	}
#endif //{ WIN32 

#ifdef qQT  //{ QT - Part	
	const char *
	BrowserGetDirectory(const char *prompt)
	{
		QFileDialog::Mode mode = QFileDialog::Directory;
		QString start(QDir::currentDirPath());
		QString filter("*");
		QString caption(prompt);

		QFileDialog fd(start, filter, 0, 0L, TRUE );
		fd.setMode(mode);
		fd.setCaption(caption);
		fd.setSelection(start);
		if (fd.exec() == QDialog::Accepted)
		{
			static CStr result;
			result = fd.dirPath();
			return result;
		}
		return 0L;
	}

	extern "C"
	{
		static int compareCStr(const CStr *s1, const CStr *s2)
		{
			return strcmp(*s1, *s2);
		}
	}

	bool
	BrowserGetMultiFiles(const char *prompt, MultiFiles & mf)
	{
		QStringList s = QFileDialog::getOpenFileNames();
		vector <CStr> allpaths;
		QStringList::Iterator f;
		for (f = s.begin(); f != s.end(); f++)
		{
			allpaths.push_back(CStr(*f));
		}
		if(allpaths.size() == 0)
			return false;

		qsort(&allpaths[0], allpaths.size(), sizeof(CStr), (int (*)(const void *, const void *))compareCStr);

		vector<CStr>::const_iterator i;
		CStr uppath, file, oldpath;
		for(i = allpaths.begin(); i != allpaths.end(); ++i)
		{
			SplitPath(*i, uppath, file);
			if(strcmp(uppath, oldpath) != 0)
				mf.newdir(uppath);
			oldpath = uppath;
			mf.newfile(file);
		}
		return true;
	}
#endif //} qQT 

#ifdef qGTK  //{ GTK - Part	
	static CStr gtksel;
	static bool gtkModalLoop;

	static gint gtkcancel(GtkWidget *widget, gpointer data)
	{
		gtkModalLoop = false;
		return TRUE;
	}

	static void file_ok_sel (GtkWidget *w, gpointer data)
	{
		gtkModalLoop = false;
		gtksel = gtk_file_selection_get_filename(GTK_FILE_SELECTION (data));
	}

	static void file_cancel_sel (GtkWidget *w, gpointer data)
	{
		gtkModalLoop = false;
	}

	static void fileop_c_dir_sel (GtkWidget *w, gpointer data)
	{
		GtkWidget *dialog     = GTK_FILE_SELECTION (GTK_WIDGET (data))->fileop_dialog;

		gtk_grab_add (dialog);
		
		// remove grab from fileselection dialog
		gtk_grab_remove (GTK_WIDGET (data));
		fprintf(stderr, "fileop_c_dir open\n");
	}
	
	const char *
	BrowserGetDirectory(const char *prompt)
	{
		/* Create a new file selection widget */
		GtkWidget *filew = gtk_file_selection_new (prompt);
		
		gtk_signal_connect(GTK_OBJECT(filew), "delete_event",
						   GTK_SIGNAL_FUNC(gtkcancel), 0L);

		/* Connect the ok_button to file_ok_sel function */
		gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
							"clicked", (GtkSignalFunc) file_ok_sel, filew);
		gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
							"clicked", (GtkSignalFunc) file_cancel_sel, filew);
		//{ WORKAROUND  Bug #440874
		// call 'fileop_c_dir_sel' when file options selected
		gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->fileop_c_dir),
							"clicked", (GtkSignalFunc) fileop_c_dir_sel, filew);
		//}  WORKAROUND  Bug #440874

		gtkModalLoop = true;
		gtksel = "";
		gtk_widget_show(filew);
		//{ WORKAROUND Bug 440874: blocks keyboard in create dialog if uncommented
		gtk_grab_add (filew);
		//} WORKAROUND Bug 440874
		while (gtkModalLoop)
		{
			gtk_main_iteration_do(TRUE);
		}

		//{ WORKAROUND Bug 440874: blocks keyboard in create dialog if uncommented
		gtk_grab_remove (filew);
		//} BWORKAROUND ug 440874
		
		gtk_widget_destroy(filew);

		if(!gtksel.empty())
		{
			static CStr fname;

			fname = gtksel;
			if(fname.empty())
				return 0L;

			struct stat sb;
			if (stat(fname, &sb) != -1 && S_ISREG(sb.st_mode))
			{
				CStr uppath, file;
				SplitPath(fname, uppath, file);
				fname = uppath;
			}

			if(fname.endsWith(kPathDelimiter))
				fname[fname.length() - 1] = '\0';

			return fname;
		}

		return 0L;
	}

	bool
	BrowserGetMultiFiles(const char *prompt, MultiFiles & mf)
	{
		/* Create a new file selection widget */
		GtkWidget *filew = gtk_file_selection_new (prompt);
		
		gtk_signal_connect(GTK_OBJECT(filew), "delete_event",
						   GTK_SIGNAL_FUNC(gtkcancel), 0L);

		/* Connect the ok_button to file_ok_sel function */
		gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
							"clicked", (GtkSignalFunc) file_ok_sel, filew);
		gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
							"clicked", (GtkSignalFunc) file_cancel_sel, filew);
		
		gtkModalLoop = true;
		gtksel = "";
		gtk_widget_show(filew);

		//{ WORKAROUND Bug 440874: Directory creation is not necessary in this file selection box
		gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(filew));
		//{ WORKAROUND Bug 440874

		gtk_grab_add (filew);
		while (gtkModalLoop)
		{
			gtk_main_iteration_do(TRUE);
		}
		gtk_grab_remove (filew);
		gtk_widget_destroy(filew);

		if(!gtksel.empty())
		{
			CStr fname = gtksel;
			if(fname.empty())
				return false;

			CStr uppath, file;
			SplitPath(fname, uppath, file);
			mf.newdir(uppath);
			mf.newfile(file);

			return true;
		}

		return false;
	}
#endif //}  qGTK
