require 'uri'
require 'cgi'
require 'open-uri'
require 'fileutils'

require 'rabbit/element'

module Rabbit
  module Parser
    module Ext
      module Image
        module_function
        def make_image(canvas, uri_str, prop={})
          path = Private.uri_string_to_image_filename(canvas, uri_str)
          begin
            Element::Image.new(path, prop)
          rescue Error
            canvas.logger.warn($!.message)
            nil
          end
        end

        def make_image_from_file(canvas, source)
          src_file = Tempfile.new("rabbit-image-source")
          src_file.open
          src_file.print(source)
          src_file.close
          image_file = prop = nil
          begin
            image_file, prop = yield(src_file.path)
          rescue ImageLoadError
            canvas.logger.warn($!.message)
          end
          return nil if image_file.nil?
          image = make_image(canvas, %Q[file://#{image_file.path}], prop)
          return nil if image.nil?
          image["_src"] = image_file # for protecting from GC
          image
        end

        module Private
          ALLOWED_IMG_URL_SCHEME = ['http', 'https', 'file']

          module_function
          def uri_string_to_image_filename(canvas, uri_string)
            if start_with_scheme?(uri_string)
              uri = URI.parse(uri_string)
              scheme = uri.scheme
              unless ALLOWED_IMG_URL_SCHEME.include?(scheme.to_s.downcase)
                return nil
              end
              uri_to_image_filename(canvas, uri)
            else
              local_path_to_image_filename(canvas, uri_string)
            end
          end

          def uri_to_image_filename(canvas, uri)
            case uri.scheme.to_s.downcase
            when "file"
              GLib.filename_from_utf8(uri.path)
            when "http", "https", "ftp"
              other_uri_filename(canvas, uri)
            else
              nil
            end
          end

          def local_path_to_image_filename(canvas, path)
            path = Pathname.new(GLib.filename_from_utf8(path))
            return path.to_s if path.absolute?

            expanded_path = canvas.full_path(path.to_s)
            if start_with_scheme?(expanded_path)
              uri_string_to_image_filename(canvas, expanded_path)
            else
              expanded_path
            end
          end

          def tmp_filename(canvas, key)
            dir = canvas.tmp_dir_name
            FileUtils.mkdir_p(dir)
            File.join(dir, CGI.escape(key))
          end

          def other_uri_filename(canvas, uri)
            filename = tmp_filename(canvas, uri.to_s)
            setup_image_file(canvas, uri, filename)
            filename
          end

          def setup_image_file(canvas, uri, filename)
            begin
              open(uri, "rb") do |in_file|
                File.open(filename, "wb") do |out|
                  out.print(in_file.read)
                end
              end
            rescue SocketError, OpenURI::HTTPError
              canvas.logger.warn("#{$!.message}: #{uri}")
            end
          end

          def start_with_scheme?(uri_string)
            /\A[a-z][a-z\d+\-.]+:/i =~ uri_string
          end
        end
      end
    end
  end
end
