/***************************************************************************
 * Copyright (c) 2009 Digi International Inc., All Rights Reserved
 * 
 * This software contains proprietary and confidential information of Digi
 * International Inc.  By accepting transfer of this copy, Recipient agrees
 * to retain this software in confidence, to prevent disclosure to others,
 * and to make no use of this software other than that for which it was
 * delivered.  This is an unpublished copyrighted work of Digi International
 * Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 * prohibited.
 * 
 * Restricted Rights Legend
 *
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 * Technical Data and Computer Software clause at DFARS 252.227-7031 or
 * subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 * Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 ***************************************************************************
 *
 * @Descr: Driver IO functions
 *
 * @History:
 *	07/17/09:	aramos      initial version.
 *	08/07/09:	aramos      remove unsafe code.
 *  08/12/09:   aramos      code revision.
 *
 ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

/*! \package Digi.Common
 */
/// <summary>
/// The Digi.Common includes the source code of common functions, constants... using in driver management
/// </summary>
namespace Digi.Common
{
    /*! \enum GenericAccessRights
     */
    /// <summary>
    /// Generic Access Rights enum
    /// The requested access to the file or device
    /// </summary>
    /// <example>
    /// <code>
    /// // Use of the constants
    /// GenericAccessRights.Read
    /// GenericAccessRights.Write
    /// GenericAccessRights.Execute
    /// GenericAccessRights.All
    /// GenericAccessRights.ReadWrite</code>
    /// </example>
    public enum GenericAccessRights : uint
    {
        Read = 0x80000000,
        Write = 0x40000000,
        Execute = 0x20000000,
        All = 0x10000000,
        ReadWrite = Read | Write
    }

    /*! \enum ShareMode
     */
    /// <summary>
    /// Share Mode enum
    /// The requested sharing mode of the file or device
    /// </summary>
    /// <example>
    /// <code>
    /// // Use of the constants
    /// ShareMode.None
    /// ShareMode.Read
    /// ShareMode.Write
    /// ShareMode.Delete
    /// ShareMode.ReadWrite</code>
    /// </example>
    public enum ShareMode : uint
    {
        None = 0x0,
        Read = 0x00000001,
        Write = 0x00000002,
        Delete = 0x00000004,
        ReadWrite = Read | Write
    }

    /*! \enum CreationDisposition
     */
    /// <summary>
    /// Creation Disposition enum
    /// An action to take on a file or device that exists or does not exist
    /// </summary>
    /// <example>
    /// <code>
    /// // Use of the constants
    /// CreationDisposition.CreateNew
    /// CreationDisposition.CreateAlways
    /// CreationDisposition.OpenExisting
    /// CreationDisposition.OpenAlways
    /// CreationDisposition.TruncateExisting
    /// CreationDisposition.OpenForLoader</code>
    /// </example>
    public enum CreationDisposition : uint
    {
        CreateNew = 1,
        CreateAlways = 2,
        OpenExisting = 3,
        OpenAlways = 4,
        TruncateExisting = 5,
        OpenForLoader = 6
    }

    /*! \enum FileAttrib
     */
    /// <summary>
    /// File Attribite enum
    /// The file or device attributes and flags
    /// </summary>
    /// <example>
    /// <code>
    /// // Use of the constants
    /// FileAttrib.None
    /// FileAttrib.Archive
    /// FileAttrib.Encrypted
    /// FileAttrib.Hidden
    /// FileAttrib.Normal
    /// FileAttrib.Offline
    /// FileAttrib.ReadOnly
    /// FileAttrib.System
    /// FileAttrib.Temporary
    /// FileAttrib.FlagBackupSemantics
    /// FileAttrib.FlagDeleteOnClose
    /// FileAttrib.FlagNoBuffering
    /// FileAttrib.FlagOpenNoRecall
    /// FileAttrib.FlagPOSIXSemantics
    /// FileAttrib.FlagOpenReparsePoint
    /// FileAttrib.FlagOverlapped
    /// FileAttrib.FlagRandomAccess
    /// FileAttrib.FlagSequentialScan
    /// FileAttrib.FlagWriteThrough</code>
    /// </example>
    public enum FileAttrib : uint
    {
        None = 0,
        Archive = 0x00000020,
        Encrypted = 0x00004000,
        Hidden = 0x00000002,
        Normal = 0x00000080,
        Offline = 0x00001000,
        ReadOnly = 0x00000001,
        System = 0x00000004,
        Temporary = 0x00000100,
        FlagBackupSemantics = 0x02000000,
        FlagDeleteOnClose = 0x04000000,
        FlagNoBuffering = 0x20000000,
        FlagOpenNoRecall = 0x00100000,
        FlagPOSIXSemantics = 0x01000000,
        FlagOpenReparsePoint = 0x00200000,
        FlagOverlapped = 0x40000000, // we don't support overlapped yet
        FlagRandomAccess = 0x10000000,
        FlagSequentialScan = 0x08000000,
        FlagWriteThrough = 0x80000000
    }

    /*! \class DriverIO
     */
    /// <summary>
    /// This class wraps the standard C/C++ CreatFile type functions into a C# class.
    /// </summary>
    public class DriverIO
    {

        private static class ImportedFunctions
        {

            /*DLLs Imported*/
            //CreateFile
            [System.Runtime.InteropServices.DllImport("coredll.dll")]
            public static extern IntPtr CreateFile(
                                            string lpFileName,
                                            uint dwDesiredAccess,
                                            uint dwShareMode,
                                            uint lpSecurityAttributes,
                                            uint dwCreationDisposition,
                                            uint dwFlagsAndAttributes,
                                            uint hTemplateFile);

            //Write
            [System.Runtime.InteropServices.DllImport("coredll.dll")]
            public static extern bool WriteFile(
                                        IntPtr hFile,
                                        byte[] lpBuffer,
                                        uint nNumberOfBytesToWrite,
                                        ref uint lpNumberOfBytesWritten,
                                        uint lpOverlapped);

            //Read
            [System.Runtime.InteropServices.DllImport("coredll.dll")]
            public static extern bool ReadFile(
                                        IntPtr hFile,
                                        byte[] lpBuffer,
                                        uint nNumberOfBytesToRead,
                                        ref uint lpNumberOfBytesRead,
                                        uint lpOverlapped);

            //DeviceIoControl
            [System.Runtime.InteropServices.DllImport("coredll.dll")]
            public static extern bool DeviceIoControl(
                                      System.IntPtr hDevice,
                                      uint dwIoControlCode,
                                      byte[] lpInBuffer,
                                      uint nInBufferSize,
                                      byte[] lpOutBuffer,
                                      uint nOutBufferSize,
                                      ref uint lpBytesReturned,
                                      uint lpOverlapped);

            //CloseHandle
            [System.Runtime.InteropServices.DllImport("coredll.dll")]
            public static extern bool CloseHandle(IntPtr hObject);

        }

        private const int InvalidHandleValue = -1;
        /// <summary>
        /// Handle to the Device or driver
        /// </summary>
        public IntPtr FileHandle = (IntPtr)InvalidHandleValue;        


        /*! \fn public bool CreateFile(string lpFileName, GenericAccessRights dwDesiredAccess,
                                    ShareMode dwShareMode,CreationDisposition dwCreationDisposition, FileAttrib dwFlagsAndAttributes)
        */
        /// <summary>
        /// This method opens a file or device.
        /// </summary>        
        /// <param name=lpFileName>Pointer to a null-terminated string that specifies the name of the object, such as file, COM port, disk device, or console, to create or open.</param>
        /// <param name=dwDesiredAccess>Type of access to the object. An application can obtain read-only access, write-only access, read/write access, or device-query access.</param>
        /// <param name=dwShareMode>Share mode for the object. If this parameter is set to zero, the object cannot be shared. Subsequent open operations on the object fail until the handle is closed.</param>
        /// <param name=dwCreationDisposition>Action to take on files that exist, and which action to take when files do not exist. The following table shows possible values.</param>
        /// <param name=dwFlagsAndAttributes>File attributes and flags for the file.</param>
        /// <returns>An open handle to the specified file indicates success.INVALID_HANDLE_VALUE indicates failure.</returns>      
        public bool CreateFile(string lpFileName, GenericAccessRights dwDesiredAccess,
                                    ShareMode dwShareMode,CreationDisposition dwCreationDisposition, FileAttrib dwFlagsAndAttributes)
        {
            FileHandle = ImportedFunctions.CreateFile(
                    lpFileName,
                    (uint)dwDesiredAccess,
                    (uint)dwShareMode,
                    0,
                    (uint)dwCreationDisposition,
                    (uint)dwFlagsAndAttributes,
                    0);

            if (FileHandle == (IntPtr)InvalidHandleValue)
                return false;
            else
                return true;
        }

        /*! \fn public int WriteFile(byte[] buffer)
        */
        /// <summary>
        /// Writes all of the buffer to the opened file or device.
        /// </summary>
        /// <param name="buffer">A byte buffer containing data to write.</param>
        /// <returns>Returns -1 if there was an error. Else it returns the number of bytes written.</returns>
        public int WriteFile(byte[] buffer)
        {
            return WriteFile(buffer, buffer.Length);
        }

        /*! \fn public int WriteFile(byte[] buffer, int BytesToWrite)
        */
        /// <summary>
        /// Writes data to the opened device.
        /// </summary>
        /// <param name="buffer">A byte buffer containing data to write</param>
        /// <param name="BytesToWrite">The number of bytes to write.</param>
        /// <returns>Returns -1 if there was an error. Else it returns the number of bytes written.</returns>
        public int WriteFile(byte[] buffer, int BytesToWrite)
        {
            bool retval;
            uint BytesWriten = 0;

            if (FileHandle == (IntPtr)InvalidHandleValue)
                return -1;

            retval = ImportedFunctions.WriteFile(FileHandle,
                                        buffer,
                                        (uint)BytesToWrite,
                                        ref BytesWriten,
                                        0);
            if (retval)
                return (int)BytesWriten;

            return -1;

        }

        /*! \fn public int ReadFile(byte[] buffer)
        */
        /// <summary>
        /// Reads data from the file or device into a buffer.
        /// </summary>
        /// <param name="buffer">The buffer to write data to.</param>
        /// <returns>Returns -1 on error, else the number of bytes read.</returns>
        public int ReadFile(byte[] buffer)
        {
            return ReadFile(buffer, buffer.Length);
        }

        /*! \fn public int ReadFile(byte[] buffer, int BytesToRead)
        */
        /// <summary>
        /// Reads data from the file or device into buffer.
        /// </summary>
        /// <param name="buffer">The buffer to write data to.</param>
        /// <param name="BytesToRead">Number of bytes to read</param>
        /// <returns>Returns -1 on error, else the number of bytes read.</returns>
        public int ReadFile(byte[] buffer, int BytesToRead)
        {
            bool retval;
            uint BytesRead = 0;

            if (FileHandle == (IntPtr)InvalidHandleValue)
                return -1;

            retval = ImportedFunctions.ReadFile(FileHandle, buffer, (uint)BytesToRead, ref BytesRead, 0);

            if (retval)
                return (int)BytesRead;

            return -1;
        }

        /*! \fn public int DeviceIOControl(uint IOControlCode, ref byte[] WriteBuffer, ref byte[] ReadBuffer)
        */
        /// <summary>
        /// Sends a IO Control to the device.
        /// </summary>
        /// <param name="IOControlCode">The IOCTL code to send</param>
        /// <param name="WriteBuffer">A buffer with data to send to the file or device.</param>
        /// <param name="ReadBuffer">A buffer to read data from the file or device.</param>
        /// <returns>Returns -1 if error, else it returns the number of bytes read.</returns>
        public int DeviceIOControl(uint IOControlCode, ref byte[] WriteBuffer, ref byte[] ReadBuffer)
        {
            return DeviceIOControl(IOControlCode, ref WriteBuffer, WriteBuffer.Length, ref ReadBuffer, ReadBuffer.Length);
        }

        /*! \fn public int DeviceIOControl(uint IOControlCode, ref byte[] WriteBuffer,
                                            int WriteBufferLength, ref byte[] ReadBuffer,
                                            int ReadBufferLength)
        */
        /// <summary>
        /// Sends a IO Control to the device
        /// </summary>
        /// <param name="IOControlCode">The IOCTL code to send</param>
        /// <param name="WriteBuffer">A buffer with data to send to the file or device.</param>
        /// <param name="WriteBufferLength">Size of the buffer to send</param>
        /// <param name="ReadBuffer">A buffer to read data from the file or device.</param>
        /// <param name="ReadBufferLength">Size of the boffer to read</param>
        /// <returns>Returns -1 if error, else it returns the number of bytes read.</returns>
        public int DeviceIOControl(
            uint IOControlCode,
            ref byte[] WriteBuffer,
            int WriteBufferLength,
            ref byte[] ReadBuffer,
            int ReadBufferLength)
        {
            bool retval;
            uint BytesRead = 0;

            if (FileHandle == (IntPtr)InvalidHandleValue)
                return -1;

            retval = ImportedFunctions.DeviceIoControl(FileHandle,
                                             IOControlCode,
                                             WriteBuffer,
                                             (uint)WriteBufferLength,
                                             ReadBuffer,
                                             (uint)ReadBufferLength,
                                             ref BytesRead,
                                             0); 
            if (retval)
                return (int)BytesRead;

            return -1;

        }

        /*! \fn public bool Close()
        */
        /// <summary>
        /// Closes the device or file.
        /// </summary>
        /// <returns>Returns true if the file is closed, false if error.</returns>
        public bool Close()
        {
            if (FileHandle == (IntPtr)InvalidHandleValue)
                return true;

            return ImportedFunctions.CloseHandle(FileHandle);

        }
    }
}