# frozen_string_literal: true

# This file is part of RASN1
# See https://codeberg.org/lemontree55/rasn1 for more informations
# Copyright (C) 2016-2024 Sylvain Daubert <sylvain.daubert@laposte.net>
# Copyright (C) 2024 LemonTree55 <lenontree@proton.me>
# This program is published under MIT license.

module RASN1
  module Types
    # ASN.1 Object ID
    #
    # An OBJECT ID provides unique identifiers.
    #
    # Object Id are shown as numbered dotted strings, like +0.1.2.1256.1+. First number is in range [0-2], the second
    # in range [0-39]. Others are not limited.
    #
    # @example Object id
    #    oid = RASN1::Types::ObjectId.new(value: "0.2.145.65589.1")
    #    oid.to_der   #=> "\x06\a\x02\x81\x11\x84\x805\x01"
    # @author Sylvain Daubert
    # @author LemonTree55
    class ObjectId < Primitive
      # ObjectId id value
      ID = 6

      # Make object id value from +der+ string
      # @param der [String]
      # @param ber [::Boolean]
      # @return [void]
      def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
        bytes = der.unpack('C*')

        ids = []
        current_id = 0
        bytes.each do |byte|
          current_id = (current_id << 7) | (byte & 0x7f)
          if byte.nobits?(0x80)
            ids << current_id
            current_id = 0
          end
        end

        first_id = [2, ids.first / 40].min
        second_id = ids.first - first_id * 40
        ids[0..0] = [first_id, second_id]

        @value = ids.join('.')
      end

      private

      # @return [String]
      # @raise [ASN1Error]
      def value_to_der
        ids = @value.to_s.split('.').map(&:to_i)

        check_first_ids(ids)

        ids[0, 2] = ids[0] * 40 + ids[1]
        octets = []
        ids.each do |v|
          if v < 128
            octets << v
          else
            octets.concat(unsigned_to_chained_octets(v))
          end
        end
        octets.pack('C*')
      end

      # @param ids [Array<::Integer>]
      # @return [void]
      # @raise [ASN1Error] one in two first ids are out of range
      def check_first_ids(ids)
        raise ASN1Error, "OBJECT ID #{@name}: first subidentifier should be less than 3" if ids[0] > 2
        raise ASN1Error, "OBJECT ID #{@name}: second subidentifier should be less than 40" if (ids[0] < 2) && (ids[1] > 39)
      end
    end
  end
end
