# Copyright (c) 2014-2016 The Hybrid Group

module RubySerial
  module Win32
    extend FFI::Library
    ffi_lib 'kernel32'
    ffi_convention :stdcall

    GENERIC_READ  = 0x80000000
    GENERIC_WRITE = 0x40000000
    OPEN_EXISTING = 3

    ERROR_CODES = {
      5 => "ERROR_ACCESS_DENIED",
      57 => "ERROR_ADAP_HDW_ERR",
      85 => "ERROR_ALREADY_ASSIGNED",
      183 => "ERROR_ALREADY_EXISTS",
      7 => "ERROR_ARENA_TRASHED",
      174 => "ERROR_ATOMIC_LOCKS_NOT_SUPPORTED",
      199 => "ERROR_AUTODATASEG_EXCEEDS_64k",
      160 => "ERROR_BAD_ARGUMENTS",
      22 => "ERROR_BAD_COMMAND",
      66 => "ERROR_BAD_DEV_TYPE",
      119 => "ERROR_BAD_DRIVER_LEVEL",
      10 => "ERROR_BAD_ENVIRONMENT",
      193 => "ERROR_BAD_EXE_FORMAT",
      222 => "ERROR_BAD_FILE_TYPE",
      11 => "ERROR_BAD_FORMAT",
      24 => "ERROR_BAD_LENGTH",
      67 => "ERROR_BAD_NET_NAME",
      58 => "ERROR_BAD_NET_RESP",
      53 => "ERROR_BAD_NETPATH",
      161 => "ERROR_BAD_PATHNAME",
      230 => "ERROR_BAD_PIPE",
      60 => "ERROR_BAD_REM_ADAP",
      159 => "ERROR_BAD_THREADID_ADDR",
      20 => "ERROR_BAD_UNIT",
      109 => "ERROR_BROKEN_PIPE",
      111 => "ERROR_BUFFER_OVERFLOW",
      142 => "ERROR_BUSY_DRIVE",
      170 => "ERROR_BUSY",
      120 => "ERROR_CALL_NOT_IMPLEMENTED",
      173 => "ERROR_CANCEL_VIOLATION",
      266 => "ERROR_CANNOT_COPY",
      82 => "ERROR_CANNOT_MAKE",
      221 => "ERROR_CHECKOUT_REQUIRED",
      129 => "ERROR_CHILD_NOT_COMPLETE",
      23 => "ERROR_CRC",
      16 => "ERROR_CURRENT_DIRECTORY",
      303 => "ERROR_DELETE_PENDING",
      55 => "ERROR_DEV_NOT_EXIST",
      145 => "ERROR_DIR_NOT_EMPTY",
      144 => "ERROR_DIR_NOT_ROOT",
      130 => "ERROR_DIRECT_ACCESS_HANDLE",
      267 => "ERROR_DIRECTORY",
      157 => "ERROR_DISCARDED",
      107 => "ERROR_DISK_CHANGE",
      112 => "ERROR_DISK_FULL",
      302 => "ERROR_DISK_TOO_FRAGMENTED",
      108 => "ERROR_DRIVE_LOCKED",
      52 => "ERROR_DUP_NAME",
      196 => "ERROR_DYNLINK_FROM_INVALID_RING",
      276 => "ERROR_EA_FILE_CORRUPT",
      255 => "ERROR_EA_LIST_INCONSISTENT",
      277 => "ERROR_EA_TABLE_FULL",
      275 => "ERROR_EAS_DIDNT_FIT",
      282 => "ERROR_EAS_NOT_SUPPORTED",
      203 => "ERROR_ENVVAR_NOT_FOUND",
      101 => "ERROR_EXCL_SEM_ALREADY_OWNED",
      217 => "ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY",
      218 => "ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY",
      216 => "ERROR_EXE_MACHINE_TYPE_MISMATCH",
      192 => "ERROR_EXE_MARKED_INVALID",
      83 => "ERROR_FAIL_I24",
      350 => "ERROR_FAIL_NOACTION_REBOOT",
      352 => "ERROR_FAIL_RESTART",
      351 => "ERROR_FAIL_SHUTDOWN",
      220 => "ERROR_FILE_CHECKED_OUT",
      80 => "ERROR_FILE_EXISTS",
      2 => "ERROR_FILE_NOT_FOUND",
      223 => "ERROR_FILE_TOO_LARGE",
      206 => "ERROR_FILENAME_EXCED_RANGE",
      224 => "ERROR_FORMS_AUTH_REQUIRED",
      31 => "ERROR_GEN_FAILURE",
      39 => "ERROR_HANDLE_DISK_FULL",
      38 => "ERROR_HANDLE_EOF",
      308 => "ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT",
      304 => "ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING",
      202 => "ERROR_INFLOOP_IN_RELOC_CHAIN",
      122 => "ERROR_INSUFFICIENT_BUFFER",
      12 => "ERROR_INVALID_ACCESS",
      487 => "ERROR_INVALID_ADDRESS",
      104 => "ERROR_INVALID_AT_INTERRUPT_TIME",
      9 => "ERROR_INVALID_BLOCK",
      117 => "ERROR_INVALID_CATEGORY",
      13 => "ERROR_INVALID_DATA",
      15 => "ERROR_INVALID_DRIVE",
      278 => "ERROR_INVALID_EA_HANDLE",
      254 => "ERROR_INVALID_EA_NAME",
      151 => "ERROR_INVALID_EVENT_COUNT",
      191 => "ERROR_INVALID_EXE_SIGNATURE",
      186 => "ERROR_INVALID_FLAG_NUMBER",
      1 => "ERROR_INVALID_FUNCTION",
      6 => "ERROR_INVALID_HANDLE",
      124 => "ERROR_INVALID_LEVEL",
      153 => "ERROR_INVALID_LIST_FORMAT",
      307 => "ERROR_INVALID_LOCK_RANGE",
      195 => "ERROR_INVALID_MINALLOCSIZE",
      190 => "ERROR_INVALID_MODULETYPE",
      123 => "ERROR_INVALID_NAME",
      301 => "ERROR_INVALID_OPLOCK_PROTOCOL",
      182 => "ERROR_INVALID_ORDINAL",
      87 => "ERROR_INVALID_PARAMETER",
      86 => "ERROR_INVALID_PASSWORD",
      198 => "ERROR_INVALID_SEGDPL",
      180 => "ERROR_INVALID_SEGMENT_NUMBER",
      209 => "ERROR_INVALID_SIGNAL_NUMBER",
      189 => "ERROR_INVALID_STACKSEG",
      188 => "ERROR_INVALID_STARTING_CODESEG",
      114 => "ERROR_INVALID_TARGET_HANDLE",
      118 => "ERROR_INVALID_VERIFY_SWITCH",
      197 => "ERROR_IOPL_NOT_ENABLED",
      147 => "ERROR_IS_JOIN_PATH",
      133 => "ERROR_IS_JOIN_TARGET",
      134 => "ERROR_IS_JOINED",
      146 => "ERROR_IS_SUBST_PATH",
      149 => "ERROR_IS_SUBST_TARGET",
      135 => "ERROR_IS_SUBSTED",
      194 => "ERROR_ITERATED_DATA_EXCEEDS_64k",
      138 => "ERROR_JOIN_TO_JOIN",
      140 => "ERROR_JOIN_TO_SUBST",
      154 => "ERROR_LABEL_TOO_LONG",
      167 => "ERROR_LOCK_FAILED",
      33 => "ERROR_LOCK_VIOLATION",
      212 => "ERROR_LOCKED",
      353 => "ERROR_MAX_SESSIONS_REACHED",
      164 => "ERROR_MAX_THRDS_REACHED",
      208 => "ERROR_META_EXPANSION_TOO_LONG",
      126 => "ERROR_MOD_NOT_FOUND",
      234 => "ERROR_MORE_DATA",
      317 => "ERROR_MR_MID_NOT_FOUND",
      131 => "ERROR_NEGATIVE_SEEK",
      215 => "ERROR_NESTING_NOT_ALLOWED",
      88 => "ERROR_NET_WRITE_FAULT",
      64 => "ERROR_NETNAME_DELETED",
      65 => "ERROR_NETWORK_ACCESS_DENIED",
      54 => "ERROR_NETWORK_BUSY",
      232 => "ERROR_NO_DATA",
      18 => "ERROR_NO_MORE_FILES",
      259 => "ERROR_NO_MORE_ITEMS",
      113 => "ERROR_NO_MORE_SEARCH_HANDLES",
      89 => "ERROR_NO_PROC_SLOTS",
      205 => "ERROR_NO_SIGNAL_SENT",
      62 => "ERROR_NO_SPOOL_SPACE",
      125 => "ERROR_NO_VOLUME_LABEL",
      26 => "ERROR_NOT_DOS_DISK",
      8 => "ERROR_NOT_ENOUGH_MEMORY",
      136 => "ERROR_NOT_JOINED",
      158 => "ERROR_NOT_LOCKED",
      288 => "ERROR_NOT_OWNER",
      21 => "ERROR_NOT_READY",
      17 => "ERROR_NOT_SAME_DEVICE",
      137 => "ERROR_NOT_SUBSTED",
      50 => "ERROR_NOT_SUPPORTED",
      309 => "ERROR_NOTIFICATION_GUID_ALREADY_DEFINED",
      110 => "ERROR_OPEN_FAILED",
      300 => "ERROR_OPLOCK_NOT_GRANTED",
      28 => "ERROR_OUT_OF_PAPER",
      84 => "ERROR_OUT_OF_STRUCTURES",
      14 => "ERROR_OUTOFMEMORY",
      299 => "ERROR_PARTIAL_COPY",
      148 => "ERROR_PATH_BUSY",
      3 => "ERROR_PATH_NOT_FOUND",
      231 => "ERROR_PIPE_BUSY",
      229 => "ERROR_PIPE_LOCAL",
      233 => "ERROR_PIPE_NOT_CONNECTED",
      63 => "ERROR_PRINT_CANCELLED",
      61 => "ERROR_PRINTQ_FULL",
      127 => "ERROR_PROC_NOT_FOUND",
      402 => "ERROR_PROCESS_MODE_ALREADY_BACKGROUND",
      403 => "ERROR_PROCESS_MODE_NOT_BACKGROUND",
      30 => "ERROR_READ_FAULT",
      72 => "ERROR_REDIR_PAUSED",
      201 => "ERROR_RELOC_CHAIN_XEEDS_SEGLIM",
      51 => "ERROR_REM_NOT_LIST",
      71 => "ERROR_REQ_NOT_ACCEP",
      207 => "ERROR_RING2_STACK_IN_USE",
      200 => "ERROR_RING2SEG_MUST_BE_MOVABLE",
      143 => "ERROR_SAME_DRIVE",
      318 => "ERROR_SCOPE_NOT_FOUND",
      27 => "ERROR_SECTOR_NOT_FOUND",
      306 => "ERROR_SECURITY_STREAM_IS_INCONSISTENT",
      132 => "ERROR_SEEK_ON_DEVICE",
      25 => "ERROR_SEEK",
      102 => "ERROR_SEM_IS_SET",
      187 => "ERROR_SEM_NOT_FOUND",
      105 => "ERROR_SEM_OWNER_DIED",
      121 => "ERROR_SEM_TIMEOUT",
      106 => "ERROR_SEM_USER_LIMIT",
      36 => "ERROR_SHARING_BUFFER_EXCEEDED",
      70 => "ERROR_SHARING_PAUSED",
      32 => "ERROR_SHARING_VIOLATION",
      305 => "ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME",
      162 => "ERROR_SIGNAL_PENDING",
      156 => "ERROR_SIGNAL_REFUSED",
      141 => "ERROR_SUBST_TO_JOIN",
      139 => "ERROR_SUBST_TO_SUBST",
      0 => "ERROR_SUCCESS",
      150 => "ERROR_SYSTEM_TRACE",
      210 => "ERROR_THREAD_1_INACTIVE",
      400 => "ERROR_THREAD_MODE_ALREADY_BACKGROUND",
      401 => "ERROR_THREAD_MODE_NOT_BACKGROUND",
      56 => "ERROR_TOO_MANY_CMDS",
      214 => "ERROR_TOO_MANY_MODULES",
      152 => "ERROR_TOO_MANY_MUXWAITERS",
      68 => "ERROR_TOO_MANY_NAMES",
      4 => "ERROR_TOO_MANY_OPEN_FILES",
      298 => "ERROR_TOO_MANY_POSTS",
      103 => "ERROR_TOO_MANY_SEM_REQUESTS",
      100 => "ERROR_TOO_MANY_SEMAPHORES",
      69 => "ERROR_TOO_MANY_SESS",
      155 => "ERROR_TOO_MANY_TCBS",
      59 => "ERROR_UNEXP_NET_ERR",
      240 => "ERROR_VC_DISCONNECTED",
      226 => "ERROR_VIRUS_DELETED",
      225 => "ERROR_VIRUS_INFECTED",
      128 => "ERROR_WAIT_NO_CHILDREN",
      29 => "ERROR_WRITE_FAULT",
      19 => "ERROR_WRITE_PROTECT",
      34 => "ERROR_WRONG_DISK",
      258 => "WAIT_TIMEOUT"
    }

    class DCB < FFI::Struct
      layout  :dcblength,   :uint32,
              :baudrate,    :uint32,
              :flags,       :uint32,    # :flag is actually a bit fields compound:
              :wreserved,   :uint16,    #   uint32 fBinary :1;
              :xonlim,      :uint16,    #   uint32 fParity :1;
              :xofflim,     :uint16,    #   uint32 fParity :1;
              :bytesize,    :uint8,     #   uint32 fOutxCtsFlow :1;
              :parity,      :uint8,     #   uint32 fOutxDsrFlow :1;
              :stopbits,    :uint8,     #   uint32 fDtrControl :2;
              :xonchar,     :int8,      #   uint32 fDsrSensitivity :1;
              :xoffchar,    :int8,      #   uint32 fTXContinueOnXoff :1;
              :errorchar,   :int8,      #   uint32 fOutX :1;
              :eofchar,     :int8,      #   uint32 fInX :1;
              :evtchar,     :int8,      #   uint32 fErrorChar :1;
              :wreserved1,  :uint16     #   uint32 fNull :1;
                                        #   uint32 fRtsControl :2;
                                        #   uint32 fAbortOnError :1;
                                        #   uint32 fDummy2 :17;
      Sizeof      = 28
      ONESTOPBIT  = 0
      TWOSTOPBITS  = 2

      STOPBITS = {
        1 => ONESTOPBIT,
        2 => TWOSTOPBITS
      }

      NOPARITY    = 0
      ODDPARITY   = 1
      EVENPARITY  = 2
      PARITY = {
        :none => NOPARITY,
        :odd  => ODDPARITY,
        :even => EVENPARITY
      }
    end

    class CommTimeouts < FFI::Struct
      layout  :read_interval_timeout,           :uint32,
              :read_total_timeout_multiplier,   :uint32,
              :read_total_timeout_constant,     :uint32,
              :write_total_timeout_multiplier,  :uint32,
              :write_total_timeout_constant,    :uint32
    end

    attach_function :CreateFileA,     [:pointer, :uint32, :uint32, :pointer, :uint32, :uint32, :pointer], :pointer, blocking: true
    attach_function :CloseHandle,     [:pointer], :int, blocking: true
    attach_function :ReadFile,        [:pointer, :pointer, :uint32, :pointer, :pointer], :int32, blocking: true
    attach_function :WriteFile,       [:pointer, :pointer, :uint32, :pointer, :pointer], :int32, blocking: true
    attach_function :GetCommState,    [:pointer, RubySerial::Win32::DCB], :int32, blocking: true
    attach_function :SetCommState,    [:pointer, RubySerial::Win32::DCB], :int32, blocking: true
    attach_function :GetCommTimeouts, [:pointer, RubySerial::Win32::CommTimeouts], :int32, blocking: true
    attach_function :SetCommTimeouts, [:pointer, RubySerial::Win32::CommTimeouts], :int32, blocking: true
  end
end
