# This should work with the C-ext cbor-ruby as well as with our cbor-pure
unless defined?(CBOR)
  require 'cbor-pure'
end
require 'cbor-nan'
require 'json'

class Object
  def cbor_diagnostic(_=nil)
    inspect
  end
end

class NilClass
  def cbor_diagnostic(_=nil)
    "null"
  end
end

class Float
  def cbor_diagnostic(options = {})           # do a little bit of JSON.stringify gaming (ECMA-262, 9.8.1)
    if nan?
      if options[:nan]
        pl = cbor_nan_toggle
        return "nan'#{('%a' % pl).gsub("+", "")}'"
      elsif !options[:no_nan]
        hex = to_cbor[1..-1].bytes.map{|x| "%02x" % x}.join
        return "float'#{hex}'" unless hex == "7e00"
      end
    end
    a = abs
    if a < 1 && a >= 1e-6
      inspect.sub(/(\d)[.](\d+)e-(\d+)/) {"0.#{"0" * ($3.to_i - 1)}#{$1}#{$2}"}
    else
      inspect.sub(/(e[+-])0+/) {$1}
    end
  end
end

raise unless 0.00006103515625.cbor_diagnostic == "0.00006103515625"
raise unless 0.99.cbor_diagnostic == "0.99"
raise unless 0.099.cbor_diagnostic == "0.099"
raise unless 0.0000099.cbor_diagnostic == "0.0000099"

class String
  unless String.instance_methods.include?(:b)
    def b
      dup.force_encoding(Encoding::BINARY)
    end
  end
  def hexbytes(sep = '')
    bytes.map{|x| "%02X" % x}.join(sep)
  end
  def cbor_diagnostic(options = {})
    if lengths = cbor_stream?
      pos = 0
      "(_ #{lengths.map{|l| r = self[pos, l].cbor_diagnostic(options); pos += l; r}.join(", ")})"
    else
      if encoding == Encoding::BINARY
        if options[:try_decode_embedded] && self != ''
          begin
            rest = self
            result = []
            while rest != ''
              dv, rest = CBOR.decode_with_rest(rest)
              result << dv
            end
            return "<< #{result.map{|x| x.cbor_diagnostic(options)}.join(", ")} >>"
          rescue StandardError => e
            # that didn't work out, so continue with other options
            # puts e.backtrace
          end
        end
        if options[:bytes_as_text] && (u8 = dup.force_encoding(Encoding::UTF_8)).valid_encoding?
          "'#{u8.cbor_diagnostic(bytes_as_text: true,
                                 utf8: options[:utf8]
          )[1..-2].gsub(/(')|(\\")|(\\.)|([^\\']+)/) {
          if $1
            "\\'"
          elsif $2
            "\""
          elsif $3
            $3
          elsif $4
            $4
          else
            fail
          end}}'"
        else
          "h'#{hexbytes}'"
        end
      else
        if options[:utf8]
          to_json
        else
          to_json.encode(Encoding::UTF_16BE).bytes.each_slice(2).map {
            |c1, c2| c = (c1 << 8)+c2; c < 128 ? c.chr : '\u%04x' % c }.join
        end
      end
    end
  end
end

class Array
  def cbor_diagnostic(options = {})
    "[#{"_ " if cbor_stream?}#{map {|x| x.cbor_diagnostic(options)}.join(", ")}]"
  end
end

class Hash
  def cbor_diagnostic(options = {})
    "{#{"_ " if cbor_stream?}#{map{ |k, v| %{#{k.cbor_diagnostic(options)}: #{v.cbor_diagnostic(options)}}}.join(", ")}}"
  end
end

class CBOR::Tagged
  def cbor_diagnostic(options = {})
    "#{tag}(#{value.cbor_diagnostic(options)})"
  end
end

class CBOR::Sequence
  def cbor_diagnostic(options = {})
    if elements == []
      "/ empty CBOR sequence /"
    else
      elements.map{ |el|
        el.cbor_diagnostic(options)
      }.join(", ")
    end
  end
end
