/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Binary II support.
 */
#ifndef __BNY_ARCHIVE__
#define __BNY_ARCHIVE__

#include "GenericArchive.h"


class BnyArchive;

/*
 * One file in a BNY archive.
 */
class BnyEntry : public GenericEntry {
public:
	BnyEntry(BnyArchive* pArchive) :
		fpArchive(pArchive), fIsSqueezed(false), fOffset(-1)
		{}
	virtual ~BnyEntry(void) {}

	// retrieve thread data
	virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength,
		CString* pErrMsg) const;
	virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv,
		ConvertHighASCII convHA, CString* pErrMsg) const;
	virtual long GetSelectionSerial(void) const { return -1; }	// doesn't matter

	virtual bool GetFeatureFlag(Feature feature) const {
		if (feature == kFeaturePascalTypes || feature == kFeatureDOSTypes ||
			feature == kFeatureHasSimpleAccess)
			return false;
		else
			return true;
	}

	NuError TestEntry(CWnd* pMsgWnd);

	bool GetSqueezed(void) const { return fIsSqueezed; }
	void SetSqueezed(bool val) { fIsSqueezed = val; }
	long GetOffset(void) const { return fOffset; }
	void SetOffset(long offset) { fOffset = offset; }

	enum {
		kBNYBlockSize		= 128,
	};

private:
	NuError CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA,
		CString* pMsg) const;
	//NuError BNYUnSqueeze(ExpandBuffer* outExp) const;

	BnyArchive*	fpArchive;		// holds FILE* for archive
	bool		fIsSqueezed;
	long		fOffset;
};


/*
 * BNY archive definition.
 */
class BnyArchive : public GenericArchive {
public:
	BnyArchive(void) : fIsReadOnly(false), fFp(nil)
		{}
	virtual ~BnyArchive(void) { (void) Close(); }

	// One-time initialization; returns an error string.
	static CString AppInit(void);

	virtual OpenResult Open(const char* filename, bool readOnly,
		CString* pErrMsg);
	virtual CString New(const char* filename, const void* options);
	virtual CString Flush(void) { return ""; }
	virtual CString Reload(void);
	virtual bool IsReadOnly(void) const { return fIsReadOnly; };
	virtual bool IsModified(void) const { return false; }
	virtual void GetDescription(CString* pStr) const { *pStr = "Binary II"; }
	virtual bool BulkAdd(ActionProgressDialog* pActionProgress,
		const AddFilesDialog* pAddOpts)
		{ ASSERT(false); return false; }
	virtual bool AddDisk(ActionProgressDialog* pActionProgress,
		const AddFilesDialog* pAddOpts)
		{ ASSERT(false); return false; }
	virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry,
		const char* newName)
		{ ASSERT(false); return false; }
	virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet);
	virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
		{ ASSERT(false); return false; }
	virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
		{ ASSERT(false); return false; }
	virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS,
		const char* newName)
		{ ASSERT(false); return false; }
	virtual CString TestVolumeName(const DiskFS* pDiskFS,
		const char* newName) const
		{ ASSERT(false); return "!"; }
	virtual CString TestPathName(const GenericEntry* pGenericEntry,
		const CString& basePath, const CString& newName, char newFssep) const
		{ ASSERT(false); return "!"; }
	virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
		const RecompressOptionsDialog* pRecompOpts)
		{ ASSERT(false); return false; }
	virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
		ActionProgressDialog* pActionProgress, const XferFileOptions* pXferOpts)
		{ ASSERT(false); return kXferFailed; }
	virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry,
		CString* pStr)
		{ ASSERT(false); return false; }
	virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry,
		const CString& str)
		{ ASSERT(false); return false; }
	virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry)
		{ ASSERT(false); return false; }
	virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry,
		const FileProps* pProps)
		{ ASSERT(false); return false; }
	virtual void PreferencesChanged(void) {}
	virtual long GetCapability(Capability cap);

	friend class BnyEntry;

private:
	virtual CString Close(void) {
		if (fFp != nil) {
			fclose(fFp);
			fFp = nil;
		}
		return "";
	}
	virtual void XferPrepare(const XferFileOptions* pXferOpts)
		{ ASSERT(false); }
	virtual CString XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
		long dataLen, unsigned char** pRsrcBuf, long rsrcLen)
		{ ASSERT(false); return "!"; }
	virtual void XferAbort(CWnd* pMsgWnd)
		{ ASSERT(false); }
	virtual void XferFinish(CWnd* pMsgWnd)
		{ ASSERT(false); }

	virtual ArchiveKind GetArchiveKind(void) { return kArchiveBNY; }
	virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
		FileDetails* pDetails)
		{ ASSERT(false); return kNuErrGeneric; }

	enum {
		kBNYBlockSize		= BnyEntry::kBNYBlockSize,
		kBNYMaxFileName		= 64,
		kBNYMaxNativeName	= 48,
		kBNYFlagCompressed	= (1<<7),
		kBNYFlagEncrypted	= (1<<6),
		kBNYFlagSparse		= (1),
	};

	typedef unsigned char uchar;
	typedef unsigned short ushort;
	typedef unsigned long ulong;

	/*
	 * An entry in a Binary II archive.  Each archive is essentially a stream
	 * of files; only the "filesToFollow" value gives any indication that
	 * something else follows this entry.
	 *
	 * We read this from the archive and then unpack it into GenericEntry
	 * fields in a BnyEntry.
	 */
	struct BnyFileEntry;			// VC++6 needs these to access private enums
	friend struct BnyFileEntry;		//   in this class
	typedef struct BnyFileEntry {
		ushort			access;
		ushort			fileType;
		ulong			auxType;
		uchar			storageType;
		ulong			fileSize;			/* in 512-byte blocks */
		ushort			prodosModDate;
		ushort			prodosModTime;
		NuDateTime		modWhen;			/* computed from previous two fields */
		ushort			prodosCreateDate;
		ushort			prodosCreateTime;
		NuDateTime		createWhen; 		/* computed from previous two fields */
		ulong			eof;
		ulong			realEOF;			/* eof is bogus for directories */
		char			fileName[kBNYMaxFileName+1];
		char			nativeName[kBNYMaxNativeName+1];
		ulong			diskSpace;			/* in 512-byte blocks */
		uchar			osType; 			/* not exactly same as NuFileSysID */
		ushort			nativeFileType;
		uchar			phantomFlag;
		uchar			dataFlags;			/* advisory flags */
		uchar			version;
		uchar			filesToFollow;		/* #of files after this one */

		uchar			blockBuf[kBNYBlockSize];
	} BnyFileEntry;

	int LoadContents(void);
	NuError LoadContentsCallback(BnyFileEntry* pEntry);

	bool IsSqueezed(uchar one, uchar two);
	bool IsDir(BnyFileEntry* pEntry);
	NuError BNYRead(void* buf, size_t nbyte);
	NuError BNYSeek(long offset);
	void BNYConvertDateTime(unsigned short prodosDate,
		unsigned short prodosTime, NuDateTime* pWhen);
	NuError BNYDecodeHeader(BnyFileEntry* pEntry);
	NuError BNYIterate(void);

	FILE*		fFp;
	bool		fIsReadOnly;
};

#endif /*__BNY_ARCHIVE__*/