# vim:set noet:set ts=3:set sts=3:
# vim:set shiftwidth=3:

require 'metadata-jpeg'

module JFIF

    require "nomenclature"
    class FoundThumbException < Exception;end
    class Binfile < File
        def read_word
                return self.sysread(2).unpack('n')[0]
        end
    end
    # this class is specialized in quick loading/saving a JPEG/JFIF image.
    class Image
        
        attr_reader :filename, :size, :comments, :thumbnail
        attr_reader :tags
        def initialize file, quick = false
            f = Binfile.open( file ).binmode
            
            @filename = file
            @size = File.size file
            @tags = Hash.new
            @comments = Array.new
            @image_data = nil
            
            if f.read_word != JFIF::Jpeg::JPEG_MARKERS.index('M_SOI')
                throw "Invalid File format (Start Of Image not found)"
            end
            # better find a SOS marker sometimes...
            while( true )
                btype = block_type( f.read_word )
                if btype == 'M_SOS'
                        # slurp the rest of the file
                        # 50 Mo is enough
                        bcontent = f.sysread @size - f.pos
                        bsize = bcontent.length
                else
                        bsize = f.read_word
                        bcontent = f.sysread bsize-2
                        # add the size information at beginning of the block
                        bcontent = [bsize].pack('n')+bcontent
                end
                @tags[btype] ||= Array.new
                case btype
                when "M_COM"
                        @comments << Marker::const_get(btype).new( btype, bcontent )
                when "M_APP1"
                        m = Marker::const_get(btype).new( btype, bcontent )
                        @thumbnail ||= m.thumbnail
                        @tags[btype] << m
                        
            
                when "M_SOS"
                        @image_data = Marker::Marker.new( btype, bcontent )
                else
                        @tags[btype] << Marker::Marker.new( btype, bcontent )
                end
                break if btype == 'M_SOS' || (@thumbnail!=nil && quick)
            end
            f.close
        end
		def block_type word
			return JFIF::Jpeg::JPEG_MARKERS[word] if JFIF::Jpeg::JPEG_MARKERS[word]!=nil
		end
		def save
			f = File.open( @filename,"w+" ).binmode
			f.write [0xFFD8].pack( 'n' )
			@tags.each{ |b,v|
				v.to_ary.each{ |tag| f.write tag.to_hex }
			}
			@comments.each{ |b|
				f.write b.to_hex
			}
			f.write @image_data.to_hex
			f.close
		end
	end
	# marker module, contains available markers
	module Marker
		# helper for marker creation
		def Marker.get_marker name
				return const_get(name).new(name, nil)
		end
		class Marker
			attr_reader :name, :content
			
			attr_reader :deletable
			def initialize name, content
				@name = name
				@content = content
				# you cannot delete me by default
				@deletable = false
			end
			def get_size
				return @content[0..1].unpack('n')[0]
			end
			def delete
				# ask to my containing image to remove me (if possible)
				
			end
			# does not deal with the size (it's inside th content)
			def to_hex
				output = [JFIF::Jpeg::JPEG_MARKERS.index( @name )].pack('n')+@content
				return output
			end
		end
		# com Marker
		class M_COM < Marker
			attr_accessor :text
			def initialize name, content=nil
				super
				raise Exception.new("Invalid Block#{name} given for M_COM Marker parser") if name!="M_COM" && name!=nil
				# if content this is a pre-made com marker
				if content
					@text = @content[2..get_size]
				else
					@text = nil
				end

				# get rid of this content
				@content = nil
				# you can delete me
				@deletable = true
			end
			def to_hex
				[0xFFFE,2+@text.length].pack('nn')+@text
			end
		end
	end
end
