app/models/coupdoeil/popover/option/offset.rb



# frozen_string_literal: true

module Coupdoeil
  class Popover
    class Option
      class Offset < Coupdoeil::Popover::Option
        INTEGER_PART_BITS = 8
        FLOAT_PART_BITS = 10
        CONFIG_PART_BITS = 2
        self.bit_size = INTEGER_PART_BITS + FLOAT_PART_BITS + CONFIG_PART_BITS

        class << self
          def parse(value)
            float_value = value.to_f
            return 0 if float_value.zero?

            base = 0
            base |= 1 if float_value.negative?
            base |= 2 if value.is_a?(String) && value.end_with?("rem")

            integer, decimals = float_value.abs.to_s.split(".")
            base |= (decimals.to_i << CONFIG_PART_BITS)
            base |= (integer.to_i << CONFIG_PART_BITS + FLOAT_PART_BITS)

            base
          end
        end

        def validate!
          return ensure_no_overflow if (value in Float | Integer) || value.to_s.match?(/^-?\d+(\.\d{1,3})?(px|rem)?$/)

          raise_invalid_option("Value should be a signed float or integer, followed or not by 'rem' or 'px'.")
        end

        private

        def ensure_no_overflow
          float_value = value.to_f
          integer, decimals = float_value.abs.to_s.split(".").map(&:to_i)
          return if integer.in?(0..255) && decimals.in?(0..999)

          raise_invalid_option("Number should be comprised between -255.999 and 255.999, \
with a maximum of 3 decimal digits.")
        end
      end
    end
  end
end