# frozen_string_literal: true

module HTTParty
  class TextEncoder
    attr_reader :text, :content_type, :assume_utf16_is_big_endian

    def initialize(text, assume_utf16_is_big_endian: true, content_type: nil)
      @text = text.dup
      @content_type = content_type
      @assume_utf16_is_big_endian = assume_utf16_is_big_endian
    end

    def call
      if can_encode?
        encoded_text
      else
        text
      end
    end

    private

    def can_encode?
      ''.respond_to?(:encoding) && charset
    end

    def encoded_text
      if 'utf-16'.casecmp(charset) == 0
        encode_utf_16
      else
        encode_with_ruby_encoding
      end
    end

    def encode_utf_16
      if text.bytesize >= 2
        if text.getbyte(0) == 0xFF && text.getbyte(1) == 0xFE
          return text.force_encoding('UTF-16LE')
        elsif text.getbyte(0) == 0xFE && text.getbyte(1) == 0xFF
          return text.force_encoding('UTF-16BE')
        end
      end

      if assume_utf16_is_big_endian # option
        text.force_encoding('UTF-16BE')
      else
        text.force_encoding('UTF-16LE')
      end
    end

    def encode_with_ruby_encoding
      # NOTE: This will raise an argument error if the
      # charset does not exist
      encoding = Encoding.find(charset)
      text.force_encoding(encoding.to_s)
    rescue ArgumentError
      text
    end

    def charset
      return nil if content_type.nil?

      if (matchdata = content_type.match(/;\s*charset\s*=\s*([^=,;"\s]+)/i))
        return matchdata.captures.first
      end

      if (matchdata = content_type.match(/;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i))
        return matchdata.captures.first.gsub(/\\(.)/, '\1')
      end
    end
  end
end
