$KCODE="U"

# this is a Page
#
# It is exactly equivalent to an HTML page
class Page
    
    # an Array containing the available Image
    attr_reader :images
    # an Array with available folder
    attr_reader :folders
    
    # the Folder of this page
    attr_reader :folder
    
    # the previous Page
    attr_accessor :previous_page
    # the next page
    attr_accessor :next_page
    #position among the other Page
    attr_accessor :position
    
    def initialize parent = nil
        @images = Array.new
        @folders = Array.new

        @folder = parent
    end
    # how many images in this page
    def image_count
        @images.length
    end
    def folder_count
        @folders.length
    end
    # add this Folder to the list of folders
    def add_folder folder
        @folders << folder
    end
    # add this Image to the list of images
    def add_file file
        @images << file
        file.page = self
    end
    def get_index_name
        return "index"+get_index_numbering+".html"
    end
    def get_index_numbering
        if @position == 0
            return ""
        else
            return @position.to_s
        end
    end
end

# this is an Image (and also a page)
class Image
    include Enumerable
    
    begin 
		require 'rubygems'
		require_gem 'libexif-ruby'
	rescue LoadError=>e
	end

    
    require 'jfif'
    require 'exif'
    require 'IPhotoRDF'
    
    # String variable
    attr_reader :name
    attr_reader :filename
    attr_reader :basename
    
    # Hash variable
    attr_reader :properties

    # Folder variable
    attr_reader :folder

    attr_reader :paths

    attr_accessor :page

    @@access = 0
    
    require 'yaml'
    def Image.setup_data_cache()
        if FileTest.exists?("cache.yaml")
            @@cache = YAML.load(File.open("cache.yaml",File::RDONLY))
        end
        @@cache ||= Hash.new
    end
    def Image.save_cache()
        puts "Saving Image cache"
        File.open("cache.yaml",File::RDWR|File::CREAT|File::TRUNC) do |file|
                file.write @@cache.to_yaml
        end
    end
    def Image.add_access(filename)
        @@access = @@access + 1
    end
    def Image.access
        @@access
    end
    attr_reader :desc_page
    def initialize filename, parent = nil
        @name = ""
        @folder = parent
        @filename = filename
        @basename = @filename.gsub(/.*\/([^\/]+)$/,'\1')
        @desc_page = @basename+".html"
        @properties = Hash.new
    end
    def <=>(other)
        if @properties[:dc_date] != nil && other.properties[:dc_date] != nil
            return @properties[:dc_date] <=> other.properties[:dc_date]
        else
            puts "XXXX no date found for #{@filename} or #{other.filename}"
            return @filename <=> other.filename
        end
    end

	NS_MAP = {"http://www.w3.org/2000/PhotoRDF/dc-1-0#" => 'dc_' }

    def load
        Image.add_access(@filename)
        if @@cache.has_key?(@filename) &&
            @@cache[@filename].has_key?(File.stat(@filename).mtime.to_i.to_s)
        
            @properties = @@cache[@filename][File.stat(@filename).mtime.to_i.to_s]
            # prepare new cache
            @name = @properties[:dc_title]
        else
            
            begin
                JFIF::Helper::TagRepository.instance.clean
                image = JFIF::Image.new(@filename)
                @properties[:dc_date] = JFIF::Helper::TagRepository.instance["Date"]
                @properties[:dc_date] ||= JFIF::Helper::TagRepository.instance["DateTimeDigitized"]
                
                rdf_document = RDF::Photo::Document.new_from_jfif_image(image)
                rdf_enumeration = rdf_document.ns
                NS_MAP.each{ |ns,map|
                    if rdf_enumeration.has_key?(ns)
                        rdf_enumeration[ns].each{ |rdf_statement|
                            uri = rdf_statement.predicate.to_s.split(/#/)
                            # statement.object is a node
                            content = ""
                            if rdf_statement.object.is_resource
                               content = rdf_statement.object.content
                            else
                                eval "content = "+rdf_statement.object.content
                            end
                            content.gsub!(/\\n/,"<br>")
                            symbol = map + uri[1]
                            @properties[symbol.intern]=content
                        }
                    end
                }
                @name = @properties[:dc_title]
                #rescue Exception => e
            rescue Exception =>e
                STDERR.puts e.to_s + " for #{@filename}"
            end
            puts "Got information for #{@filename}"
            @@cache[@filename] ||= Hash.new()
            @@cache[@filename][File.stat(@filename).mtime.to_i.to_s] = @properties
        end
        @name ||= @basename
        image = nil
        GC.start
    end
    RENDERS = { 'thumbs'=>[160,160,80],
    ##'small'=>[300,300,70],
            'big'=>[1024,1024,80]
        }
    require 'uri'
    def render thumbfolder = false
        @paths = Hash.new
        RENDERS.keys.each{ |folder|
            output_file = @output_folder+"/"+folder+"/"+@basename
            if thumbfolder
                @paths[folder] = URI.escape(@abs_href+"/"+folder+"/"+@basename) 
            else
                @paths[folder] = URI.escape("../"+@abs_href+"/"+folder+"/"+@basename) 
            end
            next if FileTest.exists?(output_file)
            if $USE_GTK==true
                    original_image = Gdk::Pixbuf.new(@filename)
                (new_width, new_height) = Utils::new_size(
                    original_image.width.to_f, 
                    original_image.height.to_f, 
                    RENDERS[folder][0],RENDERS[folder][1])
                if new_width > original_image.width.to_i && new_height > original_image.height.to_i 
                    new_width = original_image.width.to_i
                    new_height = original_image.height.to_i
                end
                puts "Rendering : "+output_file
                rescaled_image = original_image.scale(
                    new_width, 
                    new_height, Gdk::Pixbuf::INTERP_HYPER)
                File.mkpath(File.dirname(output_file))
                rescaled_image.save(output_file, "jpeg", {:quality => RENDERS[folder][2]})
                original_image = nil
                rescaled_image =nil
            else
                identify= `identify "#{@filename}"`
                width = /^[^ ]+ [^ ]+ ([^x]+)x([^ ]+).*/.match(identify)[1].to_i
                height = /^[^ ]+ [^ ]+ ([^x]+)x([^ ]+).*/.match(identify)[2].to_i
                (new_width, new_height) = Utils::new_size(
                    width, 
                    height, 
                    RENDERS[folder][0],RENDERS[folder][1])
                if new_width > width && new_height > height 
                    new_width = width
                    new_height = height
                end
                puts "Rendering : "+output_file
                File.mkpath(File.dirname(output_file))
                `convert #{@filename} -geometry #{new_width}x#{new_height} #{output_file}`
            
            end 

       }
         
         GC.start
    end
    def set_output_folder root_folder, folder
        @html_output_folder = folder
        @html_abs_href = @folder.path_to_top+folder
        
        @output_folder = folder.gsub(/#{root_folder}/,root_folder+"images/")
        @abs_href = @folder.path_to_top+folder.gsub(/#{root_folder}/,"images/")
        @abs_href.gsub!(/\/\//,"/")
    end
    
    def Image.image? filename
        return true if filename=~/\.jpe?g$/i
        return false
    end
    
    
    require 'ftools'
    require 'erb'
    ImageCompiler = ERB::Compiler.new('')
    ImageCompiler.put_cmd = "file.write"

    def Image.set_template_folder(folder)
        if folder[-1] != ?/
            folder = folder + "/"
        end
        @image_template = folder
        @@image_block = ImageCompiler.compile(File.open(folder+"image.rhtml").read)
    end
    def render_html
        image = self
        folder = @folder
        page = @page
        File.mkpath(@html_output_folder)
        
        File.open(@html_output_folder+@basename+".html", File::CREAT|File::RDWR|File::TRUNC) do |file|
            eval(@@image_block)
        end
    end
    
    
end

# this is a folder that contains pictures and maybe subfolders
class Folder
    if $USE_GTK == true
        require 'gtk2'
    end
    # item per page
    # TODO: put in template
    ITEM_PER_PAGE = 24
    
    # String variable
    attr_reader :name
    attr_reader :filename
    attr_reader :date
    attr_reader :path_to_top
    
    # Int variable
    attr_reader :image_count
    attr_reader :folder_count
    
   
    
    # parent Folder
    attr_reader :parent

    # pages in this Folder
    attr_reader :pages
 
    attr_reader :thumbnail
    
    include Enumerable
    def <=>(other)
        return other.date<=>@date
    end
    
    def initialize filename, parent=nil
        @name = ""
        @image_count = 0
        @folder_count = 0 
        @parent = parent
        if parent!=nil
            @path_to_top = parent.path_to_top + "../"
        else
            @path_to_top = ""
        end
        @filename = filename
        
        @pages = Array.new
        @basename = @filename.gsub(/.*\/([^\/]+)$/,'\1')
        puts "converting #{@basename}"
        if $USE_GTK == true
            @name = GLib.convert(@basename,"utf-8","iso-8859-1")
        else
            @name = @basename
        end
        
        @folders = Array.new
        @images = Array.new
       
        @thumbnail = nil
        
        puts "Checking for file #{@filename+"/"+@basename+".jpeg"}"  
        if FileTest.exists?(@filename+"/"+@basename+".jpeg")
            @thumbnail = Image.new(@filename+"/"+@basename+".jpeg",self)
            @thumbnail.load
            @name = @thumbnail.properties[:dc_title]
            @date = @thumbnail.properties[:dc_date]
        else
            puts "Thumbnail not found for folder #{@filename}"
        end
        @name ||= @basename
        @date ||= @basename
        
        # Parses the filesystem
        local_folder = Dir.new(filename)
        local_folder.entries.each{ |basename|
            fname = filename + "/" + basename
            if Image.image?(fname)
                #ignore thumbs
                next if basename == @basename + ".jpeg"
                #update the image_count
                @images << Image.new(fname,self)
            end
            if Folder.folder?(fname) && !Folder.ignored?(fname)
                puts "entering #{fname}"
                @folders << Folder.new(fname, self)
            end
        }
        @image_count = @images.length
        @folder_count = @folders.length
        

        

    end
    def Folder.set_exclude_pattern(pattern)
        @@exclude_pattern = pattern
    end
    def Folder.ignored? filename
        return true if filename=~/\/\.$/ or filename=~/\/\.\.$/
        return true if filename=~/#{@@exclude_pattern}$/i or filename=~/\.xvpics$/
        return false
    end
    def Folder.folder? filename
        return true if FileTest.directory?(filename)
        return false
    end
    def set_output_folder folder
        if folder[-1] != ?/
            folder = folder + "/"
        end
        @output_folder = folder  + "/" + @basename +"/"
        @output_folder.gsub!(/\/\//,"/")

    end
    def set_base_output_folder folder
        if folder[-1] != ?/
            folder = folder + "/"
        end
        @base_output_folder = folder
    end
    require 'uri'
    def relative_href
        URI.escape(@basename)
    end
    
    require 'ftools'
    require 'erb'
    FolderCompiler = ERB::Compiler.new('')
    FolderCompiler.put_cmd = "file.write"

    def Folder.set_template_folder(folder)
        if folder[-1] != ?/
            folder = folder + "/"
        end
        @folder_template = folder
        @@folder_block = FolderCompiler.compile(File.open(folder+"folder.rhtml").read)
    end
    
    
    # this renders a Folder
    def render
        displayed_filename = filename
        if displayed_filename.length > 40
            displayed_filename = displayed_filename[0..20]+"..."+displayed_filename[displayed_filename.length-20..-1]
        end
        puts "Rendering #{displayed_filename}"

        @folders.sort!
        @images.each do |image|
            image.load
        end
        @images.sort!.reverse!

        orig_images = @images.clone

        if @thumbnail != nil
            @thumbnail.set_output_folder(@base_output_folder, @output_folder)
            @thumbnail.render true
        end
        
        # this computes the page we have to use
        current_count = 0
        current_page = Page.new(self)
        current_page.position = 0
        @pages << current_page
        while @images.length > 0 || @folders.length > 0
            if current_count == ITEM_PER_PAGE
                current_count = 0
                old_page = current_page
                current_page = Page.new(self)
                current_page.previous_page = old_page
                old_page.next_page = current_page
                current_page.position = @pages.length
                @pages << current_page
            else
                current_count = current_count + 1
            end
            if @folders.length > 0
                current_page.add_folder(@folders.pop)
            elsif @images.length > 0
                image = @images.pop
                image.set_output_folder(@base_output_folder, @output_folder)
                image.render
                current_page.add_file(image)
            end
        end         
        orig_images.each do |image|
            image.render_html
        end
        
        File.mkpath(@output_folder)
        @pages.each { |page|

            page.folders.each{ |folder|
                folder.set_output_folder(@output_folder)
                folder.set_base_output_folder(@base_output_folder)
                folder.render
            }
            folders = page.folders
            images = page.images
            folder = self

            File.open(@output_folder+page.get_index_name,File::CREAT|File::RDWR|File::TRUNC) { |file|
                eval(@@folder_block)
            }
        }

    end
end


module Utils
	# give the new size of the image with correct ratio
	# ow, oh are the original dimensions of the picture
	# nw, nh, the maximum available
	def Utils.new_size( ow, oh, nw, nh)
		if oh*nw < ow*nh
			out_w = nw
			out_h = nw*oh/ow
		else
			out_h = nh
			out_w = ow*nh/oh
		end
		[out_w.to_i, out_h.to_i]
	end
end

