class W3DHub
  class HardwareSurvey
    attr_reader :data

    def initialize
      @data = {
        displays: [],
        system: {
          motherboard: {
            manufacturer: "Unknown",
            model: "Unknown",
            bios_vendor: "Unknown",
            bios_release_date: "Unknown",
            bios_version: "Unknown"
          },
          operating_system: {
            name: "Unknown",
            build: "Unknown",
            version: "Unknown",
            edition: "Unknown"
          },
          cpus: [],
          cpu_instruction_sets: {},
          ram: 0,
          gpus: []
        }
      }

      # Hardware survey only works on Windows atm

      if Gem::win_platform?
        lib_dir = File.dirname($LOADED_FEATURES.find { |file| file.include?("gosu.so") })
        SDL.load_lib("#{lib_dir}64/SDL2.dll")
      else
        SDL.load_lib("libSDL2")
      end

      query_displays
      query_motherboard
      query_operating_system
      query_cpus
      query_ram
      query_gpus

      @data.freeze
    end

    def query_displays
      SDL.GetNumVideoDisplays.times do |d|
        modes = []
        refresh_rates = []

        SDL.GetNumDisplayModes(d).times do |m|
          mode = SDL::DisplayMode.new
          SDL.GetDisplayMode(d, m, mode)

          refresh_rates << mode[:refresh_rate]

          modes << [mode[:w], mode[:h]]
        end

        @data[:displays] << {
          name: SDL.GetDisplayName(d).read_string,
          refresh_rates: refresh_rates.uniq.sort.reverse,
          resolutions: modes.uniq.sort.reverse
        }
      end
    end

    def query_motherboard
      return unless Gem::win_platform?

      Win32::Registry::HKEY_LOCAL_MACHINE.open("HARDWARE\\DESCRIPTION\\System\\BIOS", Win32::Registry::KEY_READ) do |reg|
        @data[:system][:motherboard][:manufacturer]      = safe_reg(reg, "SystemManufacturer")
        @data[:system][:motherboard][:model]             = safe_reg(reg, "SystemProductName")
        @data[:system][:motherboard][:bios_vendor]       = safe_reg(reg, "BIOSVendor")
        @data[:system][:motherboard][:bios_release_date] = safe_reg(reg, "BIOSReleaseDate")
        @data[:system][:motherboard][:bios_version]      = safe_reg(reg, "BIOSVersion")
      end
    rescue Win32::Registry::Error
      @data[:system][:motherboard][:manufacturer]      = "Unknown"
      @data[:system][:motherboard][:model]             = "Unknown"
      @data[:system][:motherboard][:bios_vendor]       = "Unknown"
      @data[:system][:motherboard][:bios_release_date] = "Unknown"
      @data[:system][:motherboard][:bios_version]      = "Unknown"
    end

    def query_operating_system
      return unless Gem::win_platform?

      Win32::Registry::HKEY_LOCAL_MACHINE.open("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", Win32::Registry::KEY_READ) do |reg|
        @data[:system][:operating_system][:name]    = safe_reg(reg, "ProductName")
        @data[:system][:operating_system][:build]   = safe_reg(reg, "CurrentBuild")
        @data[:system][:operating_system][:version] = safe_reg(reg, "DisplayVersion")
        @data[:system][:operating_system][:edition] = safe_reg(reg, "EditionID")
      end
    rescue Win32::Registry::Error
      @data[:system][:operating_system][:name]    = "Unknown"
      @data[:system][:operating_system][:build]   = "Unknown"
      @data[:system][:operating_system][:version] = "Unknown"
      @data[:system][:operating_system][:edition] = "Unknown"
    end

    def query_cpus
      if Gem::win_platform?
        begin
          Win32::Registry::HKEY_LOCAL_MACHINE.open("HARDWARE\\DESCRIPTION\\System\\CentralProcessor", Win32::Registry::KEY_READ) do |reg|
            i = 0

            reg.each_key do |key|
              reg.open(key) do |cpu|
                @data[:system][:cpus] << {
                  manufacturer: safe_reg(cpu, "VendorIdentifier", "Unknown"),
                  model: safe_reg(cpu, "ProcessorNameString").strip,
                  mhz: safe_reg(cpu, "~MHz"),
                  family: safe_reg(cpu, "Identifier")
                }

                i += 1
              end
            end
          end
        rescue Win32::Registry::Error
        end
      end

      instruction_sets = %w[ HasRDTSC HasAltiVec HasMMX Has3DNow HasSSE HasSSE2 HasSSE3 HasSSE41 HasSSE42 HasAVX HasAVX2 HasAVX512F HasARMSIMD HasNEON ] # HasLSX HasLASX # These cause a crash atm
      list = []
      instruction_sets.each do |i|
        if SDL.send(i).positive?
          list << i.sub("Has", "")
        end

        @data[:system][:cpu_instruction_sets][:"#{i.sub("Has", "").downcase}"] = SDL.send(i).positive?
      end
    end

    def query_ram
      @data[:system][:ram] = SDL.GetSystemRAM
    end

    def query_gpus
      return unless Gem::win_platform?

      Win32::Registry::HKEY_LOCAL_MACHINE.open("SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}", Win32::Registry::KEY_READ) do |reg|
        i = 0

        reg.each_key do |key, _|
          next unless key.start_with?("0")

          reg.open(key) do |device|
            vram = -1

            begin
              vram = device["HardwareInformation.qwMemorySize"].to_i
            rescue Win32::Registry::Error, TypeError
              begin
                vram = device["HardwareInformation.MemorySize"].to_i
              rescue Win32::Registry::Error, TypeError
                vram = -1
              end
            end

            next if vram.negative?

            vram = vram / 1024.0 / 1024.0

            @data[:system][:gpus] << {
              manufacturer: safe_reg(device, "ProviderName"),
              model: safe_reg(device, "DriverDesc"),
              vram: vram.round,
              driver_date: safe_reg(device, "DriverDate"),
              driver_version: safe_reg(device, "DriverVersion")
            }

            i += 1
          end
        end
      end
    rescue Win32::Registry::Error
    end

    def safe_reg(reg, key, default_value = "Unknown")
      reg[key]
    rescue Win32::Registry::Error
      default_value
    end
  end
end
