require 'mini_magick'

module Mork
  # Magicko: image management, done in two ways: 1) direct system calls to
  # imagemagick tools; 2) via the MiniMagick gem
  class Magicko
    def initialize(path)
      @path = path
      @cmd = []
    end

    def width
      img_size[0]
    end

    def height
      img_size[1]
    end

    # registered_bytes returns an array of the same size as the original image,
    # but with pixels stretched out based on the passed perspective points
    # (i.e. the centers of the four registration marks)
    # pp: a hash in the form of pp[:tl][:x], pp[:tl][:y], etc.
    def registered_bytes(pp)
      read_bytes "-distort Perspective '#{pps pp}'"
    end

    # def rm_patch(coord, blur_factor, dilate_factor)
    def rm_patch(c, blr=0, dlt=0)
      b = blr==0 ? '' : " -blur #{blr*3}x#{blr}"
      d = dlt==0 ? '' : " -morphology Dilate Octagon:#{dlt}"
      read_bytes "-crop #{c.cropper}#{b}#{d}"
    end

    # MiniMagick stuff

    def highlight_cells(coords)
      @cmd << [:stroke, 'none']
      @cmd << [:fill, 'rgba(255, 255, 0, 0.3)']
      coords.each do |c|
        @cmd << [:draw, "roundrectangle #{c.choice_cell}"]
      end
    end

    def outline(coords)
      @cmd << [:stroke, 'green']
      @cmd << [:strokewidth, '2']
      @cmd << [:fill, 'none']
      coords.each do |c|
        @cmd << [:draw, "roundrectangle #{c.choice_cell}"]
      end
    end

    def cross(coords)
      @cmd << [:stroke, 'red']
      @cmd << [:strokewidth, '3']
      coords.each do |c|
        @cmd << [:draw, "line #{c.cross1}"]
        @cmd << [:draw, "line #{c.cross2}"]
      end
    end

    def plus(x, y, l)
      @cmd << [:stroke, 'red']
      @cmd << [:strokewidth, 1]
      pts = [ x-l, y, x+l, y ].join ' '
      @cmd << [:draw, "line #{pts}"]
      pts = [ x, y-l, x, y+l ].join ' '
      @cmd << [:draw, "line #{pts}"]
    end

    def highlight_area(c)
      @cmd << [:fill, 'none']
      @cmd << [:stroke, 'yellow']
      @cmd << [:strokewidth, 3]
      @cmd << [:draw, "rectangle #{c.rect_points}"]
    end

    def highlight_rect(areas)
      return if areas.empty?
      @cmd << [:fill, 'none']
      @cmd << [:stroke, 'yellow']
      @cmd << [:strokewidth, 3]
      areas.each do |c|
        @cmd << [:draw, "rectangle #{c.rect_points}"]
      end
    end

    def join(p)
      @cmd << [:fill, 'none']
      @cmd << [:stroke, 'green']
      @cmd << [:strokewidth, 3]
      pts = [
        p[0][:x], p[0][:y],
        p[1][:x], p[1][:y],
        p[2][:x], p[2][:y],
        p[3][:x], p[3][:y]
      ].join ' '
      @cmd << [:draw, "polygon #{pts}"]
    end

    def write(fname, reg)
      if fname
        MiniMagick::Tool::Convert.new(whiny: false) do |img|
          img << @path
          exec_mm_cmd img, reg
          img << fname
        end
      else
        MiniMagick::Tool::Mogrify.new(whiny: false) do |img|
          img << @path
          exec_mm_cmd img, reg
        end
      end
    end

    private

    # calling imagemagick and capturing the converted image
    # into an array of bytes
    def read_bytes(params=nil)
      s = "|convert #{@path} #{params} gray:-"
      IO.read(s).unpack 'C*'
    end

    def exec_mm_cmd(c, pp)
      c.distort(:perspective, pps(pp)) if pp
      @cmd.each { |cmd| c.send(*cmd) }
    end


    # perspective points: brings the found registration area centers to the
    # original image boundaries; the result is that the registered image is
    # somewhat stretched, which should be okay
    def pps(pp)
      [
        pp[:tl][:x], pp[:tl][:y],     0,      0,
        pp[:tr][:x], pp[:tr][:y], width,      0,
        pp[:br][:x], pp[:br][:y], width, height,
        pp[:bl][:x], pp[:bl][:y],     0, height
      ].join ' '
    end

    def img_size
      @img_size ||= begin
        s = "|identify -format '%w,%h' #{@path}"
        IO.read(s).split(',').map(&:to_i)
      end
    end
  end
end

# def patch(shape: nil, wid: width, hei: height)
#   s = "|convert #{@path} #{shape} gray:-"
#   bytes = IO.read(s).unpack 'C*'
#   NPatch.new bytes, wid, hei
# end

# # raw_patch returns an array containing the pixels of the original image
# def raw_patch
#   @raw_pixels ||= patch
# end