#ifndef _FAT32_H
#define _FAT32_H

#define FAT32EOF 0x0FFFFFF8                                     // The little endian of 0x0ffffff8
#define SHA1EMPTY "da39a3ee5e6b4b0d3255bfef95601890afd80709"    // SHA1 hash of an empty file

#pragma pack(push, 1)
typedef struct BootEntry {                  // The structure for a FAT32 boot entry
    unsigned char       BS_jmpBoot[3];      // Assembly instruction to jump to boot code
    unsigned char       BS_OEMName[8];      // OEM Name in ASCII
    unsigned short      BPB_BytsPerSec;     // Bytes per sector, allowed values include 512, 1024, 2048, and 4096
    unsigned char       BPB_SecPerClus;     // Sectors per cluster (data unit), allowed values are powers of 2, but the cluster size must be 32KB or smaller
    unsigned short      BPB_RsvdSecCnt;     // Size in sectors of the reserved area
    unsigned char       BPB_NumFATs;        // Number of FATs
    unsigned short      BPB_RootEntCnt;     // Maximum number of files in the root directory for FAT12 and FAT16. This is 0 for FAT32
    unsigned short      BPB_TotSec16;       // 16-bit value of number of sectors in file system
    unsigned char       BPB_Media;          // Media type
    unsigned short      BPB_FATSz16;        // 16-bit size in sectors of each FAT for FAT12 and FAT16. For FAT32, this field is 0
    unsigned short      BPB_SecPerTrk;      // Sectors per track of storage device
    unsigned short      BPB_NumHeads;       // Number of heads in storage device
    unsigned int        BPB_HiddSec;        // Number of sectors before the start of partition
    unsigned int        BPB_TotSec32;       // 32-bit value of number of sectors in file system, either this value or the 16-bit value above must be 0
    unsigned int        BPB_FATSz32;        // 32-bit size in sectors of one FAT
    unsigned short      BPB_ExtFlags;       // A flag for FAT
    unsigned short      BPB_FSVer;          // The major and minor version number
    unsigned int        BPB_RootClus;       // Cluster where the root directory can be found
    unsigned short      BPB_FSInfo;         // Sector where FSINFO structure can be found
    unsigned short      BPB_BkBootSec;      // Sector where backup copy of boot sector is located
    unsigned char       BPB_Reserved[12];   // Reserved
    unsigned char       BS_DrvNum;          // BIOS INT13h drive number
    unsigned char       BS_Reserved1;       // Not used
    unsigned char       BS_BootSig;         // Extended boot signature to identify if the next three values are valid
    unsigned int        BS_VolID;           // Volume serial number
    unsigned char       BS_VolLab[11];      // Volume label in ASCII, user defines when creating the file system
    unsigned char       BS_FilSysType[8];   // File system type label in ASCII
} BootEntry;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct DirEntry {                   // The structure for a FAT32 directory entry
    unsigned char       DIR_Name[11];       // File name
    unsigned char       DIR_Attr;           // File attributes
    unsigned char       DIR_NTRes;          // Reserved
    unsigned char       DIR_CrtTimeTenth;   // Created time (tenths of second)
    unsigned short      DIR_CrtTime;        // Created time (hours, minutes, seconds)
    unsigned short      DIR_CrtDate;        // Created day
    unsigned short      DIR_LstAccDate;     // Accessed day
    unsigned short      DIR_FstClusHI;      // High 2 bytes of the first cluster address
    unsigned short      DIR_WrtTime;        // Written time (hours, minutes, seconds)
    unsigned short      DIR_WrtDate;        // Written day
    unsigned short      DIR_FstClusLO;      // Low 2 bytes of the first cluster address
    unsigned int        DIR_FileSize;       // File size in bytes (0 for directories)
} DirEntry;
#pragma pack(pop)


/**
 * This function prints the information of the file system based on the given boot entry.
 * The information includes the number of FATs, the number of bytes per sector,
 * the number of sectors per cluster, and the number of reserved sectors.
 *
 * Input:
 * bootEntry                                The pointer to the boot entry to extract information.
 * 
 * Return:
 *                                          None.
 *
**/
void printFsInfo(BootEntry *bootEntry);

/**
 * This function returns a readable format of the file/directory name.
 * If the directory entry specifies a file, then the format will be "<name>.<extension>" or "<name>".
 * Note that a file name cannot have the main part, though this function supports that in the format ".<extension>".
 * If the directory entry specifies a file container (i.e., another directory), then the format will be "<dirname>/".
 * **THE RETURNED POINTER IS LOCATED ON THE HEAP AND NEEDS TO BE FREED!**
 *
 * Input:
 * dirEntry                                 The pointer to the directory entry to extract information.
 * 
 * Return:
 *                                          None.
 *
**/
char *formattedDirName(DirEntry *dirEntry);

/**
 * This function prints the information of the a directory entry based on the given structure.
 * The information includes the name (and extension) of the file/directory, the size, and the starting cluster.
 *
 * Input:
 * dirEntry                                 The pointer to the directory entry to extract information.
 * 
 * Return:
 *                                          0 on success, -1 on failure, meaning that the directory entry has a long file name or is freed 
 *
**/
int printDirInfo(DirEntry *dirEntry);

/**
 * This function finds the directory entry index of the specified contiguously allocated deleted file.
 * If SHA1 hash of the file content is specified, then it also checked if the content hash matches.
 * If such a deleted file is not found, then it returns -1.
 * If multiple targets are found (only when sCode is NULL), then it returns -2.
 *
 * Input:
 * dirEntry                                 The pointer to the directory entry to extract information.
 * sCode                                    The pointer to the SHA1 hash of the contiguously allocated file contents.
 * disk                                     The pointer to the memory map of the FAT32 disk file.
 * bootEntry                                The pointer to the boot entry of the disk.
 * rootDirEntries                           The pointer to the array of the root directory entries.
 * dirEntryCount                            The number of the root directory entries
 * 
 * Return:
 *                                          The directory entry index of the specified deleted file, which is non-negative.
 *                                          -1 stands for not found, and -2 stands for multiple targets (only when sCode is NULL).
 *
**/
int delContEntryIndex(char *fileName, char *sCode, char *disk, BootEntry *bootEntry, DirEntry **rootDirEntries, int dirEntryCount);

/**
 * This function finds the clusters in order of the specified possibly non-contiguously allocated deleted file.
 * It applies a brute force search among all permutations of a certain number of clusters (determined by the file size),
 * assuming that the file appears only in the first 20 clusters in the data area (i.e., Cluster 2~21).
 * If no such combination matches the specified SHA1 hash, then it returns -1.
 * Otherwise, it updates the target cluster array, with each cluster in the [2, 22) range,
 * and returns the number of clusters that are occupied.
 * The first element in the target cluster array represents the number of clusters that the directory entry occupies.
 *
 * Input:
 * dirEntry                                 The pointer to the directory entry to extract information.
 * sCode                                    The pointer to the SHA1 hash of the possibly non-contiguously allocated file contents.
 * disk                                     The pointer to the memory map of the FAT32 disk file.
 * bootEntry                                The pointer to the boot entry of the disk.
 * rootDirEntries                           The pointer to the array of the root directory entries.
 * dirEntryCount                            The number of the root directory entries.
 * targetClusters                           The array for storing the target clusters in order and their count.
 *                                          It must be initialized with size 6 (the count and at most 5 clusters).
 * 
 * Return:
 *                                          The directory entry index of the specified deleted file, which is non-negative.
 *                                          -1 stands for not found.
 *
**/
int delNonContClusters(char *fileName, char *sCode, char *disk, BootEntry *bootEntry, DirEntry **rootDirEntries, int dirEntryCount, unsigned int *targetClusters);

/**
 * This function follows the FAT and returns the corresponding position in the data area.
 * It can also update the cluster pointer to the next cluster address specified by the first FAT in the disk,
 * if the mode is specified as 1. Mode 1 is **NOT** recommended if the pointer is directly from the FAT structure.
 * Any value >= 0x0ffffff8 can be considered as EOF.
 *
 * Input:
 * bootEntry                                The pointer to the boot entry of the disk.
 * cluster                                  The pointer to the cluster to follow the FAT.
 * disk                                     The pointer to the memory map of the FAT32 disk file.
 * mode                                     The mode whether to update the cluster pointer to the next cluster address.
 *                                          0 stands for do-not-update, and 1 stands for update.
 * 
 * Return:
 *                                          The corresponding position of the cluster pointer in the data area.
 *
**/
unsigned long followFAT(BootEntry *bootEntry, unsigned int *cluster, char *disk, int mode);

/**
 * This function updates all FATs with the specified directory entry and next cluster address to update.
 * It directly updates the memory map of the FAT32 disk file.
 *
 * Input:
 * bootEntry                                The pointer to the boot entry of the disk.
 * cluster                                  The cluster to update the FATs.
 * disk                                     The pointer to the memory map of the FAT32 disk file.
 * DIR_NextClus                             The address of the next cluster used for updating.
 * 
 * Return:
 *                                          None.
 *
**/
void updateFATs(BootEntry *bootEntry, unsigned int cluster, char *disk, unsigned int DIR_NextClus);


#endif
