#ifndef  _FILEIO_DOT_H
#define  _FILEIO_DOT_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "system_config.h"
#include "system.h"

#if __XC16_VERSION__ == 1020
#error XC16 v1.20 is not compatible with this firmware, please use a later version of the XC16 compiler.
#endif

#if __XC8_VERSION == 1300
#error XC8 v1.30 is not compatible with this firmware, please use either XC8 v1.21 or a later version of the XC8 compiler.
#endif

/*******************************************************************/
/*                     Structures and defines                     */
/*******************************************************************/

// Enumeration for general purpose return values
typedef enum
{
    FILEIO_RESULT_SUCCESS = 0,                  // File operation was a success
    FILEIO_RESULT_FAILURE = -1                  // File operation failed
} FILEIO_RESULT;

// Definition to indicate an invalid file handle
#define FILEIO_INVALID_HANDLE       NULL

#define FILEIO_FILE_NAME_LENGTH_8P3                 12          // Maximum file name length for 8.3 formatted files
#define FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX        11          // Maximum file name length for 8.3 formatted files with no radix

// Enumeration for formatting modes
typedef enum
{
    FILEIO_FORMAT_ERASE = 0,            // Erases the contents of the partition
    FILEIO_FORMAT_BOOT_SECTOR           // Creates a boot sector based on user-specified information and erases any existing information
} FILEIO_FORMAT_MODE;

// Enumeration for specific return codes
typedef enum
{
    FILEIO_ERROR_NONE = 0,                      // No error
    FILEIO_ERROR_ERASE_FAIL,                    // An erase failed
    FILEIO_ERROR_NOT_PRESENT,                   // No device was present
    FILEIO_ERROR_NOT_FORMATTED,                 // The disk is of an unsupported format
    FILEIO_ERROR_BAD_PARTITION,                 // The boot record is bad
    FILEIO_ERROR_UNSUPPORTED_FS,                // The file system type is unsupported
    FILEIO_ERROR_INIT_ERROR,                    // An initialization error has occured
    FILEIO_ERROR_UNINITIALIZED,                 // An operation was performed on an uninitialized device
    FILEIO_ERROR_BAD_SECTOR_READ,               // A bad read of a sector occured
    FILEIO_ERROR_WRITE,                         // Could not write to a sector
    FILEIO_ERROR_INVALID_CLUSTER,               // Invalid cluster value > maxcls
    FILEIO_ERROR_DRIVE_NOT_FOUND,               // The specified drive could not be found
    FILEIO_ERROR_FILE_NOT_FOUND,                // Could not find the file on the device
    FILEIO_ERROR_DIR_NOT_FOUND,                 // Could not find the directory
    FILEIO_ERROR_BAD_FILE,                      // File is corrupted
    FILEIO_ERROR_DONE,                          // No more files in this directory
    FILEIO_ERROR_COULD_NOT_GET_CLUSTER,         // Could not load/allocate next cluster in file
    FILEIO_ERROR_FILENAME_TOO_LONG,             // A specified file name is too long to use
    FILEIO_ERROR_FILENAME_EXISTS,               // A specified filename already exists on the device
    FILEIO_ERROR_INVALID_FILENAME,              // Invalid file name
    FILEIO_ERROR_DELETE_DIR,                    // The user tried to delete a directory with FILEIO_Remove
    FILEIO_ERROR_DELETE_FILE,                   // The user tried to delete a file with FILEIO_DirectoryRemove
    FILEIO_ERROR_DIR_FULL,                      // All root dir entry are taken
    FILEIO_ERROR_DRIVE_FULL,                     // All clusters in partition are taken
    FILEIO_ERROR_DIR_NOT_EMPTY,                 // This directory is not empty yet, remove files before deleting
    FILEIO_ERROR_UNSUPPORTED_SIZE,              // The disk is too big to format as FAT16
    FILEIO_ERROR_WRITE_PROTECTED,               // Card is write protected
    FILEIO_ERROR_FILE_UNOPENED,                 // File not opened for the write
    FILEIO_ERROR_SEEK_ERROR,                    // File location could not be changed successfully
    FILEIO_ERROR_BAD_CACHE_READ,                // Bad cache read
    FILEIO_ERROR_FAT32_UNSUPPORTED,             // FAT 32 - card not supported
    FILEIO_ERROR_READ_ONLY,                     // The file is read-only
    FILEIO_ERROR_WRITE_ONLY,                    // The file is write-only
    FILEIO_ERROR_INVALID_ARGUMENT,              // Invalid argument
    FILEIO_ERROR_TOO_MANY_FILES_OPEN,           // Too many files are already open
    FILEIO_ERROR_TOO_MANY_DRIVES_OPEN,          // Too many drives are already open
    FILEIO_ERROR_UNSUPPORTED_SECTOR_SIZE,       // Unsupported sector size
    FILEIO_ERROR_NO_LONG_FILE_NAME,             // Long file name was not found
    FILEIO_ERROR_EOF                            // End of file reached
} FILEIO_ERROR_TYPE;

// Enumeration defining standard attributes used by FAT file systems
typedef enum
{
    FILEIO_ATTRIBUTE_READ_ONLY = 0x01,          // Read-only attribute.  A file with this attribute should not be written to.
    FILEIO_ATTRIBUTE_HIDDEN = 0x02,             // Hidden attribute.  A file with this attribute may be hidden from the user.
    FILEIO_ATTRIBUTE_SYSTEM = 0x04,             // System attribute.  A file with this attribute is used by the operating system and should not be modified.
    FILEIO_ATTRIBUTE_VOLUME = 0x08,             // Volume attribute.  If the first file in the root directory of a volume has this attribute, the entry name is the volume name.
    FILEIO_ATTRIBUTE_LONG_NAME = 0x0F,          // A file entry with this attribute mask is used to store part of the file's Long File Name.
    FILEIO_ATTRIBUTE_DIRECTORY = 0x10,          // A file entry with this attribute points to a directory.
    FILEIO_ATTRIBUTE_ARCHIVE = 0x20,            // Archive attribute.  A file with this attribute should be archived.
    FILEIO_ATTRIBUTE_MASK = 0x3F                // Mask for all attributes.
} FILEIO_ATTRIBUTES;

// Enumeration defining base locations for seeking
typedef enum
{
    FILEIO_SEEK_SET = 0,                // Change the position in the file to an offset relative to the beginning of the file.
    FILEIO_SEEK_CUR,                    // Change the position in the file to an offset relative to the current location in the file.
    FILEIO_SEEK_END                     // Change the position in the file to an offset relative to the end of the file.
} FILEIO_SEEK_BASE;

// Enumeration for file access modes
typedef enum
{
    FILEIO_OPEN_READ = 0x01,            // Open the file for reading.
    FILEIO_OPEN_WRITE = 0x02,           // Open the file for writing.
    FILEIO_OPEN_CREATE = 0x04,          // Create the file if it doesn't exist.
    FILEIO_OPEN_TRUNCATE = 0x08,        // Truncate the file to 0-length.
    FILEIO_OPEN_APPEND = 0x10           // Set the current read/write location in the file to the end of the file.
} FILEIO_OPEN_ACCESS_MODES;

// Enumeration of macros defining possible file system types supported by a device
typedef enum
{
    FILEIO_FILE_SYSTEM_TYPE_NONE = 0,       // No file system
    FILEIO_FILE_SYSTEM_TYPE_FAT12,          // The device is formatted with FAT12
    FILEIO_FILE_SYSTEM_TYPE_FAT16,          // The device is formatted with FAT16
    FILEIO_FILE_SYSTEM_TYPE_FAT32           // The device is formatted with FAT32
} FILEIO_FILE_SYSTEM_TYPE;

// Summary: Contains file information and is used to indicate which file to access.
// Description: The FILEIO_OBJECT structure is used to hold file information for an open file as it's being modified or accessed.  A pointer to
//              an open file's FILEIO_OBJECT structure will be passed to any library function that will modify that file.
typedef struct
{
    uint32_t        baseClusterDir;     // The base cluster of the file's directory
    uint32_t        currentClusterDir;  // The current cluster of the file's directory
    uint32_t        firstCluster;       // The first cluster of the file
    uint32_t        currentCluster;     // The current cluster of the file
    uint32_t        size;               // The size of the file
    uint32_t        absoluteOffset;     // The absolute offset in the file
    void *          disk;               // Pointer to a device structure
    uint16_t        currentSector;      // The current sector in the current cluster of the file
    uint16_t        currentOffset;      // The position in the current sector
    uint16_t        entry;              // The position of the file's directory entry in its directory
    uint16_t        attributes;         // The file's attributes
    uint16_t        time;               // The file's last update time
    uint16_t        date;               // The file's last update date
    uint8_t         timeMs;             // The file's last update time (ms portion)
    char            name[FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX];     // The short name of the file
    struct
    {
        unsigned    writeEnabled :1;    // Indicates a file was opened in a mode that allows writes
        unsigned    readEnabled :1;     // Indicates a file was opened in a mode that allows reads

    } flags;
} FILEIO_OBJECT;

// Possible results of the FSGetDiskProperties() function.
typedef enum
{
    FILEIO_GET_PROPERTIES_NO_ERRORS = 0,
    FILEIO_GET_PROPERTIES_CACHE_ERROR,
    FILEIO_GET_PROPERTIES_DRIVE_NOT_MOUNTED,
    FILEIO_GET_PROPERTIES_CLUSTER_FAILURE,
    FILEIO_GET_PROPERTIES_STILL_WORKING = 0xFF
} FILEIO_DRIVE_ERRORS;

// Enumeration to define media error types
typedef enum
{
    MEDIA_NO_ERROR,                     // No errors
    MEDIA_DEVICE_NOT_PRESENT,           // The requested device is not present
    MEDIA_CANNOT_INITIALIZE             // Cannot initialize media
} FILEIO_MEDIA_ERRORS;

// Media information flags.  The driver's MediaInitialize function will return a pointer to one of these structures.
typedef struct
{
    FILEIO_MEDIA_ERRORS errorCode;              // The status of the intialization FILEIO_MEDIA_ERRORS
    // Flags
    union
    {
        uint8_t    value;
        struct
        {
            uint8_t    sectorSize  : 1;         // The sector size parameter is valid.
            uint8_t    maxLUN      : 1;         // The max LUN parameter is valid.
        }   bits;
    } validityFlags;

    uint16_t    sectorSize;                     // The sector size of the target device.
    uint8_t    maxLUN;                          // The maximum Logical Unit Number of the device.
} FILEIO_MEDIA_INFORMATION;

/***************************************************************************
    Function:
        void (*FILEIO_DRIVER_IOInitialize)(void * mediaConfig);

    Summary:
        Function pointer prototype for a driver function to initialize
        I/O pins and modules for a driver.

    Description:
        Function pointer prototype for a driver function to initialize
        I/O pins and modules for a driver.

    Precondition:
        None

    Parameters:
        mediaConfig - Pointer to a driver-defined config structure

    Returns:
        None
***************************************************************************/
typedef void (*FILEIO_DRIVER_IOInitialize)(void * mediaConfig);

/***************************************************************************
    Function:
        bool (*FILEIO_DRIVER_MediaDetect)(void * mediaConfig);

    Summary:
        Function pointer prototype for a driver function to detect if
        a media device is attached/available.

    Description:
        Function pointer prototype for a driver function to detect if
        a media device is attached/available.

    Precondition:
        None

    Parameters:
        mediaConfig - Pointer to a driver-defined config structure

    Returns:
        If media attached: true
        If media not atached: false
***************************************************************************/
typedef bool (*FILEIO_DRIVER_MediaDetect)(void * mediaConfig);

/***************************************************************************
    Function:
        FILEIO_MEDIA_INFORMATION * (*FILEIO_DRIVER_MediaInitialize)(void * mediaConfig);

    Summary:
        Function pointer prototype for a driver function to perform media-
        specific initialization tasks.

    Description:
        Function pointer prototype for a driver function to perform media-
        specific initialization tasks.

    Precondition:
        FILEIO_DRIVE_IOInitialize will be called first.

    Parameters:
        mediaConfig - Pointer to a driver-defined config structure

    Returns:
        FILEIO_MEDIA_INFORMATION * - Pointer to a media initialization structure
            that has been loaded with initialization values.
***************************************************************************/
typedef FILEIO_MEDIA_INFORMATION * (*FILEIO_DRIVER_MediaInitialize)(void * mediaConfig);

/***************************************************************************
    Function:
        bool (*FILEIO_DRIVER_MediaDeinitialize)(void * mediaConfig);

    Summary:
        Function pointer prototype for a driver function to deinitialize
        a media device.

    Description:
        Function pointer prototype for a driver function to deinitialize
        a media device.

    Precondition:
        None

    Parameters:
        mediaConfig - Pointer to a driver-defined config structure

    Returns:
        If Success: true
        If Failure: false
***************************************************************************/
typedef bool (*FILEIO_DRIVER_MediaDeinitialize)(void * mediaConfig);

/***************************************************************************
    Function:
        bool (*FILEIO_DRIVER_SectorRead)(void * mediaConfig,
            uint32_t sector_addr, uint8_t * buffer);

    Summary:
        Function pointer prototype for a driver function to read a sector
        of data from the device.

    Description:
        Function pointer prototype for a driver function to read a sector
        of data from the device.

    Precondition:
        The device will be initialized.

    Parameters:
        mediaConfig - Pointer to a driver-defined config structure
        sectorAddress - The address of the sector to read.  This address
            format depends on the media.
        buffer - A buffer to store the copied data sector.

    Returns:
        If Success: true
        If Failure: false
***************************************************************************/
typedef bool (*FILEIO_DRIVER_SectorRead)(void * mediaConfig, uint32_t sector_addr, uint8_t* buffer);

/***************************************************************************
    Function:
        bool (*FILEIO_DRIVER_SectorWrite)(void * mediaConfig,
            uint32_t sectorAddress, uint8_t * buffer, bool allowWriteToZero);

    Summary:
        Function pointer prototype for a driver function to write a sector
        of data to the device.

    Description:
        Function pointer prototype for a driver function to write a sector
        of data to the device.

    Precondition:
        The device will be initialized.

    Parameters:
        mediaConfig - Pointer to a driver-defined config structure
        sectorAddress - The address of the sector to write. This address
            format depends on the media.
        buffer - A buffer containing the data to write.
		allowWriteToZero - Check to prevent writing to the master boot
		    record.  This will always be false on calls that write to files,
			which will prevent a device from accidentally overwriting its
			own MBR if its root or FAT are corrupted.  This should only
			be true if the user specifically tries to construct a new MBR.

    Returns:
        If Success: true
        If Failure: false
***************************************************************************/
typedef uint8_t (*FILEIO_DRIVER_SectorWrite)(void * mediaConfig, uint32_t sector_addr, uint8_t* buffer, bool allowWriteToZero);

/***************************************************************************
    Function:
        bool (*FILEIO_DRIVER_WriteProtectStateGet)(void * mediaConfig);

    Summary:
        Function pointer prototype for a driver function to determine if
        the device is write-protected.

    Description:
        Function pointer prototype for a driver function to determine if
        the device is write-protected.

    Precondition:
        None

    Parameters:
        mediaConfig - Pointer to a driver-defined config structure

    Returns:
        If write-protected: true
        If not write-protected: false
***************************************************************************/
typedef bool (*FILEIO_DRIVER_WriteProtectStateGet)(void * mediaConfig);

// Function pointer table that describes a drive being configured by the user
typedef struct
{
    FILEIO_DRIVER_IOInitialize funcIOInit;                          // I/O Initialization function
    FILEIO_DRIVER_MediaDetect funcMediaDetect;                      // Media Detection function
    FILEIO_DRIVER_MediaInitialize funcMediaInit;                    // Media Initialization function
    FILEIO_DRIVER_MediaDeinitialize funcMediaDeinit;                // Media Deinitialization function.
    FILEIO_DRIVER_SectorRead funcSectorRead;                        // Function to read a sector of the media.
    FILEIO_DRIVER_SectorWrite funcSectorWrite;                      // Function to write a sector of the media.
    FILEIO_DRIVER_WriteProtectStateGet funcWriteProtectGet;         // Function to determine if the media is write-protected.
} FILEIO_DRIVE_CONFIG;

// Structure that contains the disk search information, intermediate values, and results
typedef struct
{
    char    disk;           /* pointer to the disk we are searching */
    bool    new_request;    /* is this a new request or a continued request */
    FILEIO_DRIVE_ERRORS properties_status;  /* status of the last call of the function */

    struct
    {
        uint8_t disk_format;           /* disk format: FAT12, FAT16, FAT32 */
        uint16_t sector_size;           /* sector size of the drive */
        uint8_t sectors_per_cluster;   /* number of sectors per cluster */
        uint32_t total_clusters;       /* the number of total clusters on the drive */
        uint32_t free_clusters;        /* the number of free (unused) clusters on drive */
    } results;                      /* the results of the current search */

    struct
    {
        uint32_t   c;
        uint32_t   curcls;
        uint32_t   EndClusterLimit;
        uint32_t   ClusterFailValue;
    } private;      /* intermediate values used to continue searches.  This
                         member should be used only by the FSGetDiskProperties()
                         function */

} FILEIO_DRIVE_PROPERTIES;

// Structure to describe a FAT file system date
typedef union
{
    struct
    {
        uint16_t day : 5;           // Day (1-31)
        uint16_t month : 4;         // Month (1-12)
        uint16_t year : 7;          // Year (number of years since 1980)
    } bitfield;
    uint16_t value;
} FILEIO_DATE;

// Function to describe the FAT file system time.
typedef union
{
    struct
    {
        uint16_t secondsDiv2 : 5;   // (Seconds / 2) ( 1-30)
        uint16_t minutes : 6;       // Minutes ( 1-60)
        uint16_t hours : 5;         // Hours (1-24)
    } bitfield;
    uint16_t value;
} FILEIO_TIME;

// Structure to describe the time fields of a file
typedef struct
{
    FILEIO_DATE date;               // The create or write date of the file/directory.
    FILEIO_TIME time;               // The create of write time of the file/directory.
    uint8_t timeMs;                 // The millisecond portion of the time.
} FILEIO_TIMESTAMP;

// Search structure
typedef struct
{
    // Return values

    uint8_t shortFileName[13];          // The name of the file that has been found (NULL-terminated).
    uint8_t attributes;                 // The attributes of the file that has been found.
    uint32_t fileSize;                  // The size of the file that has been found (bytes).
    FILEIO_TIMESTAMP timeStamp;        // The create or write time of the file that has been found.

    // Private Parameters
    uint32_t baseDirCluster;
    uint32_t currentDirCluster;
    uint16_t currentClusterOffset;
    uint16_t currentEntryOffset;
    uint16_t pathOffset;
    char driveId;
} FILEIO_SEARCH_RECORD;

/***************************************************************************
* Prototypes                                                               *
***************************************************************************/

/***************************************************************************
  Function:
    int FILEIO_Initialize (void)

    Summary:
        Initialized the FILEIO library.

    Description:
        Initializes the structures used by the FILEIO library.

    Precondition:
        None.

    Parameters:
        void

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
***************************************************************************/
int FILEIO_Initialize (void);

/***************************************************************************
  Function:
    int FILEIO_Reinitialize (void)

    Summary:
        Reinitialized the FILEIO library.

    Description:
        Reinitialized the structures used by the FILEIO library.

    Precondition:
        FILEIO_Initialize must have been called.

    Parameters:
        void

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
***************************************************************************/
int FILEIO_Reinitialize (void);

/***************************************************************************
  Function:
    typedef void (*FILEIO_TimestampGet)(FILEIO_TIMESTAMP *)

    Summary:
        Describes the user-implemented function to provide the timestamp.

    Description:
        Files in a FAT files system use time values to track create time,
        access time, and last-modified time.  In the FILEIO library, the
        user must implement a function that the library can call to
        obtain the current time.  That function will have this format.

    Precondition:
        N/A.

    Parameters:
        FILEIO_TIMESTAMP * - Pointer to a timestamp structure that
            must be populated by the user's function.

    Returns:
        void
***************************************************************************/
typedef void (*FILEIO_TimestampGet)(FILEIO_TIMESTAMP *);

/***************************************************************************
  Function:
    void FILEIO_RegisterTimestampGet (FILEIO_TimestampGet timestampFunction)

    Summary:
        Registers a FILEIO_TimestampGet function with the library.

    Description:
        The user must call this function to specify which user-implemented
        function will be called by the library to generate timestamps.

    Precondition:
        FILEIO_Initialize must have been called.

    Parameters:
        timestampFunction - A pointer to the user-implemented function
            that will provide timestamps to the library.

    Returns:
        void
***************************************************************************/
void FILEIO_RegisterTimestampGet (FILEIO_TimestampGet timestampFunction);

/***************************************************************************
  Function:
    bool FILEIO_MediaDetect (const FILEIO_DRIVE_CONFIG * driveConfig,
        void * mediaParameters)

    Summary:
        Determines if the given media is accessible.

    Description:
        This function determines if a specified media device is available
        for further access.

    Precondition:
        FILEIO_Initialize must have been called.  The driveConfig struct
        must have been initialized with the media-specific parameters and
        the FILEIO_DRIVER_MediaDetect function.

    Parameters:
        driveConfig - Constant structure containing function pointers that
            the library will use to access the drive.
        mediaParameters - Pointer to the media-specific parameter structure

    Returns:
      * If media is available : true
      * If media is not available : false
***************************************************************************/
bool FILEIO_MediaDetect (const FILEIO_DRIVE_CONFIG * driveConfig, void * mediaParameters);

/*****************************************************************************
  Function:
      FILEIO_ERROR_TYPE FILEIO_DriveMount (char driveId,
          const FILEIO_DRIVE_CONFIG * driveConfig, void * mediaParameters);

  Summary:
    Initializes a drive and loads its configuration information.
  Description:
    This function will initialize a drive and load the required information
    from it.
  Conditions:
    FILEIO_Initialize must have been called.
  Input:
    driveId -          An alphanumeric character that will be used to
                       identify the drive.
    driveConfig -      Constant structure containing function pointers that
                       the library will use to access the drive.
    mediaParameters -  Constant structure containing media\-specific values
                       that describe which instance of the media to use for
                       this operation.
  Return:
      * FILEIO_ERROR_NONE - Drive was mounted successfully
      * FILEIO_ERROR_TOO_MANY_DRIVES_OPEN - You have already mounted
        the maximum number of drives. Change FILEIO_CONFIG_MAX_DRIVES in
        fileio_config.h to increase this.
      * FILEIO_ERROR_WRITE - The library was not able to write cached
        data in the buffer to the device (can occur when using multiple drives
        and single buffer mode)
      * FILEIO_ERROR_INIT_ERROR - The driver's Media Initialize
        \function indicated that the media could not be initialized.
      * FILEIO_ERROR_UNSUPPORTED_SECTOR_SIZE - The media's sector size
        exceeds the maximum sector size specified in fileio_config.h
        (FILEIO_CONFIG_MEDIA_SECTOR_SIZE macro)
      * FILEIO_ERROR_BAD_SECTOR_READ - The stack could not read the
        boot sector of Master Boot Record from the media.
      * FILEIO_ERROR_BAD_PARTITION - The boot signature in the MBR is
        bad on your media device.
      * FILEIO_ERROR_UNSUPPORTED_FS - The partition is formatted with
        an unsupported file system.
      * FILEIO_ERROR_NOT_FORMATTED - One of the parameters in the boot
        sector is bad in the partition being mounted.
  *****************************************************************************/
FILEIO_ERROR_TYPE FILEIO_DriveMount (char driveId, const FILEIO_DRIVE_CONFIG * driveConfig, void * mediaParameters);

/***************************************************************************
    Function:
        int FILEIO_Format (FILEIO_DRIVE_CONFIG * config,
            void * mediaParameters, char mode,
            uint32_t serialNumber, char * volumeID)

    Summary:
        Formats a drive.

    Description:
        Formats a drive.

    Precondition:
        FILEIO_Initialize must have been called.

    Parameters:
        config - Drive configuration pointer
        mode - FILEIO_FORMAT_MODE specifier
        serialNumber - Serial number to write to the drive
        volumeId - Name of the drive.

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
***************************************************************************/
int FILEIO_Format (FILEIO_DRIVE_CONFIG * config, void * mediaParameters, FILEIO_FORMAT_MODE mode, uint32_t serialNumber, char * volumeId);

/***********************************************************************
  Function:
      int FILEIO_DriveUnmount (const char driveID)

  Summary:
    Unmounts a drive.
  Description:
    Unmounts a drive from the file system and writes any pending data to
    the drive.
  Conditions:
    FILEIO_DriveMount must have been called.
  Input:
    driveId -  The character representation of the mounted drive.
  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
  ***********************************************************************/
int FILEIO_DriveUnmount (const char driveId);

/******************************************************************************
  Function:
      int FILEIO_Remove (const char * pathName)

  Summary:
    Deletes a file.
  Description:
    Deletes the file specified by pathName.
  Conditions:
    The file's drive must be mounted and the file should exist.
  Input:
    pathName -  The path/name of the file.
  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet. Note
        that if the path cannot be resolved, the error will be returned for the
        current working directory.
        * FILEIO_ERROR_INVALID_ARGUMENT - The path could not be
          resolved.
        * FILEIO_ERROR_WRITE_PROTECTED - The device is write-protected.
        * FILEIO_ERROR_INVALID_FILENAME - The file name is invalid.
        * FILEIO_ERROR_DELETE_DIR - The file being deleted is actually
          a directory (use FILEIO_DirectoryRemove)
        * FILEIO_ERROR_ERASE_FAIL - The erase operation failed.
        * FILEIO_ERROR_FILE_NOT_FOUND - The file entries for this file
          are invalid or have already been erased.
        * FILEIO_ERROR_WRITE - The updated file data and entry could
          not be written to the device.
        * FILEIO_ERROR_DONE - The directory entry could not be found.
        * FILEIO_ERROR_BAD_SECTOR_READ - The directory entry could not
          be cached.
  ******************************************************************************/
int FILEIO_Remove (const char * pathName);

/*******************************************************************************
  Function:
      int FILEIO_Rename (const char * oldPathname, const char * newFilename)

  Summary:
    Renames a file.
  Description:
    Renames a file specifed by oldPathname to the name specified by
    newFilename.
  Conditions:
    The file's drive must be mounted and the file/path specified by
    oldPathname must exist.
  Input:
    oldPathName -  The path/name of the file to rename.
    newFileName -  The new name of the file.
  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet Note
        that if the path cannot be resolved, the error will be returned for the
        current working directory.
        * FILEIO_ERROR_INVALID_ARGUMENT - The path could not be
          resolved.
        * FILEIO_ERROR_WRITE_PROTECTED - The device is write-protected.
        * FILEIO_ERROR_INVALID_FILENAME - One of the file names is
          invalid.
        * FILEIO_ERROR_FILENAME_EXISTS - The new file name already
          exists on this device.
        * FILEIO_ERROR_FILE_NOT_FOUND - The file could not be found.
        * FILEIO_ERROR_WRITE - The updated file data and entry could
          not be written to the device.
        * FILEIO_ERROR_DONE - The directory entry could not be found or
          the library could not find a sufficient number of empty entries in the
          dir to store the new file name.
        * FILEIO_ERROR_BAD_SECTOR_READ - The directory entry could not
          be cached.
        * FILEIO_ERROR_ERASE_FAIL - The file's entries could not be
          erased (applies when renaming a long file name)
        * FILEIO_ERROR_DIR_FULL - New file entries could not be
          created.
        * FILEIO_ERROR_BAD_CACHE_READ - The lfn entries could not be
          cached.
  *******************************************************************************/
int FILEIO_Rename (const char * oldPathName, const char * newFileName);

/************************************************************
  Function:
      int FILEIO_DirectoryMake (const char * path)

  Summary:
    Creates the directory/directories specified by 'path.'

  Description:
    Creates the directory/directories specified by 'path.'

  Conditions:
    The specified drive must be mounted.

  Input:
    path -  Path string containing all directories to create.

  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
  ************************************************************/
int FILEIO_DirectoryMake (const char * path);

/*************************************************************************
  Function:
      int FILEIO_DirectoryChange (const char * path)

  Summary:
    Changes the current working directory.

  Description:
    Changes the current working directory to the directory specified by
    'path.'

  Conditions:
    The specified drive must be mounted and the directory being changed to
    should exist.

  Input:
    path -  The path of the directory to change to.

  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
  *************************************************************************/
int FILEIO_DirectoryChange (const char * path);

/******************************************************************************
  Function:
      uint16_t FILEIO_DirectoryGetCurrent (char * buffer, uint16_t size)

  Summary:
    Gets the name of the current working directory.
  Description:
    Gets the name of the current working directory and stores it in
    'buffer.' The directory name will be null-terminated. If the buffer
    size is insufficient to contain the whole path name, as much as
    possible will be copied and null-terminated.
  Conditions:
    A drive must be mounted.
  Input:
    buffer -  The buffer to contain the current working directory name.
    size -    Size of the buffer (bytes).
  Return:
      * uint16_t - The number of characters in the current working
        directory name. May exceed the size of the buffer. In this case, the
        name will be truncated to 'size' characters, but the full length of the
        path name will be returned.
      * Sets error code which can be retrieved with FILEIO_ErrorGet
        * FILEIO_ERROR_INVALID_ARGUMENT - The arguments for the buffer
          or its size were invalid.
        * FILEIO_ERROR_DIR_NOT_FOUND - One of the directories in your
          current working directory could not be found in its parent directory.
  ******************************************************************************/
uint16_t FILEIO_DirectoryGetCurrent (char * buffer, uint16_t size);

/************************************************************************
  Function:
      int FILEIO_DirectoryRemove (const char * pathName)

  Summary:
    Deletes a directory.
  Description:
    Deletes a directory. The specified directory must be empty.
  Conditions:
    The directory's drive must be mounted and the directory should exist.
  Input:
    pathName -  The path/name of the directory to delete.
  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
  ************************************************************************/
int FILEIO_DirectoryRemove (const char * pathName);

/***************************************************************************
  Function:
    FILEIO_ERROR_TYPE FILEIO_ErrorGet (char driveId)

    Summary:
        Gets the last error condition of a drive.

    Description:
        Gets the last error condition of the specified drive.

    Precondition:
        The drive must have been mounted.

    Parameters:
        driveId - The character representation of the drive.

    Returns:
        FILEIO_ERROR_TYPE - The last error that occurred on the drive.
***************************************************************************/
FILEIO_ERROR_TYPE FILEIO_ErrorGet (char driveId);

/***************************************************************************
  Function:
    void FILEIO_ErrorClear (char driveId)

    Summary:
        Clears the last error on a drive.

    Description:
        Clears the last error of the specified drive.

    Precondition:
        The drive must have been mounted.

    Parameters:
        driveId - The character representation of the drive.

    Returns:
        void
***************************************************************************/
void FILEIO_ErrorClear (char driveId);

/***************************************************************************************
  Function:
        int FILEIO_Open (FILEIO_OBJECT * filePtr, const char * pathName, uint16_t mode)

  Summary:
    Opens a file for access.
  Description:
    Opens a file for access using a combination of modes specified by the
    user.
  Conditions:
    The drive containing the file must be mounted.
  Input:
    filePtr -   Pointer to the file object to initialize
    pathName -  The path/name of the file to open.
    mode -      The mode in which the file should be opened. Specified by
                inclusive or'ing parameters from FILEIO_OPEN_ACCESS_MODES.
  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet Note
        that if the path cannot be resolved, the error will be returned for the
        current working directory.
        * FILEIO_ERROR_INVALID_ARGUMENT - The path could not be
          resolved.
        * FILEIO_ERROR_WRITE_PROTECTED - The device is write protected
          or this function was called in a write/create mode when writes are
          disabled in configuration.
        * FILEIO_ERROR_INVALID_FILENAME - The file name is invalid.
        * FILEIO_ERROR_ERASE_FAIL - There was an error when trying to
          truncate the file.
        * FILEIO_ERROR_WRITE - Cached file data could not be written to
          the device.
        * FILEIO_ERROR_DONE - The directory entry could not be found.
        * FILEIO_ERROR_BAD_SECTOR_READ - The directory entry could not
          be cached.
        * FILEIO_ERROR_DRIVE_FULL - There are no more clusters
          available on this device that can be allocated to the file.
        * FILEIO_ERROR_FILENAME_EXISTS - All of the possible alias
          values for this file are in use.
        * FILEIO_ERROR_BAD_CACHE_READ - There was an error caching LFN
          entries.
        * FILEIO_ERROR_INVALID_CLUSTER - The next cluster in the file
          is invalid (can occur in APPEND mode).
        * FILEIO_ERROR_COULD_NOT_GET_CLUSTER - There was an error
          finding the cluster that contained the specified offset (can occur in
          APPEND mode).
  ***************************************************************************************/
int FILEIO_Open (FILEIO_OBJECT * filePtr, const char * pathName, uint16_t mode);

/***************************************************************************
  Function:
    int FILEIO_Close (FILEIO_OBJECT * handle)

    Summary:
        Closes a file.

    Description:
        Closes a file.  This will save the unwritten data to the file and
        make the memory used to allocate a file available to open other
        files.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        handle - The handle of the file to close.

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet
        * FILEIO_ERROR_WRITE - Data could not be written to the device.
        * FILEIO_ERROR_BAD_CACHE_READ - The file's directory entry
          could not be cached.
***************************************************************************/
int FILEIO_Close (FILEIO_OBJECT * handle);

/***************************************************************************
  Function:
    int FILEIO_Flush (FILEIO_OBJECT * handle)

    Summary:
        Saves unwritten file data to the device without closing the file.

    Description:
        Saves unwritten file data to the device without closing the file.
        This function is useful if the user needs to continue writing to
        a file but also wants to ensure that data isn't lost in the event
        of a reset or power loss condition.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        handle - The handle of the file to flush.

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet
        * FILEIO_ERROR_WRITE - Data could not be written to the device.
        * FILEIO_ERROR_BAD_CACHE_READ - The file's directory entry
          could not be cached.
***************************************************************************/
int FILEIO_Flush (FILEIO_OBJECT * handle);

/***************************************************************************
  Function:
    int FILEIO_GetChar (FILEIO_OBJECT * handle)

    Summary:
        Reads a character from a file.

    Description:
        Reads a character from a file.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        handle - The handle of the file.

    Returns:
      * If Success: The character that was read (cast to an int).
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet
        * FILEIO_ERROR_WRITE_ONLY - The file is not opened in read
          mode.
        * FILEIO_ERROR_BAD_SECTOR_READ - There was an error reading the
          FAT to determine the next cluster in the file, or an error reading the
          file data.
        * FILEIO_ERROR_INVALID_CLUSTER - The next cluster in the file
          is invalid.
        * FILEIO_ERROR_EOF - There is no next cluster in the file (EOF)
        * FILEIO_ERROR_WRITE - Cached data could not be written to the
          device.
  *******************************************************************************/
int FILEIO_GetChar (FILEIO_OBJECT * handle);

/***************************************************************************
  Function:
    int FILEIO_PutChar (char c, FILEIO_OBJECT * handle)

    Summary:
        Writes a character to a file.

    Description:
        Writes a character to a file.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        c - The character to write.
        handle - The handle of the file.

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet
        * FILEIO_ERROR_READ_ONLY - The file was not opened in write
          mode.
        * FILEIO_ERROR_WRITE_PROTECTED - The media is write-protected.
        * FILEIO_ERROR_BAD_SECTOR_READ - There was an error reading the
          FAT to determine the next cluster in the file, or an error reading the
          file data.
        * FILEIO_ERROR_INVALID_CLUSTER - The next cluster in the file
          is invalid.
        * FILEIO_ERROR_WRITE - Cached data could not be written to the
          device.
        * FILEIO_ERROR_BAD_SECTOR_READ - File data could not be cached.
        * FILEIO_ERROR_DRIVE_FULL - There are no more clusters on the
          media that can be allocated to the file.
  *******************************************************************************/
int FILEIO_PutChar (char c, FILEIO_OBJECT * handle);

/***************************************************************************
  Function:
    size_t FILEIO_Read (void * buffer, size_t size, size_t count,
        FILEIO_OBJECT * handle)

    Summary:
        Reads data from a file.

    Description:
        Reads data from a file and stores it in 'buffer.'

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        buffer - The buffer that the data will be written to.
        size - The size of data objects to read, in bytes
        count - The number of data objects to read
        handle - The handle of the file.

    Returns:
    The number of data objects that were read. This value will match
    'count' if the read was successful, or be less than count if it was
    not.

    Sets error code which can be retrieved with FILEIO_ErrorGet:
      * FILEIO_ERROR_WRITE_ONLY - The file is not opened in read mode.
      * FILEIO_ERROR_BAD_SECTOR_READ - There was an error reading the
        FAT to determine the next cluster in the file, or an error reading the
        \file data.
      * FILEIO_ERROR_INVALID_CLUSTER - The next cluster in the file is
        invalid.
      * FILEIO_ERROR_EOF - There is no next cluster in the file (EOF)
      * FILEIO_ERROR_WRITE - Cached data could not be written to the
        device.
  *****************************************************************************/
size_t FILEIO_Read (void * buffer, size_t size, size_t count, FILEIO_OBJECT * handle);

/***************************************************************************
  Function:
    size_t FILEIO_Write (void * buffer, size_t size, size_t count,
        FILEIO_OBJECT * handle)

    Summary:
        Writes data to a file.

    Description:
        Writes data from 'buffer' to a file.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        buffer - The buffer that contains the data to write.
        size - The size of data objects to write, in bytes
        count - The number of data objects to write
        handle - The handle of the file.

    Returns:
    The number of data objects that were written. This value will match
    'count' if the write was successful, or be less than count if it was
    not.

    Sets error code which can be retrieved with FILEIO_ErrorGet:
      * FILEIO_ERROR_READ_ONLY - The file was not opened in write mode.
      * FILEIO_ERROR_WRITE_PROTECTED - The media is write-protected.
      * FILEIO_ERROR_BAD_SECTOR_READ - There was an error reading the
        FAT to determine the next cluster in the file, or an error reading the
        file data.
      * FILEIO_ERROR_INVALID_CLUSTER - The next cluster in the file is
        invalid.
      * FILEIO_ERROR_WRITE - Cached data could not be written to the
        device.
      * FILEIO_ERROR_BAD_SECTOR_READ - File data could not be cached.
      * FILEIO_ERROR_DRIVE_FULL - There are no more clusters on the
        media that can be allocated to the file.
  *****************************************************************************/
size_t FILEIO_Write (const void * buffer, size_t size, size_t count, FILEIO_OBJECT * handle);

/***************************************************************************
  Function:
    int FILEIO_Seek (FILEIO_OBJECT * handle, int32_t offset, int base)

    Summary:
        Changes the current read/write position in the file.

    Description:
        Changes the current read/write position in the file.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        handle - The handle of the file.
        offset - The offset of the new read/write position (in bytes) from
            the base location.  The offset will be added to FILEIO_SEEK_SET
            or FILEIO_SEEK_CUR, or subtracted from FILEIO_SEEK_END.
        base - The base location.  Is of the FILEIO_SEEK_BASE type.

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet
        * FILEIO_ERROR_WRITE - Cached data could not be written to the
          device.
        * FILEIO_ERROR_INVALID_ARGUMENT - The specified location
          exceeds the file's size.
        * FILEIO_ERROR_BAD_SECTOR_READ - There was an error reading the
          FAT to determine the next cluster in the file, or an error reading the
          file data.
        * FILEIO_ERROR_INVALID_CLUSTER - The next cluster in the file
          is invalid.
        * FILEIO_ERROR_DRIVE_FULL - There are no more clusters on the
          media that can be allocated to the file. Clusters will be allocated to
          the file if the file is opened in a write mode and the user seeks to
          the end of a file that ends on a cluster boundary.
        * FILEIO_ERROR_COULD_NOT_GET_CLUSTER - There was an error
          finding the cluster that contained the specified offset.
  *******************************************************************************/
int FILEIO_Seek (FILEIO_OBJECT * handle, int32_t offset, int base);

/***************************************************************************
  Function:
    bool FILEIO_Eof (FILEIO_OBJECT * handle)

    Summary:
        Determines if the file's current read/write position is at the end
        of the file.

    Description:
        Determines if the file's current read/write position is at the end
        of the file.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        handle - The handle of the file.

    Returns:
      * If EOF: true
      * If Not EOF: false
  *************************************************************************/
bool FILEIO_Eof (FILEIO_OBJECT * handle);

/***************************************************************************
  Function:
    long FILEIO_Tell (FILEIO_OBJECT * handle)

    Summary:
        Returns the current read/write position in the file.

    Description:
        Returns the current read/write position in the file.

    Precondition:
        The drive containing the file must be mounted and the file handle
        must represent a valid, opened file.

    Parameters:
        handle - THe handle of the file.

    Returns:
        long - Offset of the current read/write position from the beginning
            of the file, in bytes.
***************************************************************************/
long FILEIO_Tell (FILEIO_OBJECT * handle);

/******************************************************************************
  Function:
      int FILEIO_Find (const char * fileName, unsigned int attr,
          FILEIO_SEARCH_RECORD * record, bool newSearch)

  Summary:
    Searches for a file in the current working directory.
  Description:
    Searches for a file in the current working directory.
  Conditions:
    A drive must have been mounted by the FILEIO library.
  Input:
    fileName -   The file's name. May contain limited partial string search
                 elements. '?' can be used as a single\-character wild\-card
                 and '*' can be used as a multiple\-character wild card
                 (only at the end of the file's name or extension).
    attr -       Inclusive OR of all of the attributes (FILEIO_ATTRIBUTES
                 structure members) that a found file may have.
    record -     Structure containing parameters about the found file. Also
                 contains private information used for additional searches
                 for files that match the given criteria in the same
                 directory.
    newSearch -  true if this is the first search for the specified file
                 \parameters in the specified directory, false otherwise.
                 This parameter must be specified as 'true' the first time
                 this function is called with any given FILEIO_SEARCH_RECORD
                 structure. The same FILEIO_SEARCH_RECORD structure should
                 be used with subsequent calls of this function to search
                 for additional files matching the given criteria.
  Return:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE
      * Returns file information in the record parameter.

      * Sets error code which can be retrieved with FILEIO_ErrorGet Note
        that if the path cannot be resolved, the error will be returned for the
        current working directory.
        * FILEIO_ERROR_INVALID_ARGUMENT - The path could not be
          resolved.
        * FILEIO_ERROR_INVALID_FILENAME - The file name is invalid.
        * FILEIO_ERROR_BAD_CACHE_READ - There was an error searching
          directory entries.
        * FILEIO_ERROR_DONE - File not found.
  ******************************************************************************/
int FILEIO_Find (const char * fileName, unsigned int attr, FILEIO_SEARCH_RECORD * record, bool newSearch);

/***************************************************************************
  Function:
    int FILEIO_LongFileNameGet (FILEIO_SEARCH_RECORD * record, uint16_t * buffer, uint16_t length)

    Summary:
        Obtains the long file name of a file found by the FILEIO_Find
        function.

    Description:
        This function will obtain the long file name of a file found
        by the FILEIO_Find function and copy it into a user-specified
        buffer.  The name will be returned in unicode characters.

    Precondition:
        A drive must have been mounted by the FILEIO library.  The
        FILEIO_SEARCH_RECORD structure must contain valid file information
        obtained from the FILEIO_Find function.

    Parameters:
        record - The file record obtained from a successful call of
            FILEIO_Find.
        buffer - A buffer to contain the long file name of the file.
        length - The length of the buffer, in 16-bit words.

    Returns:
      * If Success: FILEIO_RESULT_SUCCESS
      * If Failure: FILEIO_RESULT_FAILURE

      * Sets error code which can be retrieved with FILEIO_ErrorGet Note
        that if the path cannot be resolved, the error will be returned for the
        current working directory.
        * FILEIO_ERROR_INVALID_ARGUMENT - The path could not be
          resolved.
        * FILEIO_ERROR_NO_LONG_FILE_NAME - The short file name does not
          have an associated long file name.
        * FILEIO_ERROR_DONE - The directory entry could not be cached
          because the entryOffset contained in record was invalid.
        * FILEIO_ERROR_WRITE - Cached data could not be written to the
          device.
        * FILEIO_ERROR_BAD_SECTOR_READ - The directory entry could not
          be cached because there was an error reading from the device.
  ***************************************************************************************************/
int FILEIO_LongFileNameGet (FILEIO_SEARCH_RECORD * record, uint16_t * buffer, uint16_t length);

/********************************************************************
  Function:
      FILEIO_FILE_SYSTEM_TYPE FILEIO_FileSystemTypeGet (char driveId)

  Summary:
    Describes the file system type of a file system.
  Description:
    Describes the file system type of a file system.
  Conditions:
    A drive must have been mounted by the FILEIO library.
  Input:
    driveId -  Character representation of the mounted device.
  Return:
      * If Success: FILEIO_FILE_SYSTEM_TYPE enumeration member
      * If Failure: FILEIO_FILE_SYSTEM_NONE
  ********************************************************************/
FILEIO_FILE_SYSTEM_TYPE FILEIO_FileSystemTypeGet (char driveId);

/*********************************************************************************
  Function:
    void FILEIO_DrivePropertiesGet()

  Summary:
    Allows user to get the drive properties (size of drive, free space, etc)

   Conditions:
    1) ALLOW_GET_FILEIO_DRIVE_PROPERTIES must be defined in FSconfig.h
    2) a FS_FILEIO_DRIVE_PROPERTIES object must be created before the function is called
    3) the new_request member of the FS_FILEIO_DRIVE_PROPERTIES object must be set before
        calling the function for the first time.  This will start a new search.
    4) this function should not be called while there is a file open.  Close all
        files before calling this function.

  Input:
    properties - a pointer to a FS_FILEIO_DRIVE_PROPERTIES object where the results should
      be stored.

  Return Values:
    This function returns void.  The properties_status of the previous call of
      this function is located in the properties.status field.  This field has
      the following possible values:

    FILEIO_GET_PROPERTIES_NO_ERRORS - operation completed without error.  Results
      are in the properties object passed into the function.
    FILEIO_GET_PROPERTIES_DRIVE_NOT_MOUNTED - there is no mounted disk.  Results in
      properties object is not valid
    FILEIO_GET_PROPERTIES_CLUSTER_FAILURE - there was a failure trying to read a
      cluster from the drive.  The results in the properties object is a partial
      result up until the point of the failure.
    FILEIO_GET_PROPERTIES_STILL_WORKING - the search for free sectors is still in
      process.  Continue calling this function with the same properties pointer
      until either the function completes or until the partial results meets the
      application needs.  The properties object contains the partial results of
      the search and can be used by the application.

  Side Effects:
    Can cause errors if called when files are open.  Close all files before
    calling this function.

    Calling this function without setting the new_request member on the first
    call can result in undefined behavior and results.

    Calling this function after a result is returned other than
    FILEIO_GET_PROPERTIES_STILL_WORKING can result in undefined behavior and results.

  Description:
    This function returns the information about the mounted drive.  The results
    member of the properties object passed into the function is populated with
    the information about the drive.

    Before starting a new request, the new_request member of the properties
    input parameter should be set to true.  This will initiate a new search
    request.

    This function will return before the search is complete with partial results.
    All of the results except the free_clusters will be correct after the first
    call.  The free_clusters will contain the number of free clusters found up
    until that point, thus the free_clusters result will continue to grow until
    the entire drive is searched.  If an application only needs to know that a
    certain number of bytes is available and doesn't need to know the total free
    size, then this function can be called until the required free size is
    verified.  To continue a search, pass a pointer to the same FILEIO_FILEIO_DRIVE_PROPERTIES
    object that was passed in to create the search.

    A new search request should be made once this function has returned a value
    other than FILEIO_GET_PROPERTIES_STILL_WORKING.  Continuing a completed search
    can result in undefined behavior or results.

    Typical Usage:
    <code>
    FILEIO_DRIVE_PROPERTIES disk_properties;

    disk_properties.new_request = true;

    do
    {
        FILEIO_DiskPropertiesGet(&disk_properties, 'A');
    } while (disk_properties.properties_status == FILEIO_GET_PROPERTIES_STILL_WORKING);
    </code>

    results.disk_format - contains the format of the drive.  Valid results are
      FAT12(1), FAT16(2), or FAT32(3).

    results.sector_size - the sector size of the mounted drive.  Valid values are
      512, 1024, 2048, and 4096.

    results.sectors_per_cluster - the number sectors per cluster.

    results.total_clusters - the number of total clusters on the drive.  This
      can be used to calculate the total disk size (total_clusters *
      sectors_per_cluster * sector_size = total size of drive in bytes)

    results.free_clusters - the number of free (unallocated) clusters on the drive.
      This can be used to calculate the total free disk size (free_clusters *
      sectors_per_cluster * sector_size = total size of drive in bytes)

  Remarks:
    PIC24F size estimates:
      Flash - 400 bytes (-Os setting)

    PIC24F speed estimates:
      Search takes approximately 7 seconds per Gigabyte of drive space.  Speed
        will vary based on the number of sectors per cluster and the sector size.
  *********************************************************************************/
void FILEIO_DrivePropertiesGet (FILEIO_DRIVE_PROPERTIES* properties, char driveId);

#endif


{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

{-| Unittests for the SlotMap.

-}

{-

Copyright (C) 2014 Google Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-}

module Test.Ganeti.SlotMap
  ( testSlotMap
  , genSlotLimit
  , genTestKey
  , overfullKeys
  ) where

import Prelude hiding (all)

import Control.Applicative
import Control.Monad
import Data.Foldable (all)
import qualified Data.Map as Map
import Data.Map (Map, member, keys, keysSet)
import Data.Set (Set, size, union)
import qualified Data.Set as Set
import Data.Traversable (traverse)
import Test.HUnit
import Test.QuickCheck

import Test.Ganeti.TestCommon
import Test.Ganeti.TestHelper
import Test.Ganeti.Types ()

import Ganeti.SlotMap

{-# ANN module "HLint: ignore Use camelCase" #-}

-- | Generates a number typical for the limit of a `Slot`.
-- Useful for constructing resource bounds when not directly constructing
-- the relevant `Slot`s.
genSlotLimit :: Gen Int
genSlotLimit = frequency [ (9, choose (1, 5))
                         , (1, choose (1, 100))
                         ] -- Don't create huge slot limits.

instance Arbitrary Slot where
  arbitrary = do
    limit <- genSlotLimit
    occ <- choose (0, limit * 2)
    return $ Slot occ limit

-- | Generates a number typical for the occupied count of a `Slot`.
-- Useful for constructing `CountMap`s.
genSlotCount :: Gen Int
genSlotCount = slotOccupied <$> arbitrary

-- | Takes a slot and resamples its `slotOccupied` count to fit the limit.
resampleFittingSlot :: Slot -> Gen Slot
resampleFittingSlot (Slot _ limit) = do
  occ <- choose (0, limit)
  return $ Slot occ limit

-- | What we use as key for testing `SlotMap`s.
type TestKey = String

-- | Generates short strings used as `SlotMap` keys.
--
-- We limit ourselves to a small set of key strings with high probability to
-- increase the chance that `SlotMap`s actually have more than one slot taken.
genTestKey :: Gen TestKey
genTestKey = frequency [ (9, elements ["a", "b", "c", "d", "e"])
                       , (1, genPrintableAsciiString)
                       ]

-- | Generates small lists.
listSizeGen :: Gen Int
listSizeGen = frequency [ (9, choose (1, 5))
                        , (1, choose (1, 100))
                        ]

-- | Generates a `SlotMap` given a generator for the keys (see `genTestKey`).
genSlotMap :: (Ord a) => Gen a -> Gen (SlotMap a)
genSlotMap keyGen = do
  n <- listSizeGen  -- don't create huge `SlotMap`s
  Map.fromList <$> vectorOf n ((,) <$> keyGen <*> arbitrary)

-- | Generates a `CountMap` given a generator for the keys (see `genTestKey`).
genCountMap :: (Ord a) => Gen a -> Gen (CountMap a)
genCountMap keyGen = do
  n <- listSizeGen  -- don't create huge `CountMap`s
  Map.fromList <$> vectorOf n ((,) <$> keyGen <*> genSlotCount)

-- | Tells which keys of a `SlotMap` are overfull.
overfullKeys :: (Ord a) => SlotMap a -> Set a
overfullKeys sm =
  Set.fromList [ a | (a, Slot occ limit) <- Map.toList sm, occ > limit ]

-- | Generates a `SlotMap` for which all slots are within their limits.
genFittingSlotMap :: (Ord a) => Gen a -> Gen (SlotMap a)
genFittingSlotMap keyGen = do
  -- Generate a SlotMap, then resample all slots to be fitting.
  slotMap <- traverse resampleFittingSlot =<< genSlotMap keyGen
  when (isOverfull slotMap) $ error "BUG: FittingSlotMap Gen is wrong"
  return slotMap

-- * Test cases

case_isOverfull :: Assertion
case_isOverfull = do

  assertBool "overfull"
    . isOverfull $ Map.fromList [("buck", Slot 3 2)]

  assertBool "not overfull"
    . not . isOverfull $ Map.fromList [("buck", Slot 2 2)]

  assertBool "empty"
    . not . isOverfull $ (Map.fromList [] :: SlotMap TestKey)

case_occupySlots_examples :: Assertion
case_occupySlots_examples = do
  let a n = ("a", Slot n 2)
  let b n = ("b", Slot n 4)

  let sm = Map.fromList [a 1, b 2]
      cm = Map.fromList [("a", 1), ("b", 1), ("c", 5)]

  assertEqual "fitting occupySlots"
    (sm `occupySlots` cm)
    (Map.fromList [a 2, b 3, ("c", Slot 5 0)])

-- | Union of the keys of two maps.
keyUnion :: (Ord a) => Map a b -> Map a c -> Set a
keyUnion a b = keysSet a `union` keysSet b

-- | Tests properties of `SlotMap`s being filled up.
prop_occupySlots :: Property
prop_occupySlots =
  forAll arbitrary $ \(sm :: SlotMap Int, cm :: CountMap Int) ->
    let smOcc = sm `occupySlots` cm
    in conjoin
         [ counterexample "input keys are preserved" $
             all (`member` smOcc) (keyUnion sm cm)
         , counterexample "all keys must come from the input keys" $
             all (`Set.member` keyUnion sm cm) (keys smOcc)
         ]

-- | Tests for whether there's still space for a job given its rate
-- limits.
case_hasSlotsFor_examples :: Assertion
case_hasSlotsFor_examples = do
  let a n = ("a", Slot n 2)
  let b n = ("b", Slot n 4)
  let c n = ("c", Slot n 8)

  let sm = Map.fromList [a 1, b 2]

  assertBool "fits" $
    sm `hasSlotsFor` Map.fromList [("a", 1), ("b", 1)]

  assertBool "doesn't fit"
    . not $ sm `hasSlotsFor` Map.fromList [("a", 1), ("b", 3)]

  let smOverfull = Map.fromList [a 1, b 2, c 10]

  assertBool "fits (untouched keys overfull)" $
    isOverfull smOverfull
      && smOverfull `hasSlotsFor` Map.fromList [("a", 1), ("b", 1)]

  assertBool "empty fitting" $
    Map.empty `hasSlotsFor` (Map.empty :: CountMap TestKey)

  assertBool "empty not fitting"
    . not $ Map.empty `hasSlotsFor` Map.fromList [("a", 1), ("b", 100)]

  assertBool "empty not fitting"
    . not $ Map.empty `hasSlotsFor` Map.fromList [("a", 1)]

-- | Tests properties of `hasSlotsFor` on `SlotMap`s that are known to
-- respect their limits.
prop_hasSlotsFor_fitting :: Property
prop_hasSlotsFor_fitting =
  forAll (genFittingSlotMap genTestKey) $ \sm ->
  forAll (genCountMap genTestKey) $ \cm ->
    sm `hasSlotsFor` cm ==? not (isOverfull $ sm `occupySlots` cm)

-- | Tests properties of `hasSlotsFor`, irrespective of whether the
-- input `SlotMap`s respect their limits or not.
prop_hasSlotsFor :: Property
prop_hasSlotsFor =
  let -- Generates `SlotMap`s for combining.
      genMaps = resize 10 $ do  -- We don't need very large SlotMaps.
        sm1 <- genSlotMap genTestKey
        -- We need to make sm2 smaller to make `hasSlots` below more
        -- likely (otherwise the LHS of ==> is always false).
        sm2 <- sized $ \n -> resize (n `div` 3) (genSlotMap genTestKey)
        -- We also want to test (sm1, sm1); we have to make it more
        -- likely for it to ever happen.
        frequency [ (1, return (sm1, sm1))
                  , (9, return (sm1, sm2)) ]

  in forAll genMaps $ \(sm1, sm2) ->
      let fits             = sm1 `hasSlotsFor` toCountMap sm2
          smOcc            = sm1 `occupySlots` toCountMap sm2
          oldOverfullBucks = overfullKeys sm1
          newOverfullBucks = overfullKeys smOcc
      in conjoin
           [ counterexample "if there's enough extra space, then the new\
                            \ overfull keys must be as before" $
             fits ==> (newOverfullBucks ==? oldOverfullBucks)
           -- Note that the other way around does not hold:
           --   (newOverfullBucks == oldOverfullBucks) ==> fits
           , counterexample "joining SlotMaps must not change the number of\
                            \ overfull keys (but may change their slot\
                            \ counts"
               . property $ size newOverfullBucks >= size oldOverfullBucks
           ]

testSuite "SlotMap"
            [ 'case_isOverfull
            , 'case_occupySlots_examples
            , 'prop_occupySlots
            , 'case_hasSlotsFor_examples
            , 'prop_hasSlotsFor_fitting
            , 'prop_hasSlotsFor
            ]


% get root of current file
root = fullfile(fileparts(mfilename('fullpath')),'../');

p_generated = genpath([root '/Core']);
addpath(p_generated);
addpath([root '/IO']);
addpath([root '/Data']);
addpath([root '/Scripts']);

p_generated = genpath([root '/ThirdParty/tetgen1.4.3/bin']);
addpath(p_generated);

p_generated = genpath([root '/ThirdParty/maslib/bin']);
addpath(p_generated);

clear p_generated;


package main

import (
	"context"

	aiplatform "cloud.google.com/go/aiplatform/apiv1beta1"
	aiplatformpb "cloud.google.com/go/aiplatform/apiv1beta1/aiplatformpb"
)

func main() {
	ctx := context.Background()
	// This snippet has been automatically generated and should be regarded as a code template only.
	// It will require modifications to work:
	// - It may require correct/in-range values for request initialization.
	// - It may require specifying regional endpoints when creating the service client as shown in:
	//   https://pkg.go.dev/cloud.google.com/go#hdr-Client_Options
	c, err := aiplatform.NewEndpointClient(ctx)
	if err != nil {
		// TODO: Handle error.
	}
	defer c.Close()

	req := &aiplatformpb.GetEndpointRequest{
		// TODO: Fill request struct fields.
		// See https://pkg.go.dev/cloud.google.com/go/aiplatform/apiv1beta1/aiplatformpb#GetEndpointRequest.
	}
	resp, err := c.GetEndpoint(ctx, req)
	if err != nil {
		// TODO: Handle error.
	}
	// TODO: Use resp.
	_ = resp
}

// [END aiplatform_v1beta1_generated_EndpointService_GetEndpoint_sync]