# frozen_string_literal: true

module Libptouch
  class Context
    OK = 0

    attr_reader :native

    def initialize
      @native = Binding.libptouch_create
      raise Libptouch::Error.new(0, "libptouch_create failed") if @native.null?
    end

    def last_message
      Binding.libptouch_strerror(@native)
    end

    def last_error_code
      Binding.libptouch_last_error(@native)
    end

    def raise_on_error(code)
      return if code == OK

      msg = Binding.libptouch_strerror(@native)
      raise Libptouch::Error.new(code, msg)
    end

    def open_usb
      raise_on_error(Binding.libptouch_open_usb(@native))
      self
    end

    def open_usb_vid_pid(vid, pid)
      raise_on_error(Binding.libptouch_open_usb_vid_pid(@native, vid, pid))
      self
    end

    def close
      Binding.libptouch_close(@native) if @native && !@native.null?
      self
    end

    def dispose
      close
      Binding.libptouch_destroy(@native) if @native && !@native.null?
      @native = nil
    end

    def check_raster(data, width_dots:, height_dots:, margin_mm: 0)
      params = Binding::RasterParams.new
      params[:width_dots] = width_dots
      params[:height_dots] = height_dots
      params[:margin_mm] = margin_mm
      buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
      buf.put_bytes(0, data)
      raise_on_error(Binding.libptouch_check_raster(@native, buf, data.bytesize,
                                                    params.pointer))
      self
    end

    def print_raster(data, width_dots:, height_dots:, margin_mm: 0)
      params = Binding::RasterParams.new
      params[:width_dots] = width_dots
      params[:height_dots] = height_dots
      params[:margin_mm] = margin_mm
      buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
      buf.put_bytes(0, data)
      raise_on_error(Binding.libptouch_print_raster(@native, buf, data.bytesize,
                                                    params.pointer))
      self
    end

    def png_file_to_raster(path, threshold: nil)
      opt_ptr = nil
      unless threshold.nil?
        o = Binding::PngOptions.new
        o[:threshold] = threshold
        opt_ptr = o.pointer
      end
      out_pp = FFI::MemoryPointer.new(:pointer)
      out_len = FFI::MemoryPointer.new(:size_t)
      out_params = Binding::RasterParams.new
      raise_on_error(Binding.libptouch_png_file_to_raster(
                       @native, path, opt_ptr, out_pp, out_len, out_params.pointer
                     ))
      raw = out_pp.read_pointer
      raise Libptouch::Error.new(OK, "null raster from PNG") if raw.null?

      len = out_len.read_size_t
      bytes = raw.read_bytes(len)
      Binding.libptouch_free_raster(raw)
      [bytes, out_params[:width_dots], out_params[:height_dots]]
    end

    def status_bytes
      buf = FFI::MemoryPointer.new(:uint8, STATUS_LENGTH)
      raise_on_error(Binding.libptouch_get_status(@native, buf))
      buf.read_bytes(STATUS_LENGTH)
    end

    def status_hash
      Libptouch.parse_status(status_bytes)
    end
  end
end
