/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Application UI classes.
 */
#ifndef __MAIN__
#define __MAIN__

#include "ContentList.h"
#include "GenericArchive.h"
#include "PrefsDialog.h"
#include "ActionProgressDialog.h"
#include "ProgressCounterDialog.h"
#include "AddFilesDialog.h"
#include "ExtractOptionsDialog.h"
#include "ConvFileOptionsDialog.h"
#include "DiskConvertDialog.h"
#include "FileNameConv.h"
//#include "ProgressCancelDialog.h"

/* user-defined window messages */
#define WMU_LATE_INIT			(WM_USER+0)
#define WMU_START				(WM_USER+1)		// used by ActionProgressDialog

typedef enum {
	kFilterIndexNuFX = 1,
	kFilterIndexBinaryII = 2,
	kFilterIndexACU = 3,
	kFilterIndexDiskImage = 4,
	kFilterIndexGeneric = 5,		// *.* filter used
} FilterIndex;

struct FileCollectionEntry;		// fwd

/*
 * The main UI window.
 */
class MainWindow : public CFrameWnd
{
public:
	MainWindow(void);
	~MainWindow(void);

	// Overridden functions
	BOOL PreCreateWindow(CREATESTRUCT& cs);
	//BOOL OnCreateClient( LPCREATESTRUCT lpcs, CCreateContext* pContext );
	void GetClientRect(LPRECT lpRect) const;

	// get a pointer to the preferences
	const Preferences* GetPreferences(void) const { return &fPreferences; }
	Preferences* GetPreferencesWr(void) { return &fPreferences; }
	// apply an update from the Preferences pages
	void ApplyNow(PrefsSheet*);

	// get the text of the next file in the selection list
	int GetPrevFileText(ReformatHolder* pHolder, CString* pTitle);
	int GetNextFileText(ReformatHolder* pHolder, CString* pTitle);

	// update the progress meter
	void SetProgressBegin(void);
	int SetProgressUpdate(int percent, const char* oldName,
		const char* newName);
	void SetProgressEnd(void);

	// update the progress counter
	bool SetProgressCounter(const char* fmt, long val);

	// handle a double-click in the content view
	void HandleDoubleClick(void);

	// do some idle processing
	void DoIdle(void);

	// return the title to put at the top of a printout
	CString GetPrintTitle(void);

	// raise flag to abort the current print job
	void SetAbortPrinting(bool val) { fAbortPrinting = val; }
	bool GetAbortPrinting(void) const { return fAbortPrinting; }
	static BOOL CALLBACK PrintAbortProc(HDC hDC, int nCode);
	bool			fAbortPrinting;
	// track printer choice
	HANDLE			fhDevMode;
	HANDLE			fhDevNames;

	// set flag to abort current operation
	//void SetAbortOperation(bool val) { fAbortOperation = val; }
	//bool			fAbortOperation;

	// pause, for debugging
	void EventPause(int duration);

	ContentList* GetContentList(void) const { return fpContentList; }

	void SetActionProgressDialog(ActionProgressDialog* pActionProgress) {
		fpActionProgress = pActionProgress;
	}
	void SetProgressCounterDialog(ProgressCounterDialog* pProgressCounter) {
		fpProgressCounter = pProgressCounter;
	}
	GenericArchive* GetOpenArchive(void) const { return fpOpenArchive; }

	int GetFileParts(const GenericEntry* pEntry,
		ReformatHolder** ppHolder) const;

	// force processing of pending messages
	BOOL PeekAndPump();

	// make a happy noise after successful execution of a command
	void SuccessBeep(void);
	// make a not-so-happy noise
	void FailureBeep(void);

	// remove a file, returning a helpful message on failure
	CString RemoveFile(const char* fileName);

	// choose the place to put a file
	bool ChooseAddTarget(DiskImgLib::A2File** ppTargetSubdir,
		DiskImgLib::DiskFS** ppDiskFS);

	// try a disk image override dialog
	int TryDiskImgOverride(DiskImg* pImg, const char* fileSource,
		DiskImg::FSFormat defaultFormat, int* pDisplayFormat,
		bool allowUnknown, CString* pErrMsg);
	// copy all blocks from one disk image to another
	DIError CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk,
		bool partial, ProgressCancelDialog* pPCDialog);

	// does the currently open archive pathname match?
	bool IsOpenPathName(const char* path);
	// raise a flag to cause a full reload of the open file
	void SetReopenFlag(void) { fNeedReopen = true; }

	static void ConfigureReformatFromPreferences(ReformatHolder* pReformat);
	static ReformatHolder::SourceFormat ReformatterSourceFormat(DiskImg::FSFormat format);

	// save a buffer of data as a file in a disk image or file archive
	static bool SaveToArchive(GenericArchive::FileDetails* pDetails,
		const unsigned char* dataBuf, long dataLen,
		const unsigned char* rsrcBuf, long rsrcLen,
		CString& errMsg, CWnd* pDialog);

	static const char kOpenNuFX[];
	static const char kOpenBinaryII[];
	static const char kOpenACU[];
	static const char kOpenDiskImage[];
	static const char kOpenAll[];
	static const char kOpenEnd[];

private:
	static const char* kModeNuFX;
	static const char* kModeBinaryII;
	static const char* kModeACU;
	static const char* kModeDiskImage;

	// Command handlers
	afx_msg int OnCreate(LPCREATESTRUCT lpcs);
	afx_msg LONG OnLateInit(UINT, LONG);
	//afx_msg LONG OnCloseMainDialog(UINT, LONG);
	afx_msg void OnSize(UINT nType, int cx, int cy);
	afx_msg void OnGetMinMaxInfo(MINMAXINFO* pMMI);
	afx_msg void OnPaint(void);
	//afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
	afx_msg void OnSetFocus(CWnd* pOldWnd);
	afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
	afx_msg BOOL OnQueryEndSession(void);
	afx_msg void OnEndSession(BOOL bEnding);
	afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
	//afx_msg LONG OnHelp(UINT wParam, LONG lParam);
	afx_msg void OnFileNewArchive(void);
	afx_msg void OnFileOpen(void);
	afx_msg void OnFileOpenVolume(void);
	afx_msg void OnUpdateFileOpenVolume(CCmdUI* pCmdUI);
	afx_msg void OnFileReopen(void);
	afx_msg void OnUpdateFileReopen(CCmdUI* pCmdUI);
	afx_msg void OnFileSave(void);
	afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI);
	afx_msg void OnFileClose(void);
	afx_msg void OnUpdateFileClose(CCmdUI* pCmdUI);
	afx_msg void OnFileArchiveInfo(void);
	afx_msg void OnUpdateFileArchiveInfo(CCmdUI* pCmdUI);
	afx_msg void OnFilePrint(void);
	afx_msg void OnUpdateFilePrint(CCmdUI* pCmdUI);
	afx_msg void OnFileExit(void);
	afx_msg void OnEditCopy(void);
	afx_msg void OnUpdateEditCopy(CCmdUI* pCmdUI);
	afx_msg void OnEditPaste(void);
	afx_msg void OnUpdateEditPaste(CCmdUI* pCmdUI);
	afx_msg void OnEditPasteSpecial(void);
	afx_msg void OnUpdateEditPasteSpecial(CCmdUI* pCmdUI);
	afx_msg void OnEditFind(void);
	afx_msg void OnUpdateEditFind(CCmdUI* pCmdUI);
	afx_msg void OnEditSelectAll(void);
	afx_msg void OnUpdateEditSelectAll(CCmdUI* pCmdUI);
	afx_msg void OnEditInvertSelection(void);
	afx_msg void OnUpdateEditInvertSelection(CCmdUI* pCmdUI);
	afx_msg void OnEditPreferences(void);
	afx_msg void OnEditSort(UINT id);
	afx_msg void OnUpdateEditSort(CCmdUI* pCmdUI);
	afx_msg void OnActionsView(void);
	afx_msg void OnUpdateActionsView(CCmdUI* pCmdUI);
	afx_msg void OnActionsOpenAsDisk(void);
	afx_msg void OnUpdateActionsOpenAsDisk(CCmdUI* pCmdUI);
	afx_msg void OnActionsAddFiles(void);
	afx_msg void OnUpdateActionsAddFiles(CCmdUI* pCmdUI);
	afx_msg void OnActionsAddDisks(void);
	afx_msg void OnUpdateActionsAddDisks(CCmdUI* pCmdUI);
	afx_msg void OnActionsCreateSubdir(void);
	afx_msg void OnUpdateActionsCreateSubdir(CCmdUI* pCmdUI);
	afx_msg void OnActionsExtract(void);
	afx_msg void OnUpdateActionsExtract(CCmdUI* pCmdUI);
	afx_msg void OnActionsTest(void);
	afx_msg void OnUpdateActionsTest(CCmdUI* pCmdUI);
	afx_msg void OnActionsDelete(void);
	afx_msg void OnUpdateActionsDelete(CCmdUI* pCmdUI);
	afx_msg void OnActionsRename(void);
	afx_msg void OnUpdateActionsRename(CCmdUI* pCmdUI);
	afx_msg void OnActionsEditComment(void);
	afx_msg void OnUpdateActionsEditComment(CCmdUI* pCmdUI);
	afx_msg void OnActionsEditProps(void);
	afx_msg void OnUpdateActionsEditProps(CCmdUI* pCmdUI);
	afx_msg void OnActionsRenameVolume(void);
	afx_msg void OnUpdateActionsRenameVolume(CCmdUI* pCmdUI);
	afx_msg void OnActionsRecompress(void);
	afx_msg void OnUpdateActionsRecompress(CCmdUI* pCmdUI);
	afx_msg void OnActionsConvDisk(void);
	afx_msg void OnUpdateActionsConvDisk(CCmdUI* pCmdUI);
	afx_msg void OnActionsConvFile(void);
	afx_msg void OnUpdateActionsConvFile(CCmdUI* pCmdUI);
	afx_msg void OnActionsConvToWav(void);
	afx_msg void OnUpdateActionsConvToWav(CCmdUI* pCmdUI);
	afx_msg void OnActionsConvFromWav(void);
	afx_msg void OnUpdateActionsConvFromWav(CCmdUI* pCmdUI);
	afx_msg void OnActionsImportBAS(void);
	afx_msg void OnUpdateActionsImportBAS(CCmdUI* pCmdUI);
	afx_msg void OnToolsDiskEdit(void);
	afx_msg void OnToolsDiskConv(void);
	afx_msg void OnToolsBulkDiskConv(void);
	afx_msg void OnToolsSSTMerge(void);
	afx_msg void OnToolsVolumeCopierVolume(void);
	afx_msg void OnToolsVolumeCopierFile(void);
	afx_msg void OnToolsEOLScanner(void);
	afx_msg void OnToolsTwoImgProps(void);
	afx_msg void OnToolsDiskImageCreator(void);
	afx_msg void OnHelpContents(void);
	afx_msg void OnHelpWebSite(void);
	afx_msg void OnHelpOrdering(void);
	afx_msg void OnHelpAbout(void);
	afx_msg void OnRtClkDefault(void);

	void ProcessCommandLine(void);
	void ResizeClientArea(void);
	void DrawEmptyClientArea(CDC* pDC, const CRect& clientRect);
	int TmpExtractAndOpen(GenericEntry* pEntry, int threadKind,
		const char* modeStr);
	int TmpExtractForExternal(GenericEntry* pEntry);
	void DoOpenArchive(const char* pathName, const char* ext,
		int filterIndex, bool readOnly);
	int LoadArchive(const char* filename, const char* extension,
			int filterIndex, bool readOnly, bool createFile);
	int DoOpenVolume(CString drive, bool readOnly);
	void SwitchContentList(GenericArchive* pOpenArchive);
	void CloseArchiveWOControls(void);
	void CloseArchive(void);
	void SetCPTitle(const char* pathname, GenericArchive* pArchive);
	void SetCPTitle(void);
	GenericEntry* GetSelectedItem(ContentList* pContentList);
	void HandleView(void);

	void DeleteFileOnExit(const char* name);

	void ReopenArchive(void);

	/* some stuff from Actions.cpp */
	//int GetFileText(SelectionEntry* pSelEntry, ReformatHolder* pHolder,
	//	CString* pTitle);
	void GetFilePart(const GenericEntry* pEntry, int whichThread,
		ReformatHolder* pHolder) const;

	void DoBulkExtract(SelectionSet* pSelSet,
		const ExtractOptionsDialog* pExtOpts);
	bool ExtractEntry(GenericEntry* pEntry, int thread,
		ReformatHolder* pHolder, const ExtractOptionsDialog* pExtOpts,
		bool* pOverwriteExisting, bool* pOvwrForAll);
	int OpenOutputFile(CString* pOutputPath, const PathProposal& pathProp,
		time_t arcFileModWhen, bool* pOverwriteExisting, bool* pOvwrForAll,
		FILE** pFp);
	bool DoBulkRecompress(ActionProgressDialog* pActionProgress,
		SelectionSet* pSelSet, const RecompressOptionsDialog* pRecompOpts);
	void CalcTotalSize(LONGLONG* pUncomp, LONGLONG* pComp) const;

	/* some stuff from Clipboard.cpp */
	CString CreateFileList(SelectionSet* pSelSet);
	static CString DblDblQuote(const char* str);
	long GetClipboardContentLen(void);
	HGLOBAL CreateFileCollection(SelectionSet* pSelSet);
	CString CopyToCollection(GenericEntry* pEntry, void** pBuf, long* pBufLen);
	void DoPaste(bool pasteJunkPaths);
	CString ProcessClipboard(const void* vbuf, long bufLen,
		bool pasteJunkPaths);
	CString ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
		const char* pathName, const unsigned char* buf, long remLen);

	/* some stuff from Tools.cpp */
	int DetermineImageSettings(int convertIdx, bool addGzip,
		DiskImg::OuterFormat* pOuterFormat, DiskImg::FileFormat* pFileFormat,
		DiskImg::PhysicalFormat* pPhysicalFormat,
		DiskImg::SectorOrder* pSectorOrder);
	void BulkConvertImage(const char* pathName, const char* targetDir,
		const DiskConvertDialog& convDlg, CString* pErrMsg);
	int SSTOpenImage(int seqNum, DiskImg* pDiskImg);
	int SSTLoadData(int seqNum, DiskImg* pDiskImg, unsigned char* trackBuf,
		long* pBadCount);
	long SSTGetBufOffset(int track);
	long SSTCountBadBytes(const unsigned char* sctBuf, int count);
	void SSTProcessTrackData(unsigned char* trackBuf);
	void VolumeCopier(bool openFile);
	bool EditTwoImgProps(const char* fileName);


	void PrintListing(const ContentList* pContentList);

	// set when one of the tools modifies the file we have open
	bool			fNeedReopen;

	CToolBar		fToolBar;
	CStatusBar		fStatusBar;

	// currently-open archive, if any
	GenericArchive*	fpOpenArchive;
	// name of open archive, for display only -- if this is a temporary
	// file launched from another instance of CP, this won't be the name
	// of an actual file on disk.
	CString			fOpenArchivePathName;	// for display only

	// archive viewer, open when file is open
	// NOTE: make a super-class for a tree-structured display or other
	//  kinds of display, so we can avoid the if/then/else.  Rename
	//  ContentList to DetailList or FlatList or something.
	ContentList*	fpContentList;

	// currently selected set of goodies; used when viewing, extracting, etc.
	//SelectionSet*	fpSelSet;

	// action progress meter, if any
	ActionProgressDialog*	fpActionProgress;

	// progress counter meter, if any
	ProgressCounterDialog*	fpProgressCounter;

	// modeless standard "find" dialog
	CFindReplaceDialog*	fpFindDialog;
	CString			fFindLastStr;
	bool			fFindDown;
	bool			fFindMatchCase;
	bool			fFindMatchWholeWord;

	// our preferences
	Preferences		fPreferences;

	/*
	 * Manage a list of files that must be deleted before we exit.
	 */
	class DeleteList {
	private:
		class DeleteListNode {
		public:
			DeleteListNode(const CString& name) : fName(name),
				fPrev(nil), fNext(nil) {}
			~DeleteListNode(void) {}

			DeleteListNode*	fPrev;
			DeleteListNode*	fNext;
			CString		fName;
		};

	public:
		DeleteList(void) { fHead = nil; }
		~DeleteList(void) {
			WMSG1("Processing DeleteList (head=0x%08lx)\n", fHead);
			DeleteListNode* pNode = fHead;
			DeleteListNode* pNext;

			while (pNode != nil) {
				pNext = pNode->fNext;
				if (unlink(pNode->fName) != 0) {
					WMSG2(" WARNING: delete of '%s' failed, err=%d\n",
						pNode->fName, errno);
				} else {
					WMSG1(" Deleted '%s'\n", pNode->fName);
				}
				delete pNode;
				pNode = pNext;
			}
			WMSG0("Processing DeleteList completed\n");
		}

		void Add(const CString& name) {
			DeleteListNode* pNode = new DeleteListNode(name);
			if (fHead != nil) {
				fHead->fPrev = pNode;
				pNode->fNext = fHead;
			}
			fHead = pNode;
			WMSG1("Delete-on-exit '%s'\n", (LPCTSTR) name);
		}

		DeleteListNode*	fHead;
	};
	DeleteList		fDeleteList;

	DECLARE_MESSAGE_MAP()
};

#define GET_MAIN_WINDOW() ((MainWindow*)::AfxGetMainWnd())

#define SET_PROGRESS_BEGIN() ((MainWindow*)::AfxGetMainWnd())->SetProgressBegin()
#define SET_PROGRESS_UPDATE(perc) \
	((MainWindow*)::AfxGetMainWnd())->SetProgressUpdate(perc, nil, nil)
#define SET_PROGRESS_UPDATE2(perc, oldName, newName) \
	((MainWindow*)::AfxGetMainWnd())->SetProgressUpdate(perc, oldName, newName)
#define SET_PROGRESS_END() ((MainWindow*)::AfxGetMainWnd())->SetProgressEnd()

#define SET_PROGRESS_COUNTER(val) \
	((MainWindow*)::AfxGetMainWnd())->SetProgressCounter(nil, val)
#define SET_PROGRESS_COUNTER_2(fmt, val) \
	((MainWindow*)::AfxGetMainWnd())->SetProgressCounter(fmt, val)

#define GET_PREFERENCES() ((MainWindow*)::AfxGetMainWnd())->GetPreferences()
#define GET_PREFERENCES_WR() ((MainWindow*)::AfxGetMainWnd())->GetPreferencesWr()

#endif /*__MAIN__*/