require "rabbit/utils"

require "rabbit/image/base"

module Rabbit
  module ImageManipulable
    class EPS < Base

      unshift_loader(self)

      GS_COMMANDS = %w(gs gswin32c)

      include SystemRunner

      class << self
        def match?(filename)
          File.open(filename) do |f|
            begin
              f.each_line do |line|
                case line
                when /^%!PS-Adobe-\d+.\d+ EPS/i
                  return true
                when /^%%/
                  # ignore
                else
                  return false
                end
              end
              false
            rescue EncodingError, ArgumentError
              false
            end
          end
        end
      end

      private
      def update_size
        load_image
      end

      def load_image(width=nil, height=nil)
        data = begin
                 eps_to_png(width, height)
               rescue EPSCanNotHandleError
                 eps_to_pnm(width, height)
               end
        load_data(data)
      end

      def eps_to_png(width=nil, height=nil)
        eps_to(width, height, "pngalpha")
      end

      def eps_to_pnm(width=nil, height=nil)
        eps_to(width, height, "pnm")
      end

      def eps_to(width, height, device, *gs_options)
        x, y, w, h, r = eps_size
        resolution = (r || Canvas::INTERNAL_DPI).round
        ratio = resolution.to_f / Canvas::INTERNAL_DPI
        width = ((width || w) * ratio).round
        height = ((height || h) * ratio).round

        adjust_eps_if_need(x, y) do |path|
          tmp = Tempfile.new("rabbit-image-eps")
          args = %W(-q -dBATCH -dNOPAUSE -sDEVICE=#{device}
            -sOutputFile=#{tmp.path} -dEPSFitPage
            -dGraphicsAlphaBits=4 -dTextAlphaBits=4
            -g#{width}x#{height} -r#{resolution}x#{resolution}
            #{path})
          if GS_COMMANDS.any? {|gs| run(gs, *args)}
            begin
              tmp.open
              tmp.binmode
              tmp.read
            ensure
              tmp.close
            end
          else
            raise EPSCanNotHandleError.new("gs #{args.join(' ')}", GS_COMMANDS)
          end
        end
      end

      def eps_size
        sx, sy, w, h, r = nil
        File.read(@filename).split(/(?:\r\n?|\n)/).each do |line|
          if /^%%BoundingBox:\s*/ =~ line
            next if $POSTMATCH.chomp == '(atend)'
            sx, sy, ex, ey = $POSTMATCH.scan(/-?\d+/).map{|x| Integer(x)}
            w, h = ex - sx, ey - sy
          elsif /^%%Feature:\s*\*Resolution\s*(\d+)dpi/ =~ line
            r = $1.to_i
          end
          break if r and sx and sy and w and h
        end
        [sx, sy, w, h, r]
      end

      def adjust_eps_if_need(x, y)
        if x and y and x > 0 and y > 0
          tmp = Tempfile.new("rabbit-loader-adjusted-eps")
          tmp.puts("#{x} neg #{y} neg translate")
          tmp.print(File.open(@filename) {|f| f.read})
          tmp.close
          yield tmp.path
        else
          yield @filename
        end
      end
    end
  end
end
