require "ffi" unless defined?(FFI)

module Windows
  module ServiceStructs
    extend FFI::Library

    typedef :ulong, :dword

    class SERVICE_STATUS < FFI::Struct
      layout(
        :dwServiceType, :ulong,
        :dwCurrentState, :ulong,
        :dwControlsAccepted, :ulong,
        :dwWin32ExitCode, :ulong,
        :dwServiceSpecificExitCode, :ulong,
        :dwCheckPoint, :ulong,
        :dwWaitHint, :ulong
      )
    end

    class SERVICE_STATUS_PROCESS < FFI::Struct
      layout(
        :dwServiceType, :dword,
        :dwCurrentState, :dword,
        :dwControlsAccepted, :dword,
        :dwWin32ExitCode, :dword,
        :dwServiceSpecificExitCode, :dword,
        :dwCheckPoint, :dword,
        :dwWaitHint, :dword,
        :dwProcessId, :dword,
        :dwServiceFlags, :dword
      )
    end

    class SERVICE_DESCRIPTION < FFI::Struct
      layout(:lpDescription, :pointer)
    end

    class SERVICE_DELAYED_AUTO_START_INFO < FFI::Struct
      layout(:fDelayedAutostart, :int) # BOOL

      alias aset []=

      # Intercept the accessor so that we can handle either true/false or 1/0.
      # Since there is only one member, there's no need to check the key name.
      #
      def []=(key, value)
        [0, false].include?(value) ? aset(key, 0) : aset(key, 1)
      end
    end

    class QUERY_SERVICE_CONFIG < FFI::Struct
      layout(
        :dwServiceType, :dword,
        :dwStartType, :dword,
        :dwErrorControl, :dword,
        :lpBinaryPathName, :pointer,
        :lpLoadOrderGroup, :pointer,
        :dwTagId, :dword,
        :lpDependencies, :pointer,
        :lpServiceStartName, :pointer,
        :lpDisplayName, :pointer
      )

      def dependencies
        length = self[:lpServiceStartName].address - self[:lpDependencies].address - 1
        self[:lpDependencies].read_bytes(length).split(0.chr)
      end
    end

    class ENUM_SERVICE_STATUS_PROCESS < FFI::Struct
      layout(
        :lpServiceName, :pointer,
        :lpDisplayName, :pointer,
        :ServiceStatusProcess, SERVICE_STATUS_PROCESS
      )
    end

    class SC_ACTION < FFI::Struct
      layout(:Type, :int, :Delay, :dword)
    end

    class SERVICE_FAILURE_ACTIONS < FFI::Struct
      layout(
        :dwResetPeriod, :dword,
        :lpRebootMsg, :pointer,
        :lpCommand, :pointer,
        :cActions, :dword,
        :lpsaActions, :pointer # Array of SC_ACTION structs
      )
    end

    class SERVICE_TABLE_ENTRY < FFI::Struct
      layout(
        :lpServiceName, :pointer,
        :lpServiceProc, :pointer
      )
    end

    class LUID < FFI::Struct
      layout(
        :LowPart, :ulong,
        :HighPart, :long
      )
    end

    class LUID_AND_ATTRIBUTES < FFI::Struct
      layout(
        :Luid, LUID,
        :Attributes, :ulong
      )
    end

    class TOKEN_PRIVILEGES < FFI::Struct
      layout(
        :PrivilegeCount, :dword,
        :Privileges, [LUID_AND_ATTRIBUTES, 1]
      )
    end
  end
end
