using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Vanara.PInvoke
{
	/// <summary>Items from the cabinet.dll</summary>
	public static partial class Cabinet
	{
		public const int CB_MAX_CAB_PATH = 256;
		public const int CB_MAX_CABINET_NAME = 256;
		public const int CB_MAX_CHUNK = 32768;
		public const int CB_MAX_DISK = 0x7fffffff;
		public const int CB_MAX_DISK_NAME = 256;
		public const int CB_MAX_FILENAME = 256;

		[PInvokeData("fci.h")]
		public const int tcompSHIFT_LZX_WINDOW = 8;

		[PInvokeData("fci.h")]
		public const int tcompSHIFT_QUANTUM_LEVEL = 4;

		[PInvokeData("fci.h")]
		public const int tcompSHIFT_QUANTUM_MEM = 8;

		/// <summary>
		/// The <c>FNFCIALLOC</c> provides the declaration for the application-defined callback function to allocate memory within an FCI context.
		/// </summary>
		/// <param name="cb">Bytes to be allocated from the stack.</param>
		/// <returns>pointer to the allocated space</returns>
		/// <remarks>
		/// <para>The function accepts parameters similar to malloc.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcialloc
		[PInvokeData("fci.h", MSDNShortId = "339ac9d2-60bc-4a90-8a46-6fbb073be9d1")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate IntPtr PFNALLOC(uint cb);

		/// <summary>
		/// The <c>FNFCICLOSE</c> macro provides the declaration for the application-defined callback function to close a file in an FCI context.
		/// </summary>
		/// <param name="hf">The file handle.</param>
		/// <param name="err">The error.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>Returns 0 if the file was successfully closed. A return value of –1 indicates an error.</returns>
		/// <remarks>The function accepts parameters similar to _close, with the addition of err and pv.</remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciclose
		[PInvokeData("fci.h", MSDNShortId = "c4edf6ca-0b16-4e30-933b-934f8930c6d6")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate int PFNFCICLOSE(HFILE hf, out int err, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIDELETE</c> macro provides the declaration for the application-defined callback function to delete a file in the FCI context.
		/// </summary>
		/// <param name="pszFile">The name of the file to be deleted.</param>
		/// <param name="err">The error.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>Returns 0 if the file was successfully deleted. A return value of –1 indicates an error.</returns>
		/// <remarks>The function accepts parameters similar to remove, with the addition of err and pv.</remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcidelete
		[PInvokeData("fci.h", MSDNShortId = "5c85ad86-2794-4f7c-8c10-18fea3519b11")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate int PFNFCIDELETE(string pszFile, out int err, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIFILEPLACED</c> macro provides the declaration for the application-defined callback function to notify when a file is
		/// placed in the cabinet.
		/// </summary>
		/// <param name="pccab">Pointer to the CCAB structure containing the parameters of the cabinet on which the file has been stored.</param>
		/// <param name="pszFile">The name of the file to be replaced.</param>
		/// <param name="cbFile">The length of the file.</param>
		/// <param name="fContinuation">true if this is a later segment of a continued file</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>Returns 0 if the file was successfully replaced. A return value of –1 indicates an error.</returns>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcifileplaced
		[PInvokeData("fci.h", MSDNShortId = "f8a1bcfc-8a13-49cf-a3e7-caec6c6421b0")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate int PFNFCIFILEPLACED(ref CCAB pccab, string pszFile, int cbFile, [MarshalAs(UnmanagedType.Bool)] bool fContinuation, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIFREE</c> macro provides the declaration for the application-defined callback function to free previously allocated
		/// memory in an FCI context.
		/// </summary>
		/// <param name="memory">The pointer to the memory to be freed.</param>
		/// <remarks>
		/// <para>The function accepts parameters similar to free.</para>
		/// <para>Examples</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcifree
		[PInvokeData("fci.h", MSDNShortId = "48f052e2-7786-430a-b3dc-afcfdffae387")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate void PFNFREE(IntPtr memory);

		/// <summary>
		/// The <c>FNFCIGETNEXTCABINET</c> macro provides the declaration for the application-defined callback function to request
		/// information for the next cabinet.
		/// </summary>
		/// <param name="pccab">Points to copy of old ccab structure to modify.</param>
		/// <param name="cbPrevCab">Estimate of size of previous cabinet.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns><see langword="true"/> on success.</returns>
		/// <remarks>
		/// <para>
		/// The CCAB structure referenced by this function is relevant to the most recently completed cabinet. However, with each successful
		/// operation the iCab field contained within this structure will have incremented by 1. Additionally, the next cabinet will be
		/// created using the fields in this structure. The szCab, in particular, should be modified as necessary. In particular, the szCab
		/// field, which specifies the cabinet name, should be changed for each cabinet.
		/// </para>
		/// <para>When creating multiple cabinets, typically the iCab field is used to create the name.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcigetnextcabinet
		[PInvokeData("fci.h", MSDNShortId = "d56fb63e-91bf-4991-a954-176211697a2e")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public delegate bool PFNFCIGETNEXTCABINET(ref CCAB pccab, uint cbPrevCab, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIGETOPENINFO</c> macro provides the declaration for the application-defined callback function to open a file and
		/// retrieve file date, time, and attribute.
		/// </summary>
		/// <param name="pszName">complete path to filename.</param>
		/// <param name="pdate">location to return FAT-style date code.</param>
		/// <param name="ptime">location to return FAT-style time code.</param>
		/// <param name="pattribs">location to return FAT-style attributes.</param>
		/// <param name="err">The error.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>Return value is file handle of open file to read, or INVALID_FILE_HANDLE on failure.</returns>
		/// <remarks>The function should open the file using the file open function compatible with those passed into FCICreate.</remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcigetopeninfo
		[PInvokeData("fci.h", MSDNShortId = "5baccb69-7872-4d67-ad74-70cdd7459f8d")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate HFILE PFNFCIGETOPENINFO(string pszName, ref ushort pdate, ref ushort ptime, ref ushort pattribs, out int err, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIGETTEMPFILE</c> macro provides the declaration for the application-defined callback function to obtain a temporary
		/// file name.
		/// </summary>
		/// <param name="pszTempName">Buffer to receive complete tempfile name.</param>
		/// <param name="cbTempName">Size of pszTempName buffer.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns><see langword="true"/> on success.</returns>
		/// <remarks>
		/// The function can return a filename that already exists by the time it is opened. For this reason, the caller should be prepared
		/// to make several attempts to create temporary files.
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcigettempfile
		[PInvokeData("fci.h", MSDNShortId = "8978f688-d8f1-437a-b298-eed1e7dac012")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public delegate bool PFNFCIGETTEMPFILE(StringBuilder pszTempName, int cbTempName, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIOPEN</c> macro provides the declaration for the application-defined callback function to open a file in an FCI context.
		/// </summary>
		/// <param name="pszFile">File name.</param>
		/// <param name="oflag">The kind of operations allowed.</param>
		/// <param name="pmode">Permission mode.</param>
		/// <param name="err">The error.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>Return value is file handle of open file to read, or INVALID_FILE_HANDLE on failure.</returns>
		/// <remarks>The function accepts parameters similar to _open.</remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciopen
		[PInvokeData("fci.h", MSDNShortId = "72cf50cb-c895-4953-9c4d-f8ddaa294f2a")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate HFILE PFNFCIOPEN(string pszFile, int oflag, int pmode, out int err, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIREAD</c> macro provides the declaration for the application-defined callback function to read data from a file in an
		/// FCI context.
		/// </summary>
		/// <param name="hf">File descriptor referring to the open file.</param>
		/// <param name="memory">Storage location for data.</param>
		/// <param name="cb">Maximum number of bytes to read.</param>
		/// <param name="err">The error.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>returns the number of bytes read</returns>
		/// <remarks>
		/// <para>The function accepts parameters similar to _read with the addition to err and pv.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciread
		[PInvokeData("fci.h", MSDNShortId = "dd4e97ff-efbc-462b-b954-bc3260fa1513")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate uint PFNFCIREAD(HFILE hf, IntPtr memory, uint cb, out int err, IntPtr pv);

		/// <summary>
		/// The <c>FNFCISEEK</c> macro provides the declaration for the application-defined callback function to move a file pointer to the
		/// specified location in an FCI context.
		/// </summary>
		/// <param name="hf">File descriptor referring to an open file.</param>
		/// <param name="dist">Number of bytes from origin.</param>
		/// <param name="seektype">Initial position..</param>
		/// <param name="err">The error.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>returns the offset, in bytes, of the new position from the beginning of the file.</returns>
		/// <remarks>
		/// <para>The function accepts parameters similar to _lseek with the addition to err and pv.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciseek
		[PInvokeData("fci.h", MSDNShortId = "e5a14c98-4de6-452e-8993-afb7964aeee7")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate int PFNFCISEEK(HFILE hf, int dist, int seektype, out int err, IntPtr pv);

		/// <summary>
		/// The <c>FNFCISTATUS</c> macro provides the declaration for the application-defined callback function to update the user.
		/// </summary>
		/// <param name="typeStatus">The type status.</param>
		/// <param name="cb1">
		/// File: Size of compressed block. Folder: Amount of folder copied to cabinet so far. Cabinet: Estimated cabinet size that was
		///       previously passed to fnfciGetNextCabinet().
		/// </param>
		/// <param name="cb2">File: Size of uncompressed block. Folder: Total size of folder. Cabinet: Actual cabinet size.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>Returns -1 to signal that FCI should abort; Returns anything other than -1 on success.</returns>
		/// <remarks>
		/// If typeStatus equals <c>statusCabinet</c> the returned value indicates the desired size for the cabinet file. FCI updates the
		/// maximum cabinet size remaining using this returned value. This allows a client to generate multiple cabinets per disk, and have
		/// FCI limit the size accordingly.
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcistatus
		[PInvokeData("fci.h", MSDNShortId = "529fd3c8-9783-4dbe-9268-a9137935cf9b")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate int PFNFCISTATUS(CabinetFileStatus typeStatus, uint cb1, uint cb2, IntPtr pv);

		/// <summary>
		/// The <c>FNFCIWRITE</c> macro provides the declaration for the application-defined callback function to write data to a file in an
		/// FCI context.
		/// </summary>
		/// <param name="hf">File descriptor of file into which data is written.</param>
		/// <param name="memory">Data to be written.</param>
		/// <param name="cb">Number of bytes.</param>
		/// <param name="err">The error.</param>
		/// <param name="pv">An application-defined value.</param>
		/// <returns>returns the number of bytes actually written.</returns>
		/// <remarks>The function accepts parameters similar to _write.</remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciwrite
		[PInvokeData("fci.h", MSDNShortId = "ca4c3b5b-1ed5-4f12-8317-c1e1dac5f816")]
		[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
		public delegate uint PFNFCIWRITE(HFILE hf, IntPtr memory, uint cb, out int err, IntPtr pv);

		/// <summary>Used by <see cref="PFNFCISTATUS"/>.</summary>
		[PInvokeData("fci.h")]
		public enum CabinetFileStatus
		{
			/// <summary>compressing a block into a folder</summary>
			File,

			/// <summary>adding a folder to a cabinet</summary>
			Folder,

			/// <summary>writing out a complete cabinet</summary>
			Cabinet
		}

		[PInvokeData("fci.h")]
		public enum FCIERROR
		{
			FCIERR_NONE,
			FCIERR_OPEN_SRC,
			FCIERR_READ_SRC,
			FCIERR_ALLOC_FAIL,
			FCIERR_TEMP_FILE,
			FCIERR_BAD_COMPR_TYPE,
			FCIERR_CAB_FILE,
			FCIERR_USER_ABORT,
			FCIERR_MCI_FAIL,
			FCIERR_CAB_FORMAT_LIMIT
		}

		[PInvokeData("fci.h")]
		[Flags]
		public enum TCOMP
		{
			tcompMASK_TYPE = 0x000F,
			tcompTYPE_NONE = 0x0000,
			tcompTYPE_MSZIP = 0x0001,
			tcompTYPE_QUANTUM = 0x0002,
			tcompTYPE_LZX = 0x0003,
			tcompBAD = 0x000F,
			tcompMASK_LZX_WINDOW = 0x1F00,
			tcompLZX_WINDOW_LO = 0x0F00,
			tcompLZX_WINDOW_HI = 0x1500,
			tcompMASK_QUANTUM_LEVEL = 0x00F0,
			tcompQUANTUM_LEVEL_LO = 0x0010,
			tcompQUANTUM_LEVEL_HI = 0x0070,
			tcompMASK_QUANTUM_MEM = 0x1F00,
			tcompQUANTUM_MEM_LO = 0x0A00,
			tcompQUANTUM_MEM_HI = 0x1500,
			tcompMASK_RESERVED = 0xE000,
		}

		/// <summary>The <c>FCIAddFile</c> adds a file to the cabinet under construction.</summary>
		/// <param name="hfci">A valid FCI context handle returned by the FCICreate function.</param>
		/// <param name="pszSourceFile">The name of the file to add; this value should include path information.</param>
		/// <param name="pszFileName">The name under which to store the file in the cabinet.</param>
		/// <param name="fExecute">If set <c>TRUE</c>, the file will be executed when extracted.</param>
		/// <param name="pfnfcignc">
		/// Pointer to an application-defined callback function to obtain specifications on the next cabinet to create. The function should
		/// be declared using the FNFCIGETNEXTCABINET macro.
		/// </param>
		/// <param name="pfnfcis">
		/// Pointer to an application-defined callback function to update the progress information available to the user. The function should
		/// be declared using the FNFCISTATUS macro.
		/// </param>
		/// <param name="pfnfcigoi">
		/// Pointer to an application-defined callback function to open a file and retrieve the file date, time, and attributes. The function
		/// should be declared using the FNFCIGETOPENINFO macro.
		/// </param>
		/// <param name="typeCompress">
		/// <para>The compression type to use.</para>
		/// <para><c>Note</c> To indicate LZX compression, use the TCOMPfromLZXWindow macro.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>tcompTYPE_NONE 0x0000</term>
		/// <term>No compression.</term>
		/// </item>
		/// <item>
		/// <term>tcompTYPE_MSZIP 0x0001</term>
		/// <term>Microsoft ZIP compression.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, it returns <c>TRUE</c>; otherwise, <c>FALSE</c>.</para>
		/// <para>Extended error information is provided in the ERF structure used to create the FCI context.</para>
		/// </returns>
		/// <remarks>
		/// When set, the _A_EXEC attribute is added to the file entry in the CAB. This mechanism is used in some Microsoft self-extracting
		/// executables, and could be used for this purpose in any custom extraction application.
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fciaddfile BOOL DIAMONDAPI FCIAddFile( HFCI hfci, LPSTR
		// pszSourceFile, LPSTR pszFileName, BOOL fExecute, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis, PFNFCIGETOPENINFO
		// pfnfcigoi, TCOMP typeCompress );
		[DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)]
		[PInvokeData("fci.h", MSDNShortId = "f99e8718-853b-4d35-98ae-61a8333dbaba")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool FCIAddFile(HFCI hfci, string pszSourceFile, string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fExecute, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis, PFNFCIGETOPENINFO pfnfcigoi, TCOMP typeCompress);

		/// <summary>The <c>FCICreate</c> function creates an FCI context.</summary>
		/// <param name="perf">Pointer to an ERF structure that receives the error information.</param>
		/// <param name="pfnfcifp">
		/// Pointer to an application-defined callback function to notify when a file is placed in the cabinet. The function should be
		/// declared using the FNFCIFILEPLACED macro.
		/// </param>
		/// <param name="pfna">
		/// Pointer to an application-defined callback function to allocate memory. The function should be declared using the FNFCIALLOC macro.
		/// </param>
		/// <param name="pfnf">
		/// Pointer to an application-defined callback function to free previously allocated memory. The function should be delcared using
		/// the FNFCIFREE macro.
		/// </param>
		/// <param name="pfnopen">
		/// Pointer to an application-defined callback function to open a file. The function should be declared using the FNFCIOPEN macro.
		/// </param>
		/// <param name="pfnread">
		/// Pointer to an application-defined callback function to read data from a file. The function should be declared using the FNFCIREAD macro.
		/// </param>
		/// <param name="pfnwrite">
		/// Pointer to an application-defined callback function to write data to a file. The function should be declared using the FNFCIWRITE macro.
		/// </param>
		/// <param name="pfnclose">
		/// Pointer to an application-defined callback function to close a file. The function should be declared using the FNFCICLOSE macro.
		/// </param>
		/// <param name="pfnseek">
		/// Pointer to an application-defined callback function to move a file pointer to the specific location. The function should be
		/// declared using the FNFCISEEK macro.
		/// </param>
		/// <param name="pfndelete">
		/// Pointer to an application-defined callback function to delete a file. The function should be declared using the FNFCIDELETE macro.
		/// </param>
		/// <param name="pfnfcigtf">
		/// Pointer to an application-defined callback function to retrieve a temporary file name. The function should be declared using the
		/// FNFCIGETTEMPFILE macro.
		/// </param>
		/// <param name="pccab">Pointer to a CCAB structure that contains the parameters for creating a cabinet.</param>
		/// <param name="pv">Pointer to an application-defined value that is passed to callback functions.</param>
		/// <returns>
		/// <para>If the function succeeds, it returns a non- <c>NULL</c> HFCI context pointer; otherwise, <c>NULL</c>.</para>
		/// <para>Extended error information is provided in the ERF structure.</para>
		/// </returns>
		/// <remarks>
		/// FCI supports multiple simultaneous contexts. As a result it is possible to create or extract multiple cabinets at the same time
		/// within the same application. If the application is multithreaded, it is also possible to run a different context in each thread;
		/// however, an application cannot use the same context simultaneously in multiple threads. For example, FCIAddFile cannot be called
		/// from two different threads, using the same FCI context.
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fcicreate HFCI DIAMONDAPI FCICreate( PERF perf, PFNFCIFILEPLACED
		// pfnfcifp, PFNFCIALLOC pfna, PFNFCIFREE pfnf, PFNFCIOPEN pfnopen, PFNFCIREAD pfnread, PFNFCIWRITE pfnwrite, PFNFCICLOSE pfnclose,
		// PFNFCISEEK pfnseek, PFNFCIDELETE pfndelete, PFNFCIGETTEMPFILE pfnfcigtf, PCCAB pccab, void *pv );
		[DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)]
		[PInvokeData("fci.h", MSDNShortId = "bfcea06d-2f09-405c-955c-0f56149148f2")]
		public static extern SafeHFCI FCICreate(out ERF perf, PFNFCIFILEPLACED pfnfcifp, PFNALLOC pfna, PFNFREE pfnf, PFNFCIOPEN pfnopen, PFNFCIREAD pfnread, PFNFCIWRITE pfnwrite, PFNFCICLOSE pfnclose, PFNFCISEEK pfnseek, PFNFCIDELETE pfndelete, PFNFCIGETTEMPFILE pfnfcigtf, in CCAB pccab, IntPtr pv);

		/// <summary>
		/// The <c>FCIDestroy</c> function deletes an open FCI context, freeing any memory and temporary files associated with the context.
		/// </summary>
		/// <param name="hfci">A valid FCI context handle returned by the FCICreate function.</param>
		/// <returns>
		/// <para>If the function succeeds, it returns <c>TRUE</c>; otherwise, <c>FALSE</c>.</para>
		/// <para>Extended error information is provided in the ERF structure used to create the FCI context.</para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fcidestroy BOOL DIAMONDAPI FCIDestroy( HFCI hfci );
		[DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)]
		[PInvokeData("fci.h", MSDNShortId = "bb1a6294-664f-450f-b8ec-d6f8957d920e")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool FCIDestroy(HFCI hfci);

		/// <summary>The <c>FCIFlushCabinet</c> function completes the current cabinet.</summary>
		/// <param name="hfci">A valid FCI context handle returned by theFCICreate function.</param>
		/// <param name="fGetNextCab">Specifies whether the function pointed to by the supplied GetNextCab parameter will be called.</param>
		/// <param name="pfnfcignc">
		/// Pointer to an application-defined callback function to obtain specifications on the next cabinet to create. The function should
		/// be declared using the FNFCIGETNEXTCABINET macro.
		/// </param>
		/// <param name="pfnfcis">
		/// Pointer to an application-defined callback function to update the user. The function should be declared using the FNFCISTATUS macro.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, it returns <c>TRUE</c>; otherwise, <c>FALSE</c>.</para>
		/// <para>Extended error information is provided in the ERF structure used to create the FCI context.</para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// The <c>FCIFlushCabinet</c> API forces the current cabinet under construction to be completed immediately and then written to
		/// disk. Further calls to FCIAddFile will result in files being added to another cabinet.
		/// </para>
		/// <para>
		/// In the event the current cabinet has reached the application-specified media size limit, the pending data within an FCI's
		/// internal buffers will be placed into another cabinet.
		/// </para>
		/// <para>
		/// The fGetNextCab flag determines whether the function pointed to by the GetNextCab parameter will be called. If fGetNextCab is set
		/// <c>TRUE</c>, GetNextCab is called to obtain continuation information. If <c>FALSE</c>, then GetNextCab is called only in the
		/// event the cabinet overflows.
		/// </para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fciflushcabinet BOOL DIAMONDAPI FCIFlushCabinet( HFCI hfci, BOOL
		// fGetNextCab, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis );
		[DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)]
		[PInvokeData("fci.h", MSDNShortId = "dc586260-180e-4a6b-accf-2ddd62ac1335")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool FCIFlushCabinet(HFCI hfci, [MarshalAs(UnmanagedType.Bool)] bool fGetNextCab, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis);

		/// <summary>The <c>FCIFlushFolder</c> function forces the current folder under construction to be completed immediately.</summary>
		/// <param name="hfci">A valid FCI context handle returned by the FCICreate function.</param>
		/// <param name="pfnfcignc">
		/// Pointer to an application-defined callback function to obtain specifications on the next cabinet to create. The function should
		/// be declared using the FNFCIGETNEXTCABINET macro.
		/// </param>
		/// <param name="pfnfcis">
		/// Pointer to an application-defined callback function to update the user. The function should be declared using the FNFCISTATUS macro.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, it returns <c>TRUE</c>; otherwise, FASLE.</para>
		/// <para>Extended error information is provided in the ERF structure used to create the FCI context.</para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// The <c>FCIFlushFolder</c> API forces the folder currently under construction to be completed immediately; effectively resetting
		/// the compression history if a compression method is in use.
		/// </para>
		/// <para>
		/// The callback function indicated by GetNextCab will be called if the cabinet overflows, which occurs if the pending data buffered
		/// inside an FCI causes the application-specified cabinet media size to be exceeded.
		/// </para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fciflushfolder BOOL DIAMONDAPI FCIFlushFolder( HFCI hfci,
		// PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis );
		[DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)]
		[PInvokeData("fci.h", MSDNShortId = "dc9c226e-e309-48c3-9edb-3f0a040c0c18")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool FCIFlushFolder(HFCI hfci, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis);

		/// <summary>The <c>CCAB</c> structure contains cabinet information.</summary>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fci/ns-fci-ccab typedef struct { ULONG cb; ULONG cbFolderThresh; UINT
		// cbReserveCFHeader; UINT cbReserveCFFolder; UINT cbReserveCFData; int iCab; int iDisk; int fFailOnIncompressible; USHORT setID;
		// char szDisk[CB_MAX_DISK_NAME]; char szCab[CB_MAX_CABINET_NAME]; char szCabPath[CB_MAX_CAB_PATH]; } CCAB;
		[PInvokeData("fci.h", MSDNShortId = "e25cb72b-4c96-40e9-9fd5-2920e4a01d3a")]
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
		public struct CCAB
		{
			/// <summary>The maximum size, in bytes, of a cabinet created by FCI.</summary>
			public uint cb;

			/// <summary>The maximum size, in bytes, that a folder will contain before a new folder is created.</summary>
			public uint cbFolderThresh;

			/// <summary>The size, in bytes, of the CFHeader reserve area. Possible value range is 0-255.</summary>
			public uint cbReserveCFHeader;

			/// <summary>The size, in bytes, of the CFFolder reserve area. Possible value range is 0-255.</summary>
			public uint cbReserveCFFolder;

			/// <summary>The size, in bytes, of the CFData reserve area. Possible value range is 0-255.</summary>
			public uint cbReserveCFData;

			/// <summary>The number of created cabinets.</summary>
			public int iCab;

			/// <summary>The maximum size, in bytes, of a cabinet created by FCI.</summary>
			public int iDisk;

			/// <summary>TRUE if fail if a block is incompressible.</summary>
			public int fFailOnIncompressible;

			/// <summary>A value that represents the association between a collection of linked cabinet files.</summary>
			public ushort setID;

			/// <summary>The name of the disk on which the cabinet is placed.</summary>
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CB_MAX_DISK_NAME)]
			public string szDisk;

			/// <summary>
			/// <para>The name of the cabinet.</para>
			/// </summary>
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CB_MAX_CABINET_NAME)]
			public string szCab;

			/// <summary>
			/// <para>The full path that indicates where to create the cabinet.</para>
			/// </summary>
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CB_MAX_CAB_PATH)]
			public string szCabPath;
		}

		/// <summary>The <c>ERF</c> structure contains error information from FCI/FDI. The caller should not modify this structure.</summary>
		// https://docs.microsoft.com/en-us/windows/desktop/api/fdi_fci_types/ns-fdi_fci_types-erf typedef struct { int erfOper; int erfType;
		// BOOL fError; } ERF;
		[PInvokeData("fdi_fci_types.h", MSDNShortId = "ddbccad9-a68c-4be7-90dc-e3dd25f5cf3b")]
		public struct ERF
		{
			/// <summary>
			/// <para>An FCI/FDI error code.</para>
			/// <para>The following values are returned for FCI:</para>
			/// <list type="table">
			/// <listheader>
			/// <term>Value</term>
			/// <term>Meaning</term>
			/// </listheader>
			/// <item>
			/// <term>FCIERR_NONE 0x00</term>
			/// <term>No Error.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_OPEN_SRC 0x01</term>
			/// <term>Failure opening the file to be stored in the cabinet.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_READ_SRC 0x02</term>
			/// <term>Failure reading the file to be stored in the cabinet.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_ALLOC_FAIL 0x03</term>
			/// <term>Out of memory in FCI.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_TEMP_FILE 0x04</term>
			/// <term>Could not create a temporary file.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_BAD_COMPR_TYPE 0x05</term>
			/// <term>Unknown compression type.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_CAB_FILE 0x06</term>
			/// <term>Could not create the cabinet file.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_USER_ABORT 0x07</term>
			/// <term>FCI aborted.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_MCI_FAIL 0x08</term>
			/// <term>Failure compressing data.</term>
			/// </item>
			/// <item>
			/// <term>FCIERR_CAB_FORMAT_LIMIT 0x09</term>
			/// <term>Data-size or file-count exceeded CAB format limits.</term>
			/// </item>
			/// </list>
			/// <para>The following values are returned for FDI:</para>
			/// <list type="table">
			/// <listheader>
			/// <term>Value</term>
			/// <term>Meaning</term>
			/// </listheader>
			/// <item>
			/// <term>FDIERROR_NONE 0x00</term>
			/// <term>No error.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_CABINET_NOT_FOUND 0x01</term>
			/// <term>The cabinet file was not found.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_NOT_A_CABINET 0x02</term>
			/// <term>The cabinet file does not have the correct format.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_UNKNOWN_CABINET_VERSION 0x03</term>
			/// <term>The cabinet file has an unknown version number.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_CORRUPT_CABINET 0x04</term>
			/// <term>The cabinet file is corrupt.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_ALLOC_FAIL 0x05</term>
			/// <term>Insufficient memory.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_BAD_COMPR_TYPE 0x06</term>
			/// <term>Unknown compression type used in the cabinet folder.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_MDI_FAIL 0x07</term>
			/// <term>Failure decompressing data from the cabinet file.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_TARGET_FILE 0x08</term>
			/// <term>Failure writing to the target file.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_RESERVE_MISMATCH 0x09</term>
			/// <term>The cabinets within a set do not have the same RESERVE sizes.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_WRONG_CABINET 0x0A</term>
			/// <term>The cabinet returned by fdintNEXT_CABINET is incorrect.</term>
			/// </item>
			/// <item>
			/// <term>FDIERROR_USER_ABORT 0x0B</term>
			/// <term>FDI aborted.</term>
			/// </item>
			/// </list>
			/// </summary>
			public int erfOper;

			/// <summary>An optional error value filled in by FCI/FDI. For FCI, this is usually the C runtime errno value.</summary>
			public int erfType;

			/// <summary>A flag that indicates an error. If <c>TRUE</c>, an error is present.</summary>
			[MarshalAs(UnmanagedType.Bool)]
			public bool fError;
		}

		/// <summary>Provides a handle to a file compression interface.</summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct HFCI : IHandle
		{
			private IntPtr handle;

			/// <summary>Initializes a new instance of the <see cref="HFCI"/> struct.</summary>
			/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
			public HFCI(IntPtr preexistingHandle) => handle = preexistingHandle;

			/// <summary>Returns an invalid handle by instantiating a <see cref="HFCI"/> object with <see cref="IntPtr.Zero"/>.</summary>
			public static HFCI NULL => new HFCI(IntPtr.Zero);

			/// <summary>Gets a value indicating whether this instance is a null handle.</summary>
			public bool IsNull => handle == IntPtr.Zero;

			/// <summary>Performs an explicit conversion from <see cref="HFCI"/> to <see cref="IntPtr"/>.</summary>
			/// <param name="h">The handle.</param>
			/// <returns>The result of the conversion.</returns>
			public static explicit operator IntPtr(HFCI h) => h.handle;

			/// <summary>Performs an implicit conversion from <see cref="IntPtr"/> to <see cref="HFCI"/>.</summary>
			/// <param name="h">The pointer to a handle.</param>
			/// <returns>The result of the conversion.</returns>
			public static implicit operator HFCI(IntPtr h) => new HFCI(h);

			/// <summary>Implements the operator !=.</summary>
			/// <param name="h1">The first handle.</param>
			/// <param name="h2">The second handle.</param>
			/// <returns>The result of the operator.</returns>
			public static bool operator !=(HFCI h1, HFCI h2) => !(h1 == h2);

			/// <summary>Implements the operator ==.</summary>
			/// <param name="h1">The first handle.</param>
			/// <param name="h2">The second handle.</param>
			/// <returns>The result of the operator.</returns>
			public static bool operator ==(HFCI h1, HFCI h2) => h1.Equals(h2);

			/// <inheritdoc/>
			public override bool Equals(object obj) => obj is HFCI h ? handle == h.handle : false;

			/// <inheritdoc/>
			public override int GetHashCode() => handle.GetHashCode();

			/// <inheritdoc/>
			public IntPtr DangerousGetHandle() => handle;
		}

		/// <summary>Provides a <see cref="SafeHandle"/> for <see cref="HFCI"/> that is disposed using <see cref="FCIDestroy"/>.</summary>
		public class SafeHFCI : SafeHANDLE
		{
			/// <summary>Initializes a new instance of the <see cref="SafeHFCI"/> class and assigns an existing handle.</summary>
			/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
			/// <param name="ownsHandle">
			/// <see langword="true"/> to reliably release the handle during the finalization phase; otherwise, <see langword="false"/> (not recommended).
			/// </param>
			public SafeHFCI(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { }

			/// <summary>Initializes a new instance of the <see cref="SafeHFCI"/> class.</summary>
			private SafeHFCI() : base() { }

			/// <summary>Performs an implicit conversion from <see cref="SafeHFCI"/> to <see cref="HFCI"/>.</summary>
			/// <param name="h">The safe handle instance.</param>
			/// <returns>The result of the conversion.</returns>
			public static implicit operator HFCI(SafeHFCI h) => h.handle;

			/// <inheritdoc/>
			protected override bool InternalReleaseHandle() => FCIDestroy(this);
		}
	}
}