require "gdk_pixbuf2"

require "rabbit/utils"

module Rabbit
  module ImageManipulable

    class Base
      extend ModuleLoader

      attr_reader :width, :height, :original_width, :original_height

      def initialize(filename, props)
        @filename = filename
        @props = normalize_props(props)
        update_size
        @original_width = @width
        @original_height = @height
      end

      def [](key)
        @props[normalize_prop_key(key)]
      end

      def []=(key, value)
        @props[normalize_prop_key(key)] = value
      end

      def keep_ratio
        self["keep_ratio"]
      end

      def keep_ratio=(value)
        self["keep_ratio"] = value
      end

      def pixbuf
        @pixbuf
      end

      def resize(w, h)
        if w.nil? and h.nil?
          return
        elsif keep_ratio
          if w and h.nil?
            h = (original_height * w.to_f / original_width).ceil
          elsif w.nil? and h
            w = (original_width * h.to_f / original_height).ceil
          end
        else
          w ||= width
          h ||= height
        end
        w = w.ceil if w
        h = h.ceil if h
        if w and w > 0 and h and h > 0 and [w, h] != [width, height]
          @width = w
          @height = h
        end
      end

      def draw(canvas, x, y, params={})
        default_params = {
          :width => width,
          :height => height,
        }
        canvas.draw_pixbuf(pixbuf, x, y, default_params.merge(params))
      end

      private
      def normalize_props(props)
        normalized_props = {}
        (props || {}).each do |key, value|
          normalized_props[normalize_prop_key(key)] = value
        end
        keep_ratio_key = normalize_prop_key("keep_ratio")
        unless normalized_props.has_key?(keep_ratio_key)
          normalized_props[keep_ratio_key] = true
        end
        normalized_props
      end

      def normalize_prop_key(key)
        key.to_s.gsub(/-/, "_")
      end

      def load_by_pixbuf_loader(data)
        loader = Gdk::PixbufLoader.new
        id = loader.signal_connect("size_prepared") do |l, width, height|
          @width = width
          @height = height
        end
        begin
          loader.last_write(data)
        rescue Gdk::PixbufError
          loader.close rescue Gdk::PixbufError
          raise ImageLoadError.new("#{@filename}: #{$!.message}")
        end
        loader.signal_handler_disconnect(id)
        loader
      end
    end
  end
end
