//**************************************************************************************************
// COM-PORT OPERATIONS — OPEN/CLOSE:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "Precompiled.h"
#include "SharedIncludes.h"
namespace XDeviceAccess {
//==================================================================================================

/// <exception cref="InternalInteropException" />
IntPtr ComPortHelpers::Open( int portNo, int baudRate,
                             int sysInputQueueSize, int sysOutputQueueSize )
{
   HDisposableSZ
      dszFileName( "COM{0}:", portNo ),
      dszDef( "baud={0} parity=N data=8 stop=1", baudRate );

   DCB dcb = {};
   dcb.DCBlength = sizeof( DCB );
   dcb.fBinary = TRUE;

   COMMTIMEOUTS tmo = {};

   if ( !::BuildCommDCBAndTimeouts( %dszDef, &dcb, &tmo ) )
      throw IntInteropExceptionCreator::CreateFromWin32Error();

   HANDLE handle = ::CreateFile( %dszFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                                 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );
   if ( handle == INVALID_HANDLE_VALUE )
      throw IntInteropExceptionCreator::CreateFromWin32Error();
   if ( !::SetupComm( handle, sysInputQueueSize, sysOutputQueueSize ) ||
        !::SetCommState( handle, &dcb ) || !::SetCommTimeouts( handle, &tmo ) )
   {
      ::CloseHandle( handle );
      throw IntInteropExceptionCreator::CreateFromWin32Error();
   }

   return (IntPtr)handle;
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InternalInteropException" />
void ComPortHelpers::Close([HOptionalThroughMinusOne][HInterlockedAccess] IntPtr% portHandle)
{
   Win32Exception ^err;
   if ( !TryClose( portHandle, err ) )
      throw IntInteropExceptionCreator::Create( err );
}

bool ComPortHelpers::TryClose( [HOptionalThroughMinusOne][HInterlockedAccess] IntPtr% portHandle,
                               [HOut] Win32Exception^ err )
{
   HANDLE hPort = ($PVOID)HPointer::InterlockedAllBitsValueExchange( portHandle );
   if ( hPort != INVALID_HANDLE_VALUE && !::CloseHandle( hPort ) )
   {
      err = HWin32Error::CreateExceptionFromLastError();
      return false;
   }
   err = nullptr;
   return true;
}

//==================================================================================================
} // XDeviceAccess
