# encoding: UTF-8

module Spontaneous
  module ImageSize
    def self.read(file)
      reader = ImageReader.new(file)
      reader.size
    end

    class ImageReader

      def initialize(file)
        if file.kind_of? IO
          read_size(file)
        else
          File.open(file, 'rb') { |io| read_size(io) }
        end

      end

      def read_size(file)
        klass = image_type(file)
        @image = klass.new(file)
      end

      def image_type(file)
        img_top = file.read(16)
        bytes = img_top.unpack("C*")
        file.seek(0,0)
        if img_top =~ /^GIF8[7,9]a/
          Formats::GIF
        elsif bytes[0, 8] == [137, 80, 78, 71, 13, 10, 26, 10]
          Formats::PNG
        elsif bytes[0, 2] == [255, 216]
          Formats::JPEG
        else
          Formats::OTHER
        end
      end

      def size
        @image.size
      end

      module Formats
        class Abstract
          def initialize(file)
            @file = file
            def @file.read_o(length = 1, offset = nil)
              self.seek(offset, 0) if offset
              ret = self.read(length)
              raise "cannot read!!" unless ret
              ret
            end
            # read size straight away so that if file is closed we don't raise an error
            @size = read_size
          end
          def size
            @size
          end

          def read_size
            [0,0]
          end
        end

        class GIF < Abstract
          def read_size
            @file.read_o(6)
            @file.read_o(4).unpack('vv')
          end
        end

        class JPEG < Abstract
          JPEGCodeCheck = [
            "\xc0", "\xc1", "\xc2", "\xc3",
            "\xc5", "\xc6", "\xc7",
            "\xc9", "\xca", "\xcb",
            "\xcd", "\xce", "\xcf",
          ].map { |c| c.unpack('C').first }

          def read_size
            c_marker = 255 # Section marker.

            @file.read_o(2)
            while(true)
              marker, code, length = @file.read_o(4).unpack('CCn')
              raise "JPEG marker not found!" if marker != c_marker

              if JPEGCodeCheck.include?(code)
                height, width = @file.read_o(5).unpack('xnn')
                return([width, height])
              end
              @file.read_o(length - 2)
            end
          end
        end
        class PNG < Abstract
          def read_size
            @file.read_o(12)
            raise "This file is not PNG." unless @file.read_o(4) == "IHDR"
            @file.read_o(8).unpack('NN')
          end
        end



        class OTHER < Abstract
          def read_size
            [0,0]
          end
        end
      end
    end
  end
end