//	FileSystemTypes.h


#ifndef _H_FileSystemTypes
#define _H_FileSystemTypes

#include "IC_Errors.h"
#include "Utils.h"

#define	NewObject(object, type, err)		\
	((object = new type) != NULL)			\
		? TRUE								\
		: !ReportError(err = IC_Err_OUT_OF_MEMORY)

#include "GenDiskLocSpec.h"
#include "DosDiskLocSpec.h"
#include "ProDiskLocSpec.h"	//	ByteMunger.h
#include "PasDiskLocSpec.h"
#include "CpmDiskLocSpec.h"

typedef enum {
	FSObject_NONE, 
	
	FSObject_ROOT_FOLDER,		//	root above all 
	FSObject_ENTRY, 
	FSObject_FILE, 
	FSObject_FOLDER, 
	FSObject_DISK_ROOT_FOLDER, 	//	has no topic, use DISK instead
	FSObject_DISK, 
	
	FSObject_NUMTYPES
} FSObjectType;

#define	FSObject_IS_FOLDER(_TYPE)	\
	((_TYPE) == FSObject_FOLDER || (_TYPE) == FSObject_DISK_ROOT_FOLDER || (_TYPE) == FSObject_DISK)
	
/******************************************************************
*/
//	WARNING: first 6 entries of 
//	FSType and IC_FileDescIndexType
//	** MUST MATCH **
typedef enum {
	FSType_UNK,

	FSType_GEN,
	FSType_DOS, 
	FSType_PRO,
	FSType_PAS, 
	FSType_CPM, 
	FSType_C2P, 

	FSType_NUMTYPES
};
typedef Byte	FSType;

#define		FSType_HYB	((FSType)(FSType_NUMTYPES + 1))

/******************************************************************
*/
class CEntry;
class CEntryPro;
class CEntryDos;
class CEntryPas;
class CEntryCpm;
class CEntryUrl;

typedef union {
	CEntry		*gen;
	CEntryPro	*pro;
	CEntryDos	*dos;
	CEntryPas	*pas;
	CEntryCpm	*cpm;
	CEntryUrl	*url;
} CEntryUnion;

/******************************************************************
*/
class CFolder;
class CFolderPro;
class CFolderDos;
class CFolderPas;
class CFolderCpm;
class CFolderUrl;

typedef union {
	CFolder		*gen;
	CFolderPro	*pro;
	CFolderDos	*dos;
	CFolderPas	*pas;
	CFolderCpm	*cpm;
	CFolderUrl	*url;
} CFolderUnion;
/******************************************************************
*/
class CFile;
class CFilePro;
class CFileDos;
class CFilePas;
class CFileCpm;
class CFileUrl;

typedef union {
	CFile		*gen;
	CFilePro	*pro;
	CFileDos	*dos;
	CFilePas	*pas;
	CFileCpm	*cpm;
	CFileUrl	*url;
} CFileUnion;
/******************************************************************
*/
class CDisk;
class CDiskPro;
class CDiskDos;
class CDiskPas;
class CDiskCpm;
class CDiskUrl;

typedef union {
	CDisk		*gen;
	CDiskPro	*pro;
	CDiskDos	*dos;
	CDiskPas	*pas;
	CDiskCpm	*cpm;
	CDiskUrl	*url;
} CDiskUnion;


/******************************************************************
*/

typedef struct Gen_Disk Gen_Disk;
typedef struct Pro_Disk Pro_Disk;
typedef struct Pro_Block Pro_Block;	//	only for volumes kept on disk
typedef struct Dos_Disk Dos_Disk;
typedef struct Pas_Disk Pas_Disk;
typedef struct Cpm_Disk Cpm_Disk;
typedef struct Nib_Disk Nib_Disk;

typedef union {
	Gen_Disk	*gen;
	Pro_Disk	*pro;
	Pro_Block	*proBlock;			//	only for volumes on disk
	Dos_Disk	*dos;
	Pas_Disk	*pas;
	Cpm_Disk	*cpm;
} DiskImageUnion;

typedef union {
	short				value;
	Gen_SectorSpec		gen;
	Dos_SectorSpec		dos;
	Pro_BlockSpec		pro;
	Pas_BlockSpec		pas;
	Cpm_BlockSpec		cpm;
} DiskLocSpecUnion;

/********************************/
typedef enum {
	DiskType_inMemory_Raw,	 		//	kept in memory, flushed every write
	DiskType_inMemory_Nib,	 		//	kept in memory, flushed every write
	DiskType_inMemory_DiskCopy,	 	//	DiskCopy, never NIB, kept in memory, flushed every write
	DiskType_inMemory_2img,	 		//	2img, can be NIB, kept in memory, flushed every write
	DiskType_onDisk_Raw, 			//	(assert always ProDOS/pascal)
	DiskType_onDisk_DiskCopy,	 	//	(assert always ProDOS/pascal)
	DiskType_onDisk_2img,		 	//	(assert always ProDOS/pascal)
	DiskType_onDisk_Physical,	 	//	a real floppy (ProDOS/pascal)

	DiskType_URL	//	WOAH, REALLY?
} DiskType;

#define	IS_DiskType_PHYSICAL(_diskType)	\
	((_diskType) == DiskType_onDisk_Physical)

#define	IS_DiskType_IN_MEMORY(_diskType)	\
	((_diskType) >= DiskType_inMemory_Raw && (_diskType) <= DiskType_inMemory_2img)

#define	IS_DiskType_DiskCopy(_diskType)	\
	((_diskType) == DiskType_inMemory_DiskCopy || (_diskType) == DiskType_onDisk_DiskCopy)

#define	IS_DiskType_2img(_diskType)	\
	((_diskType) == DiskType_onDisk_2img || (_diskType) == DiskType_inMemory_2img)

#define	IS_DiskType_URL(_diskType)	\
	((_diskType) == DiskType_URL)

#define			IMG_Flag_LOCKED			((ulong)0x80000000)
#define			IMG_Flag_HAS_DOS_VOL	((ulong)0x00000100)
#define			IMG_Flag_VOL_NUM_MASK	((ulong)0x000000FF)
typedef ulong	IMG_FlagType;

enum {
	IMG_Format_DOS, 
	IMG_Format_PRO, 
	IMG_Format_NIB, 
	IMG_Format_C2P, 
	IMG_Format_CPM 
};
typedef ulong	IMG_FormatType;

#define	IMG_FileType	'2IMG'
#define	ADFS_Creator	'pdos'

#define GetAppCreator()	ADFS_Creator

typedef struct {
	ulong			fileType;			//	'2IMG'
	ulong			fileCreator;		//	ADFS_Creator for my app
	ushort			headerLen;			//	52 doesn't include last 12 bytes of padding
	ushort			version;			//	1
	IMG_FormatType	format;				//	(sector order) 0=DOS, 1=ProDOS, 2=Nibble
	IMG_FlagType	flags;				//	See IMG_FlagType above
	ulong			blocks;				//	ProDOS volumes only
	ulong			img_start_offset;	//	where in file img start? 64
	ulong			img_len;			//	== blocks * 512
	ulong			comment;			//	can be 0
	ulong			comment_len;		//	can be 0
	ulong			creator_data;		//	can be 0
	ulong			creator_data_len;	//	can be 0
	char			padding[16];
} Disk_2IMG_Header;		//	64 bytes

enum {
	DiskCopy_DiskFormat_400, 
	DiskCopy_DiskFormat_800, 
	DiskCopy_DiskFormat_720, 
	DiskCopy_DiskFormat_1440, 
	DiskCopy_DiskFormat_UNK
};
typedef Byte	DiskCopy_DiskFormatType;

enum {
	DiskCopy_FormatByte_400				= 0x12, 
	DiskCopy_FormatByte_GreaterThan400	= 0x22,  //	typical
	DiskCopy_FormatByte_800				= 0x24
};
typedef Byte	DiskCopy_FormatByteType;

typedef struct {
	unsigned char				diskName[64];	//	name, duh
	ulong						dataSize;		//	bytes in file
	ulong						tagSize;		//	set to zero
	ulong						dataChecksum;	//	checksum of all user data
	ulong						tagChecksum;	//	checksum of tag data (set to zero)
	DiskCopy_DiskFormatType		diskFormat;		//	set to UNK
	DiskCopy_FormatByteType		formatByte;		//	typically > 400
	ushort						privateData;	//	set to 0x0100
} Disk_DiskCopy_Header;	//	84 bytes

typedef union {
	Disk_2IMG_Header		twoimg;
	Disk_DiskCopy_Header	diskCopy;
} DiskHeaderUnion;

/*
	If diskType is NOT inMemory, then of course we have a ProDOS disk.
	The DiskImageUnion then, rather than NULL, is a pointer to a
	one BLOCK sized piece of memory, that is, the current block.
*/

typedef struct {
	short				driveNumS;
	short				driveRefNumS;
	ushort				blocksS;
	Boolean				lockedB;
} PhysVolumeRec;

typedef struct {
	FSSpec				fileSpec;
	short				refCountS;	//	hybrid disks can have more than one
	short				refNum;		//	when you open the disk image
	Boolean				skipCheckSumB;
	FSType				origOrder;	//	original sector order
	DiskHeaderUnion		header;
} ImageVolumeRec;

typedef struct Gen_Item Gen_Item;

class CGenerator;

typedef struct {
	Gen_Item		*itemP;
	Boolean			mirrorB;
} UrlVolumeRec;

typedef struct {
	DiskType			diskType;
	short				refCountS;
	Boolean				interleavePartitionsB;
	
	union {
		ImageVolumeRec	image;
		PhysVolumeRec	phys;
		UrlVolumeRec	url;
	} vol;
} DiskDeviceRec;

typedef struct {
	ushort				idS;
	ulong				offsetL;		//	sector 0 starts here
	ulong				writeStartL;	//	for disk image, where to start saving (hybrid disks)
	ulong				writeLengthL;
} PartitionRec;

/*
	1) can have more than one partition pointing to a single device
	2) partitions can be of different types (ie: dos *and* prodos)
	3) all partitions should point back to a single device record 
		with a refcount?  or a list of forward pointers?
*/
typedef struct DiskImageRec {
	DiskDeviceRec		*deviceP;
	PartitionRec		partition;
	
	DiskImageUnion		image;		//	if NOT null, then 140k image, else on disk
	FSType				curOrder;	//	current sector order
	FSType				osType;		//	file system on disk
} DiskImageRec;

#define	ImageRec_DiskType(_recP)		((_recP)->deviceP->diskType)
#define	ImageRec_VolRec(_recP)			((_recP)->deviceP->vol)
#define	ImageRec_FileSpec(_recP)		(ImageRec_VolRec(_recP).image.fileSpec)
#define	ImageRec_OrigOrder(_recP)		(ImageRec_VolRec(_recP).image.origOrder)
#define	ImageRec_UrlItem(_recP)			(ImageRec_VolRec(_recP).url)

#define	IS_ImageRec_PHYSICAL(_recP)		IS_DiskType_PHYSICAL(ImageRec_DiskType(_recP))
#define	IS_ImageRec_IN_MEMORY(_recP)	IS_DiskType_IN_MEMORY(ImageRec_DiskType(_recP))
#define	IS_ImageRec_DiskCopy(_recP)		IS_DiskType_DiskCopy(ImageRec_DiskType(_recP))
#define	IS_ImageRec_2img(_recP)			IS_DiskType_2img(ImageRec_DiskType(_recP))
#define	IS_ImageRec_URL(_recP)			IS_DiskType_URL(ImageRec_DiskType(_recP))
#define	IS_ImageRec_NIB(_recP)								\
	(ImageRec_DiskType(_recP) == DiskType_inMemory_Nib		\
	|| (ImageRec_DiskType(_recP) == DiskType_inMemory_2img	\
		&& ImageRec_VolRec(_recP).image.header.twoimg.format == IMG_Format_NIB))

typedef struct ADFS_NewDiskRec ADFS_NewDiskRec;

class CDialogCopy;

typedef struct {
	ADFS_NewDiskRec		*newDiskRecP;
	CDialogCopy			*copyDialogP;
	char				*diskNameZ;
} ADFS_NewDiskCompletionRec;

typedef enum {
	ADFS_NewDiskProg_SAVE_PREF, 
	ADFS_NewDiskProg_LOAD_140K, 
	ADFS_NewDiskProg_CRE_IMG_REC, 
	ADFS_NewDiskProg_WRITE_140K, 
	ADFS_NewDiskProg_MOUNT, 
	ADFS_NewDiskProg_SAVE_BOOT, 
	ADFS_NewDiskProg_DELETE_BOOT, 
	ADFS_NewDiskProg_ZERO, 
	ADFS_NewDiskProg_EXPAND, 
	ADFS_NewDiskProg_RESTORE_BOOT, 
	ADFS_NewDiskProg_UPDATE_TYPE, 

	ADFS_NewDiskProg_NUMTYPES
} ADFS_NewDiskProgType;

typedef	char				ADFS_ProgStr[64];
extern	ADFS_ProgStr		gProgStrs[ADFS_NewDiskProg_NUMTYPES];

void			FillInImageRecHeader(
	unsigned char			*fileNameP, 
	long					fileSize, 
	Boolean					nibblizedB, 
	DiskImageRec			*imageRecP);

Boolean		NewDisk_CB(ADFS_NewDiskRec *newDiskP);
void		ByteSwap2IMG(Disk_2IMG_Header *headerP);
void		CalcDiskCopyChecksum(DiskImageRec *imageRecP);
void		ByteSwapDiskCopy(Disk_DiskCopy_Header *headerP);
char		*strcat_FSType(FSType osType, char *buf);

#endif
