module Squib

  # Cache all pngs we've already loaded
  #
  # :nodoc:
  # @api private
  def cache_load_image(file)
    @img_cache ||= {}
    @img_cache[file] ||= open_png file
  end
  module_function :cache_load_image

  # Open a PNG file, checking magic bytes if it's a real PNG
  # Magic bytes taken from:
  # https://en.wikipedia.org/wiki/List_of_file_signatures
  # :nodoc:
  # @api private
  PNG_MAGIC = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
  def open_png(file)
    if PNG_MAGIC == File.read(file, 8).bytes
      return Cairo::ImageSurface.from_png(file)
    else
      raise ArgumentError.new("ERROR: #{file} is not a PNG file")
    end
  end
  module_function :open_png

  class Card

    # :nodoc:
    # @api private
    def png(file, box, paint, trans)
      Squib.logger.debug {"RENDERING PNG: \n  file: #{file}\n  box: #{box}\n  paint: #{paint}\n  trans: #{trans}"}
      return if file.nil? or file.eql? ''
      png = Squib.cache_load_image(file)
      use_cairo do |cc|
        cc.translate(box.x, box.y)
        box.width    = png.width.to_f  if box.width  == :native
        box.height   = png.height.to_f if box.height == :native
        box.width    = png.width.to_f * box.height.to_f / png.height.to_f if box.width == :scale
        box.height   = png.height.to_f * box.width.to_f / png.width.to_f  if box.height == :scale

        scale_width  = box.width.to_f / png.width.to_f
        scale_height = box.height.to_f / png.height.to_f
        warn_png_scale(file, scale_width, scale_height)
        cc.scale(scale_width, scale_height)

        cc.rotate(trans.angle)
        cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
        cc.translate(-box.x, -box.y)

        trans.crop_width  = png.width.to_f  if trans.crop_width  == :native
        trans.crop_height = png.height.to_f if trans.crop_height == :native
        cc.rounded_rectangle(box.x, box.y, trans.crop_width, trans.crop_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
        cc.clip
        cc.translate(-trans.crop_x, -trans.crop_y)


        cc.set_source(png, box.x, box.y)
        cc.operator = paint.blend unless paint.blend == :none
        if paint.mask.empty?
          cc.paint(paint.alpha)
        else
          cc.set_source_squibcolor(paint.mask)
          cc.mask(png, box.x, box.y)
        end
      end
    end

    # :nodoc:
    # @api private
    def warn_png_scale(file, scale_width, scale_height)
      if @deck.conf.warn_png_scale? && (scale_width > 1.0 || scale_height > 1.0)
        Squib.logger.warn "PNG is being upscaled - antialiasing could result: #{file}"
      end
    end

    # :nodoc:
    # @api private
    def svg(file, svg_args, box, paint, trans)
      Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
      Squib.logger.warn 'Both an SVG file and SVG data were specified' unless file.to_s.empty? || svg_args.data.to_s.empty?
      return if (file.nil? or file.eql? '') and svg_args.data.nil? # nothing specified TODO Move this out to arg validator
      svg_args.data = File.read(file) if svg_args.data.to_s.empty?
      begin
        svg = Rsvg::Handle.new_from_data(svg_args.data)
      rescue Rsvg::Error::Failed
        Squib.logger.error "Invalid SVG data. Is '#{file}' a valid svg file?"
        return
      end
      box.width    = svg.width  if box.width == :native
      box.height   = svg.height if box.height == :native
      box.width  = svg.width.to_f * box.height.to_f / svg.height.to_f if box.width == :scale
      box.height = svg.height.to_f * box.width.to_f / svg.width.to_f  if box.height == :scale
      scale_width  = box.width.to_f / svg.width.to_f
      scale_height = box.height.to_f / svg.height.to_f
      use_cairo do |cc|
        cc.translate(box.x, box.y)
        cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
        cc.rotate(trans.angle)
        cc.scale(scale_width, scale_height)

        trans.crop_width  = box.width  if trans.crop_width  == :native
        trans.crop_height = box.height if trans.crop_height == :native
        cc.rounded_rectangle(0, 0, trans.crop_width / scale_width, trans.crop_height / scale_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
        cc.clip
        cc.translate(-trans.crop_x, -trans.crop_y)

        cc.operator = paint.blend unless paint.blend == :none
        if paint.mask.to_s.empty?
          cc.render_rsvg_handle(svg, id: svg_args.id)
        else
          tmp = Cairo::ImageSurface.new(box.width / scale_width, box.height / scale_height)
          tmp_cc = Cairo::Context.new(tmp)
          tmp_cc.render_rsvg_handle(svg, id: svg_args.id)
          cc.set_source_squibcolor(paint.mask)
          cc.mask(tmp, 0, 0)
        end
      end
    end

  end
end
