/**
{file:
    {name: LmiFile.h}
    {description: Objects to provide access to files on disk or other persistent storage.}
    {copyright:
    	(c) 2011-2018 Vidyo, Inc.,
    	433 Hackensack Avenue,
    	Hackensack, NJ 07601.

    	All rights reserved.

    	The information contained herein is proprietary to Vidyo, Inc.
    	and shall not be reproduced, copied (in whole or in part), adapted,
    	modified, disseminated, transmitted, transcribed, stored in a retrieval
    	system, or translated into any language in any form by any means
    	without the express written consent of Vidyo, Inc.
    	                  ***** CONFIDENTIAL *****
    }
}
*/
#ifndef LMI_FILE_H_
#define LMI_FILE_H_

#include <Lmi/Utils/LmiTypes.h>
#include <Lmi/Utils/LmiAllocator.h>
#include <Lmi/Utils/LmiString.h>
#include <Lmi/Utils/LmiStringVector.h>
#include <Lmi/Utils/LmiMisc.h>
#include <Lmi/Os/LmiPath.h>
#include <Lmi/Os/LmiDirectoryEntry.h>
#include <Lmi/Os/LmiInterruptableSleeper.h>
#include <Lmi/Os/LmiAtomicInteger.h>

LMI_BEGIN_EXTERN_C

/* TODO: Move this to its own file.  Useful for more than just LmiFile. */
/**
	{type:
		{name: LmiErrorCode}
		{parent: LmiFile}
		{description: An operating system specific error codes.  Typically, variables of this type hold Windows GetLastError() or errno values.}
	}
*/
#if (defined(LMI_HAVE_WINDOWS_H) || defined (LMI_HAVE_WINSOCK2_H)) && !defined(__CYGWIN__)
#if LMI_HAVE_WINSOCK2_H
#include <winsock2.h>
#elif LMI_HAVE_WINDOWS_H
#include <windows.h>
#endif
typedef DWORD LmiErrorCode;
#else
typedef int LmiErrorCode;
#endif

/**
	{function:
		{name: LmiErrorCodeCStr}
		{parent: LmiErrorCode}
		{description: Get a human readable description of an error.}
		{prototype: const char* LmiErrorCodeCStr(LmiErrorCode errorCode)}
		{parameter:
			{name: errorCode}
			{description: An error code.}}
		{return: Returns a human readable description of the error.}
	}
*/
const char* LmiErrorCodeCStr(LmiErrorCode errorCode);

#if LMI_HAVE_STDIO_H
#	include <stdarg.h>
#	include <stdio.h>
#	define LMI_FILE_USE_STDIO_ 1
typedef FILE* LmiFileHandle_;

#	if LMI_HAVE_FSEEKO64 && LMI_HAVE___OFF64_T
typedef __off64_t LmiFilePosition_;
#	elif LMI_HAVE_FSEEKO64 && LMI_HAVE_OFF64_T
typedef off64_t LmiFilePosition_;
#	elif LMI_HAVE_FSEEKO
typedef off_t LmiFilePosition_;
#	elif LMI_HAVE__FSEEKI64
typedef __int64 LmiFilePosition_;
#	else
typedef long LmiFilePosition_;
#	endif
	/* Include windows header for MAX_PATH */
#	if LMI_HAVE_WINSOCK2_H
#		include <winsock2.h>
#	elif LMI_HAVE_WINDOWS_H
#		include <windows.h>
#	endif

#	if defined(PATH_MAX)
#		define LMI_FILE_PATH_MAX PATH_MAX
#	elif defined(MAX_PATH)
#		define LMI_FILE_PATH_MAX MAX_PATH
#	else
#		error Need definition of LMI_FILE_PATH_MAX
#	endif

#else	/* Not stdio.h */
#	define LMI_FILE_USE_NONE_ 1
typedef int LmiFileHandle_;
typedef int LmiFilePosition_;
#	define LMI_FILE_PATH_MAX 256
#endif /* LMI_HAVE_STDIO_H */

/**
	{type:
		{name: LmiFileOpenFlags}
		{parent: LmiFile}
		{description: A flag representing a mode in which to open an LmiFile.}
		{value: {name: LMI_FILEOPENFLAGS_Read} {description: Indicates that a file is to be opened for reading.}}
		{value: {name: LMI_FILEOPENFLAGS_Write} {description: Indicates that a file is to be opened for writing.  If the file does not exist the system will attempt to create it.}}
		{value: {name: LMI_FILEOPENFLAGS_Append} {description: Indicates that a file is to be opened for appending (writing at the end of file).  This is only meaningful if LMI_FILEOPENFLAGS_Write is also set in a flag set.}}
		{value: {name: LMI_FILEOPENFLAGS_Binary} {description: Indicates that a file is to be read and/or written in binary mode, rather than text mode, for systems where this is a meaningful distinction.}}
	}
*/
enum {
	LMI_FILEOPENFLAGS_Read = 1 << 0,
	LMI_FILEOPENFLAG_Read = 1 << 0,		/* Deprecated */
	LMI_FILEOPENFLAGS_Write = 1 << 1,
	LMI_FILEOPENFLAG_Write = 1 << 1,	/* Deprecated */
	LMI_FILEOPENFLAGS_Append = 1 << 2,
	LMI_FILEOPENFLAG_Append = 1 << 2,	/* Deprecated */
	LMI_FILEOPENFLAGS_Binary = 1 << 3,
	LMI_FILEOPENFLAG_Binary = 1 << 3,	/* Deprecated */
};
typedef int LmiFileOpenFlags;

/**
	{type:
		{name: LmiFileReadOptions}
		{parent: LmiFile}
		{description: Options representing a mode in which LmiFileReadString should operate.}
 		{value: {name: LMI_FILEREADOPTIONS_None} {description: Indicates no options are demanded.}}
		{value: {name: LMI_FILEREADOPTIONS_IncludeDelimiter} {description: Indicates that the terminating delimiter should be included in the output string.}}
	}
*/
enum {
	LMI_FILEREADOPTIONS_None = 0,
	LMI_FILEREADOPTIONS_IncludeDelimiter = 1 << 0
};
typedef int LmiFileReadOptions;

/**
	{type:
		{name: LmiFileHandle}
		{parent: LmiFile}
		{description: An operating system handle associated with an open stream.  Typically, this is an alias for a pointer of
 		type FILE defined in the Standard C Library.}
	}
*/
typedef LmiFileHandle_ LmiFileHandle;

/**
	{type:
		{name: LmiFilePosition}
		{parent: LmiFile}
		{description: A value representing a read/write position within an LmiFile object.}
		{note: This will be a signed integer type, but may be able to hold values larger than the maximum value of LmiSizeT.}
	}
*/
typedef LmiFilePosition_ LmiFilePosition;

/**
	{type:
		{name: LmiFile}
		{parent: Os}
		{include: Lmi/Os/LmiFile.h}
		{description: Represents a file on a file system.}
		{constant:
			{name: LMI_FILE_PATH_MAX}
			{type: LmiSizeT}
			{description: The maximum size of a pathname on the platform.}}
 		{constant symbolic="no": 
 			{name: lmiFileIn}
 			{type: LmiFile} 
 			{description: An LmiFile object representing \"standard in\".}}
  		{constant symbolic="no":
 			{name: lmiFileOut}
 			{type: LmiFile} 
 			{description: An LmiFile object representing \"standard out\".}}
  		{constant symbolic="no": 
 			{name: lmiFileError}
 			{type: LmiFile} 
 			{description: An LmiFile object representing \"standard error\".}}
	}
*/
typedef struct {
	LmiDirectoryEntry base;
	LmiBool closeHandle;
	LmiFileHandle handle;
	LmiFileOpenFlags flags;
} LmiFile;

/* Standard in, out and error */
extern LmiFile lmiFileIn;
extern LmiFile lmiFileOut;
extern LmiFile lmiFileError;

/**
	{function:
		{name: LmiFileConstruct}
		{parent: LmiFile}
		{description: Construct an LmiFile object.}
		{prototype: LmiFile* LmiFileConstruct(LmiFile* f, const LmiPath* path)}
		{parameter:
			{name: f}
			{description: The file object to construct.}}
		{parameter:
			{name: path}
			{description: The path on disk to the file.  The syntax of this path is platform-dependent.}}
		{return: The file object on success, or NULL on failure.}
	}
*/
LmiFile* LmiFileConstruct(LmiFile* f, const LmiPath* path);

/**
	{function:
		{name: LmiFileConstructCStr}
		{parent: LmiFile}
		{description: Construct an LmiFile object.}
		{prototype: LmiFile* LmiFileConstructCStr(LmiFile* f, const char* path, LmiAllocator* a)}
		{parameter:
			{name: f}
			{description: The file object to construct.}}
		{parameter:
			{name: path}
			{description: The path on disk to the file.  The syntax of this path is platform-dependent.}}
 		{parameter:
			{name: a}
			{description: An allocator.}}
		{return: The file object on success, or NULL on failure.}
	}
*/
LmiFile* LmiFileConstructCStr(LmiFile* f, const char* path, LmiAllocator* alloc);

/**
	{function:
		{name: LmiFileConstructAndOpen}
		{parent: LmiFile}
		{description: Construct an LmiFile object and open the corresponding file on the file system. }
		{prototype: LmiFile* LmiFileConstructAndOpen(LmiFile* f, const LmiPath* path, LmiFileOpenFlags flags)}
		{parameter:
			{name: f}
			{description: The file object to use to open the file.}}
 		{parameter:
			{name: path}
			{description: The path on disk to the file to open.  The syntax of this path is platform-dependent.}}
		{parameter:
			{name: flags}
			{description: A set of flags (bitwise-or'd together) indicating the mode in which to open the file.}}
		{return: The file object on success, or NULL on failure.}
	}
*/
LmiFile* LmiFileConstructAndOpen(LmiFile* f, const LmiPath* path, LmiFileOpenFlags flags);

/**
	{function:
		{name: LmiFileConstructAndOpenCStr}
		{parent: LmiFile}
		{description: Construct an LmiFile object and open the corresponding file on the file system. }
		{prototype: LmiFile* LmiFileConstructAndOpenCStr(LmiFile* f, const char* path, LmiFileOpenFlags flags, LmiAllocator* a)}
		{parameter:
			{name: f}
			{description: The file object to use to open the file.}}
 		{parameter:
			{name: path}
			{description: The path on disk to the file to open.  The syntax of this path is platform-dependent.}}
		{parameter:
			{name: flags}
			{description: A set of flags (bitwise-or'd together) indicating the mode in which to open the file.}}
  		{parameter:
			{name: a}
			{description: An allocator.}}
		{return: The file object on success, or NULL on failure.}
	}
*/
LmiFile* LmiFileConstructAndOpenCStr(LmiFile* f, const char* path, LmiFileOpenFlags flags, LmiAllocator* a);

/**
	{function:
		{name: LmiFileConstructFromFileHandle}
		{parent: LmiFile}
		{description: Construct an LmiFile object from an open stream associated with an operating system file handle.}
		{prototype: LmiFile* LmiFileConstructFromFileHandle(LmiFile* f, LmiFileHandle handle, const LmiPath* path, LmiFileOpenFlags flags)}
		{parameter:
			{name: f}
			{description: The file object to construct.}}
 		{parameter:
			{name: handle}
			{description: A file handle.}}
		{parameter:
			{name: path}
			{description: The path to the file to open.  The syntax of this path is platform-dependent.
 			  The path is provided strictly for logging purposes and do not effect the stream associated with the file handle.}}
		{parameter:
			{name: flags}
			{description: A set of flags (bitwise-or'd together) indicating the mode in which the file was opened.
 			  The flags are provided strictly for logging purposes and do not effect the stream associated with the file handle.}}
		{return: The file object on success, or NULL on failure.}
	}
*/
LmiFile* LmiFileConstructFromFileHandle(LmiFile* f, LmiFileHandle handle, const LmiPath* path, LmiFileOpenFlags flags);

/**
	{function:
		{name: LmiFileConstructFromFileHandleCStr}
		{parent: LmiFile}
		{description: Construct an LmiFile object from an open stream associated with an operating system file handle.}
		{prototype: LmiFile* LmiFileConstructFromFileHandleCStr(LmiFile* f, LmiFileHandle handle, const char* path, LmiFileOpenFlags flags, LmiAllocator* a)}
		{parameter:
			{name: f}
			{description: The file object to construct.}}
 		{parameter:
			{name: handle}
			{description: A file handle.}}
		{parameter:
			{name: path}
			{description: The path to the file to open.  The syntax of this path is platform-dependent.
 			  The path is provided strictly for logging purposes and do not effect the stream associated with the file handle.}}
		{parameter:
			{name: flags}
			{description: A set of flags (bitwise-or'd together) indicating the mode in which the file was opened.
 			  The flags are provided strictly for logging purposes and do not effect the stream associated with the file handle.}}
  		{parameter:
			{name: a}
			{description: An allocator.}}
		{return: The file object on success, or NULL on failure.}
	}
*/
LmiFile* LmiFileConstructFromFileHandleCStr(LmiFile* f, LmiFileHandle handle, const char* path, LmiFileOpenFlags flags, LmiAllocator* a);
/**
	{function:
		{name: LmiFileDestruct}
		{parent: LmiFile}
		{description: Destruct an LmiFile object, closing it if it is open.}
		{prototype: void LmiFileDestruct(LmiFile* f)}
		{parameter:
			{name: f}
			{description: The object to destruct.}}
	}
*/
void LmiFileDestruct(LmiFile* f);

/**
	{function:
		{name: LmiFileGetPath}
		{parent: LmiFile}
		{description: Get the file\'s path. }
		{prototype: const LmiPath* LmiFileGetPath(const LmiFile* f)}
		{parameter:
			{name: f}
			{description: The file object.}}
		{return: Returns the file\'s path.}
	}
*/
const LmiPath* LmiFileGetPath(const LmiFile* f);

/**
{function:
	{name: LmiFileExists}
	{parent: LmiFile}
	{description: Determines whether the file exists on the file system.}
	{prototype: LmiBool LmiFileExists(const LmiFile* x)}
	{parameter:
		{name: x}
		{description: The file object.}}
	{return: Returns LMI_TRUE if the file exists as a regular file or LMI_FALSE otherwise.}
}
*/
LmiBool LmiFileExists(const LmiFile* x);

/**
	{function:
		{name: LmiFileIsOpened}
		{parent: LmiFile}
		{description: Determine whether the file is opened }
		{prototype: LmiBool LmiFileIsOpened(const LmiFile* f)}
		{parameter:
			{name: f}
			{description: The file object.}}
		{return: Returns LMI_TRUE on if the file is already opened or LMI_FALSE if it is not.}
	}
*/
LmiBool LmiFileIsOpened(const LmiFile* f);

/**
	{function:
		{name: LmiFileOpen}
		{parent: LmiFile}
		{description: Open the file.  If the file is already opened, this will fail.}
		{prototype: LmiBool LmiFileOpen(LmiFile* f, LmiFileOpenFlags flags)}
		{parameter:
			{name: f}
			{description: The file object to use to open the file.}}
		{parameter:
			{name: flags}
			{description: A set of flags (bitwise-or'd together) indicating the mode in which to open the file.}}
		{return: Returns LMI_TRUE on success, LMI_FALSE on failure.}
	}
*/
LmiBool LmiFileOpen(LmiFile* f, LmiFileOpenFlags flags);

/**
	{function:
		{name: LmiFileClose}
		{parent: LmiFile}
		{description: Close a file object.}
		{prototype: LmiBool LmiFileClose(LmiFile* f)}
		{parameter:
			{name: f}
			{description: The file object to close}}
		{return: LMI_TRUE on success, LMI_FALSE if an error was reported in closing the file.}
	}
*/
LmiBool LmiFileClose(LmiFile* f);

/**
	{function:
		{name: LmiFileRead}
		{parent: LmiFile}
		{description: Read data from a file at its current read position.}
		{prototype: LmiSizeT LmiFileRead(LmiFile* f, LmiUint8* buf, LmiSizeT bufSize)}
		{parameter:
			{name: f}
			{description: The file from which to read.}}
		{parameter:
			{name: buf}
			{description: A buffer into which to place the data read.}}
		{parameter:
			{name: bufSize}
			{description: The size of the buffer into which to read data, in bytes.}}
		{return: The number of bytes actually read.  On error or EOF, this will be 0.}
	}
*/
LmiSizeT LmiFileRead(LmiFile* f, LmiUint8* buf, LmiSizeT bufSize);

/**
	{function:
		{name: LmiFileReadString}
		{parent: LmiFile}
		{description:
			Read a string from a file starting at its current read position up to either
			the first occurrence of one of the {code:delimiters} or {code:n} characters
			which ever comes first. The read position is advanced to the byte just passed the
			first occurrence of one of the {code:delimiters} (regardless of {code:n}).
		}
		{prototype: const LmiString* LmiFileReadString(LmiFile* f, LmiFileReadOptions options, const char* delimiters, LmiSizeT n, LmiString* s)}
		{parameter:
			{name: f}
			{description: The file from which to read.}}
 		{parameter:
			{name: options}
			{description: A set of options (bitwise-or'd together) indicating the mode in which to read the string.}}
		{parameter:
			{name: delimiters}
			{description: A list of delimiters.}}
		{parameter:
			{name: n}
			{description: The maximum number of characters to read.}}
 		{parameter:
			{name: s}
			{description: A string into which to place the data read.}}
		{return: A pointer to the string or NULL on error or EOF.  Use LmiFileAtEof to distinguish whether EOF or an error occurred.}
		{example:
			LmiString s;
 			LmiAllocator* alloc = LmiAllocatorGetDefault();
 			LmiStringConstructDefault(&s, alloc);
			LmiFileReadString(file, LMI_FILEREADOPTIONS_None, "\\n", 256, &s);
		}
	}
*/
const LmiString* LmiFileReadString(LmiFile* f, LmiFileReadOptions options, const char* delimiters, LmiSizeT n, LmiString* s);

/**
	{function:
		{name: LmiFileReadCStr}
		{parent: LmiFile}
		{description:
			Read a string from a file starting at its current read position up to either
			the first occurrence of one of the {code:delimiters} or {code:n} - 1 characters
			which ever comes first. The read position is advanced to the byte just passed the
			first occurrence of one of the {code:delimiters} (regardless of {code:n}).
		}
		{prototype: char* LmiFileReadCStr(LmiFile* f, LmiFileReadOptions options, const char* delimiters, LmiSizeT n, char* str)}
		{parameter:
			{name: f}
			{description: The file from which to read.}}
		{parameter:
			{name: options}
			{description: A set of options (bitwise-or'd together) indicating the mode in which to read the string.}}
		{parameter:
			{name: delimiters}
			{description: A list of delimiters.}}
 		{parameter:
			{name: n}
			{description: The size of the string (not the length) into which to read data, in bytes.}}
 		{parameter:
			{name: str}
			{description: A string into which to place the data read.}}
		{return: A pointer to the string or NULL on error or EOF.  Use LmiFileAtEof to distinguish whether EOF or an error occurred.}
		{example:
			char str[80];
			\/\* Mimic fgets \*\/
			LmiFileReadCStr(file, LMI_FILEREADOPTIONS_IncludeDelimiter, "\n", sizeof(str), str);
		}
	}
*/
char* LmiFileReadCStr(LmiFile* f, LmiFileReadOptions options, const char* delimiters, LmiSizeT n, char* str);

/**
	{function:
		{name: LmiFileReadToString}
		{parent: LmiFile}
		{description: Read the remainder of a file (after the current position) into a string.}
		{prototype: LmiBool LmiFileReadToString(LmiFile* f, LmiString* s)}
		{parameter:
			{name: f}
			{description: The file to read from.}}
		{parameter:
			{name: s}
			{description: The string to write to.}}
		{return: LMI_TRUE on success, LMI_FALSE on error. An error will occur if the remainder of the
			file is larger than the maximum possible string size.}
	}
*/
LmiBool LmiFileReadToString(LmiFile* f, LmiString* s);

/**
	{function:
		{name: LmiFileWrite}
		{parent: LmiFile}
		{description: Write data to a file at its current write position.}
		{prototype: LmiSizeT LmiFileWrite(LmiFile* f, const LmiUint8* buf, LmiSizeT bufSize)}
		{parameter:
			{name: f}
			{description: The file to which to write.}}
		{parameter:
			{name: buf}
			{description: The data to write to the file.}}
		{parameter:
			{name: bufSize}
			{description: The length of the data to write to the file.}}
		{return: The number of bytes actually written.  On error, this may be
			less than bufSize.}
	}
*/
LmiSizeT LmiFileWrite(LmiFile* f, const LmiUint8* buf, LmiSizeT bufSize);

/**
	{function:
		{name: LmiFileWriteCStr}
		{parent: LmiFile}
		{description: Write a C NUL-terminated string to a file at its current write position.}
		{prototype: LmiSizeT LmiFileWriteCStr(LmiFile* f, const char* str)}
		{parameter:
			{name: f}
			{description: The file to which to write.}}
		{parameter:
			{name: str}
			{description: The NUL-terminated string to write to the file.}}
		{return: The number of bytes actually written.  On error, this may be
			less than the length of str.}
	}
*/
LmiSizeT LmiFileWriteCStr(LmiFile* f, const char* str);

/**
	{function:
		{name: LmiFileWriteString}
		{parent: LmiFile}
		{description: Write a string to a file at its current write position.}
		{prototype: LmiSizeT LmiFileWriteString(LmiFile* f, const LmiString* s)}
		{parameter:
			{name: f}
			{description: The file to which to write.}}
		{parameter:
			{name: s}
			{description: The string to write to the file.}}
		{return: The number of bytes actually written.  On error, this may be
			less the size of the string.}
	}
*/
LmiSizeT LmiFileWriteString(LmiFile* f, const LmiString* s);

/**
	{function:
		{name: LmiFilePrintf}
		{parent: LmiFile}
		{description: Format and Write data to a file at its current write position.}
		{prototype: LmiInt LmiFilePrintf(LmiFile* f, const char* format, ...)}
		{parameter:
			{name: f}
			{description: The file to which to write.}}
		{parameter:
			{name: format}
			{description: A printf-style format string.}}
		{parameter:
			{name: ...}
			{description: The arguments to the format string.}}
		{return: The number of bytes actually written or -1 on error.}
	}
*/
LmiInt LmiFilePrintf(LmiFile* f, const char* format, ...)
  LmiFunctionPrintfLike(2, 3);

/**
	{function:
		{name: LmiFileVPrintf}
		{parent: LmiFile}
		{description: Format and Write data to a file at its current write position, passing a va_list for the format arguments.}
		{prototype: LmiInt LmiFileVPrintf(LmiFile* f, const char* format, va_list args)}
		{parameter:
			{name: f}
			{description: The file to which to write.}}
		{parameter:
			{name: format}
			{description: A printf-style format string.}}
		{parameter:
			{name: args}
			{description: A stdarg.h collection of arguments.}}
		{return: The number of bytes actually written or -1 on error.}
	}
*/
LmiInt LmiFileVPrintf(LmiFile* f, const char* format, va_list args)
  LmiFunctionVPrintfLike(2);

/**
	{function:
		{name: LmiFileGetPosition}
		{parent: LmiFile}
		{description: Get the current read/write position of a file.}
		{prototype: LmiFilePosition LmiFileGetPosition(LmiFile* f)}
		{parameter:
			{name: f}
			{description: The file whose position to get.}}
		{return: The current position in the file.  On error, including if the position is not representable in LmiFilePosition, this value will be negative.  Negative values will cause LmiFileSetPosition to return failure.}
	}
*/
LmiFilePosition LmiFileGetPosition(LmiFile* f);

/**
	{function:
		{name: LmiFileSetPosition}
		{parent: LmiFile}
		{description: Set the current read/write position of a file.}
		{prototype: LmiBool LmiFileSetPosition(LmiFile* f, LmiFilePosition pos)}
		{parameter:
			{name: f}
			{description: The file whose position to set.}}
		{parameter:
			{name: pos}
			{description: The position to set in the file.  The only portable values to set in this are positive values returned from LmiFileGetPosition, or 0 meaning beginning of file.}}
		{return: LMI_TRUE on success, LMI_FALSE on error.}
	}
*/
LmiBool LmiFileSetPosition(LmiFile* f, LmiFilePosition pos);

/**
	{function:
		{name: LmiFileAtEof}
		{parent: LmiFile}
		{description: Query whether a file's position is currently at the end of the file. }
		{prototype: LmiBool LmiFileAtEof(LmiFile* f)}
		{parameter:
			{name: f}
			{description: The file object to query for EOF.}}
		{return: LMI_TRUE if the file is at EOF, LMI_FALSE if not or if an error was reported.}
	}
*/
LmiBool LmiFileAtEof(LmiFile* f);

/**
	{function:
		{name: LmiFileFlush}
		{parent: LmiFile}
		{description: Flush any buffered file data out to the file's storage medium. }
		{prototype: LmiBool LmiFileFlush(LmiFile* f)}
		{parameter:
			{name: f}
			{description: The file object to flush.}}
		{return: LMI_TRUE on success, LMI_FALSE if an error was reported while flushing the file.}
	}
*/
LmiBool LmiFileFlush(LmiFile* f);

/**
	{function:
		{name: LmiFileGetSize}
		{parent: LmiFile}
		{description: Gets the size of the file. }
		{prototype: LmiUint64 LmiFileGetSize(LmiFile* f)}
		{parameter:
			{name: f}
			{description: The file object.}}
		{return: Size of the file.}
		{note: The file should be open in binary mode.}
	}
*/
LmiUint64 LmiFileGetSize(LmiFile* f);

/**
	{function:
		{name: LmiFileGetStdin}
		{parent: LmiFile}
		{description: Gets the standard input as an LmiFile object. }
		{prototype: LmiFile* LmiFileGetStdin(void)}
		{return: Returns the standard input as a pointer to an LmiFile object, or NULL if not supported.}
	}
*/
LmiFile* LmiFileGetStdin(void);

/**
	{function:
		{name: LmiFileGetStdout}
		{parent: LmiFile}
		{description: Gets the standard output as an LmiFile object. }
		{prototype: LmiFile* LmiFileGetStdout(void)}
		{return: Returns the standard output as a pointer to an LmiFile object, or NULL if not supported.}
	}
*/
LmiFile* LmiFileGetStdout(void);

/**
	{function:
		{name: LmiFileGetStderr}
		{parent: LmiFile}
		{description: Gets the standard error as an LmiFile object. }
		{prototype: LmiFile* LmiFileGetStderr(void)}
		{return: Returns the standard error as a pointer to an LmiFile object, or NULL if not supported.}
	}
*/
LmiFile* LmiFileGetStderr(void);

/**
	{function:
		{name: LmiFileRenameNoLogging}
		{parent: LmiFile}
		{description: Rename (move) a file in the file system without generating log messages.}
		{prototype: LmiBool LmiFileRenameNoLogging(LmiFile* f, const LmiPath* newPath, LmiErrorCode* errorCode)}
		{parameter:
			{name: f}
			{description: The file object.}}
		{parameter:
			{name: newPath}
			{description: A path to the desired new name and location of the file.  The syntax of this path is platform-dependent.}}
 		{parameter:
			{name: errorCode}
			{description: Returns an OS dependent error code if the operation failed.}}
		{return: Returns LMI_TRUE if the file was successfully moved or LMI_FALSE otherwise.}
		{note: In general, renaming a file within the same file system will succeed, if there is no existing file with the desired name.  (However, exceptions may occur, such as if some process has the file open exclusively.)  Moving a file across file systems, or so as to replace an existing file, is platform-dependent.}
	}
*/
LmiBool LmiFileRenameNoLogging(LmiFile* f, const LmiPath* newPath, LmiErrorCode* errorCode);

/**
	{function:
		{name: LmiFileRename}
		{parent: LmiFile}
		{description: Rename (move) a file in the file system.}
		{prototype: LmiBool LmiFileRename(LmiFile* f, const LmiPath* newPath)}
		{parameter:
			{name: f}
			{description: The file object.}}
		{parameter:
			{name: newPath}
			{description: A path to the desired new name and location of the file.  The syntax of this path is platform-dependent.}}
		{return: Returns LMI_TRUE if the file was successfully moved or LMI_FALSE otherwise.}
		{note: In general, renaming a file within the same file system will succeed, if there is no existing file with the desired name.  (However, exceptions may occur, such as if some process has the file open exclusively.)  Moving a file across file systems, or so as to replace an existing file, is platform-dependent.}
	}
*/
LmiBool LmiFileRename(LmiFile* f, const LmiPath* newPath);

/**
	{function:
		{name: LmiFileRemoveNoLogging}
		{parent: LmiFile}
		{description: Remove the file from the file system without generating log messages.}
		{prototype: LmiBool LmiFileRemoveNoLogging(LmiFile* f, LmiErrorCode* errorCode)}
 		{parameter:
			{name: f}
			{description: The file object.}}
  		{parameter:
			{name: errorCode}
			{description: Returns an OS dependent error code if the operation failed.}}
		{return: Returns LMI_TRUE if the file was successfully deleted or LMI_FALSE otherwise.}
	}
*/
LmiBool LmiFileRemoveNoLogging(LmiFile* f, LmiErrorCode* errorCode);
#define LmiFileDeleteNoLogging(f_, e_)	LmiFileRemoveNoLogging(f_, e_)		/* Deprecated */

/**
	{function:
		{name: LmiFileRemove}
		{parent: LmiFile}
		{description: Remove the file from the file system.}
		{prototype: LmiBool LmiFileRemove(LmiFile* f)}
 		{parameter:
			{name: f}
			{description: The file object.}}
		{return: Returns LMI_TRUE if the file was successfully deleted or LMI_FALSE otherwise.}
	}
*/
LmiBool LmiFileRemove(LmiFile* f);
#define LmiFileDelete(f_)	LmiFileRemove(f_)		/* Deprecated */

LmiBool LmiFileInitialize(void);
void LmiFileUninitialize(void);

typedef struct LmiAsyncDataBuffer_
{
	LmiAllocator* alloc;
	LmiAtomicInteger readPtr, writePtr;
	LmiSizeT bufferLength;
	LmiUint8* buffer;
	LmiString name;
} LmiAsyncDataBuffer;

LmiAsyncDataBuffer* LmiAsyncDataBufferConstruct(LmiAsyncDataBuffer* buffer, LmiSizeT bufferLength, const char* name, LmiAllocator* alloc);
void LmiAsyncDataBufferDestruct(LmiAsyncDataBuffer* buffer);

LmiBool LmiAsyncDataBufferWrite(LmiAsyncDataBuffer* buffer, const LmiUint8* data, LmiSizeT length);
LmiSizeT LmiAsyncDataBufferRead(LmiAsyncDataBuffer* buffer, LmiUint8* data, LmiSizeT length);
LmiBool LmiAsyncDataBufferReset(LmiAsyncDataBuffer* buffer);

typedef struct LmiAsyncFile_
{
	LmiAllocator* alloc;
	LmiAsyncDataBuffer buffer;
	LmiThread thread;
	LmiInterruptableSleeper is;
	LmiFile file;
	LmiString name;

	LmiSizeT bufferLength;
	LmiSizeT readLength;
	LmiUint8* readBuffer;
	LmiBool stopFlag;
	LmiBool isStarted;
	LmiBool inUse;

} LmiAsyncFile;

LmiAsyncFile* LmiAsyncFileConstruct(LmiAsyncFile* file, const char* name, LmiSizeT bufferLength, LmiAllocator* alloc);
void LmiAsyncFileDestruct(LmiAsyncFile* file);

LmiBool LmiAsyncFileStart(LmiAsyncFile* file);
LmiBool LmiAsyncFileStop(LmiAsyncFile* file);

LmiBool LmiAsyncFileWrite(LmiAsyncFile* file, const LmiUint8* data, LmiSizeT dataLength);
LmiBool LmiAsyncFileRewind(LmiAsyncFile* file);

typedef struct LmiAsyncDataBufferMultiple_
{
	LmiAllocator* alloc;
	LmiAtomicInteger writePtr, readPtr;
	LmiAtomicInteger* writingFlagArray;
	LmiSizeT dataLength, dataLengthPlusSizeT;
	LmiSizeT bufferLength;
	LmiUint8* buffer;
} LmiAsyncDataBufferMultiple;

LmiAsyncDataBufferMultiple* LmiAsyncDataBufferMultipleConstruct(LmiAsyncDataBufferMultiple* buffer, LmiSizeT dataLength, LmiSizeT bufferLength, LmiAllocator* alloc);
void LmiAsyncDataBufferMultipleDestruct(LmiAsyncDataBufferMultiple* buffer);

LmiBool LmiAsyncDataBufferMultipleWrite(LmiAsyncDataBufferMultiple* buffer, const LmiUint8* data, LmiSizeT length);
LmiBool LmiAsyncDataBufferMultipleRead(LmiAsyncDataBufferMultiple* buffer, LmiUint8* data, LmiSizeT* length);

typedef struct LmiAsyncFileMultiple_
{
	LmiAllocator* alloc;
	LmiAsyncDataBufferMultiple adb;
	LmiInterruptableSleeper is;
	LmiThread thread;
	LmiFile file;
	LmiUint8* tempData;

	LmiSizeT dataLength;

	LmiBool isStarted;
} LmiAsyncFileMultiple;

LmiAsyncFileMultiple* LmiAsyncFileMultipleConstruct(LmiAsyncFileMultiple* file, const char* name, LmiSizeT bufferLength, LmiSizeT dataLength, LmiAllocator* alloc);
void LmiAsyncFileMultipleDestruct(LmiAsyncFileMultiple* file);

LmiBool LmiAsyncFileMultipleStart(LmiAsyncFileMultiple* file);
void LmiAsyncFileMultipleStop(LmiAsyncFileMultiple* file);

LmiBool LmiAsyncFileMultipleWrite(LmiAsyncFileMultiple* file, const LmiUint8* data, LmiSizeT length);
LmiBool LmiAsyncFileMultipleRewind(LmiAsyncFileMultiple* file);

LMI_END_EXTERN_C

#endif /* LMI_FILE_H_ */
