#! /usr/bin/ruby

$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')

require 'fcgi'
require 'logger'
require 'amrita/template'
require 'xattr'
require 'digest/md5'
require 'uri'
require 'cgi/pathmap'
require 'cacher/webthumbnailer'

LOGGER = Logger.new('/tmp/log')

class String
	def urldecode
		gsub(/%([0-F]{2})/) { $1.to_i(16).chr }
	end
end

class XMLTemplateFile < Amrita::TemplateFile
	def initialize(file)
		super(file)
		self.xml = true
		self.asxml = true
		self.expand_attr = true
		self.amrita_id = 'amrita:id'
		self.use_compiler = true
	end
end

class Integer
	def as_filesize
		if self > 2**30
			("%.2f" % (self.to_f / 2**30)) << ' gb'
		elsif self > 2**20
			("%.2f" % (self.to_f / 2**20)) << ' mb'
		elsif self > 2**10
			("%.2f" % (self.to_f / 2**10)) << ' kb'
		else
			(self.to_s) << ' bytes'
		end
	end
end

class File
	def self.contentsize(filename)
		stat = File.stat(filename)
		if(stat.directory?)
			dc = 0
			fc = 0
			begin
				Dir.open(filename).each do |d|
					if(d[0] != '.'[0])
						if File.stat(File.join(filename, d)).directory? 
							dc += 1
						else
							fc += 1
						end
					end
				end
				Amrita::SanitizedString.new [  # FIXME: this escaping is too simplistic
					if dc > 0 
						"#{dc} #{if dc > 1 then "directories" else "directory" end}," 
					else 
						"" 
					end,
					"#{fc} #{if fc > 1 then "files" else "file" end}"
				].join(' ')
			rescue
				'?'
			end
		else
			stat.size.as_filesize
		end
	end

	def self.images(filename)
		stat = File.stat(filename)
		images = []
		if(stat.directory?)
			begin
				Dir.open(filename).each do |d|
					if(d[0] != '.'[0])
						if /.(jpg|jpeg|gif|png|tif|tiff|svg)$/i =~ d
							images << d
						end
					end
				end
				Amrita::SanitizedString.new [
					if images.empty? 
						"" 
					else 
						images.map { |i| 
							f = File.join(File.basename(filename), i)
							# FIXME: proper URL escaping
							"<a href='#{f}'><img src='?thumbnail=#{f}' alt='#{i}' title='#{i}' /></a>" 
						}.join(' ')
					end
				].join(' ')
			rescue
				'?'
			end
		else
			''
		end
	end
end

class FCGILet
	attr_accessor :out, :in, :docroot, :systempath, :mode, :url
	def query
		url.query
	end
	def host
		url.host
	end
	def path
		url.path.urldecode
	end
	def initialize(req)
		self.docroot = req.docroot
		self.url = req.selfurl
		self.systempath = req.path_translated
		
		self.out = req.out
		self.in = req.in
		
		self.mode = req.env['REQUEST_METHOD']
	end
end

class Ambindextrous < FCGILet
	attr_accessor :template, :edittemplate, :do_images
	def initialize(req)
		super
		LOGGER.debug("systempath = #{systempath}")
		LOGGER.debug("docroot = #{docroot}")
		if File.directory? systempath
			dir = systempath
		else
			dir = File.dirname(systempath)
		end
		paths = [dir, docroot, File.join(File.dirname(__FILE__), '..')]
		files = ['.ambindextrous.html', 'ambindextrous.html']
		templatefile = paths.map { |e| files.map { |f| File.join(e, f) } }.flatten.select { |e| LOGGER.debug("Seeking #{e}."); t = File.exists? e; if t: LOGGER.debug('found'); end; t }.first

		LOGGER.debug("templatefile = #{templatefile}")
		self.template = XMLTemplateFile.new(templatefile)
		if File.exists?(File.join(docroot, 'ambindextrous-edit.html'))
			self.edittemplate = XMLTemplateFile.new(File.join(docroot, 'ambindextrous-edit.html'))
		else
			self.edittemplate = XMLTemplateFile.new('ambindextrous-edit.html')
		end
		self.do_images = if File.read(templatefile).grep(/amrita:id=.images./): true  else false end
	end

	def run
		if(mode == 'POST')
			save_feedback
		else
			out << "Content-type: text/html\n\n"
			if(query == 'edit-feedback')
				show_edit
			else
				show_listing
			end
		end
	end

	def save_feedback
		param_data = self.in.read(self.in.size)
		param_data = param_data.split('&').map {|e| e.split('=')}
		params = Hash.new { |h,k| h[k] = param_data.assoc(k)[1] }
		if params['text']
			File.set_attr(systempath, 'feedback', params['text'].formdecode)
		end
		out << "Status: 302\nLocation: #{path}\n\n"
	end

	def show_edit
		begin
			data = { :text => File.get_attr(systempath, 'feedback') , :path => path }
		rescue Errno::EOPNOTSUPP => e
			data = { :text => '', :path => path }
		end
		edittemplate.expand out, data
	end

	def show_listing
		data = Hash.new
		data[:path] = path
		data[:images] = []
		data[:entries] = []

		begin
			a = File.get_attr systempath, 'feedback'
		rescue Errno::EOPNOTSUPP => e
			a = ''
		end

		data[:feedback] = Amrita::SanitizedString.new(a.gsub("(.*)\n", '<p>\1</p>')) if a
		begin
			a = File.get_attr systempath, 'description'
		rescue Errno::EOPNOTSUPP => e
			a = ''
		end
		data[:description] = Amrita::SanitizedString.new(a.gsub("\n", "<br />")) if a
		Dir.open(systempath).each do |e|
			begin 

				next if !File.readable? File.join(systempath, e)
				next if e[0] == ?.
				
				s = File.stat(qp = File.join(systempath, e))
				extension = ''

				if match = /([.][a-z0-9]+)$/i.match(e)
					LOGGER.debug "Chopping extension from #{e}"
					if ['html', 'png', 'jpg', 'jpeg', 'gif', 'swf', 'mp3', 'ogg'].include? match[1].downcase
						extension = match[1]
						e = e[(0..(-extension.length - 1))]
					end
				end

				LOGGER.debug e.inspect

				entry = {
					:pathurl => Amrita::SanitizedString.new(e.urlencode),
					:path => e, 
					:extension => extension,
					:description => (File.get_attr(qp, 'description') rescue ''),
					:images => (File.images(qp) rescue ''), 
					:type => if s.directory? then '/' else '' end, 
					:time => s.mtime.strftime('%e %b %y'), 
					:size => File.contentsize(qp)
				} 
				if /[.](jpg|jpeg|gif|png|svg)/i =~ e
					entry[:thumbnail] = '/global-site-overlay/ambindextrous/bin/thumbnailer/128x128' + File.join(self.url.path, e.urlencode)
					data[:images] << entry
				else
					data[:entries] << entry
				end
			rescue Exception => x
				LOGGER.debug x
			end
		end
		if !do_images 
			data[:entries] += data[:images]
		end
		data[:images].sort! { |x,y| x[:path] <=> y[:path] }
		data[:entries].sort! { |x,y| x[:path] <=> y[:path] }
		LOGGER.debug { data[:images].inspect }
		template.expand out, data
	end
end

class Thumbnailer < FCGILet
	attr_accessor :size
	def initialize(fcgi, size)
		self.size = size
		super(fcgi)
	end

	def run(image)
		cacher = WebThumbnailer.new(size)
		file = File.join(systempath, image)
		content = cacher.thumbnail(file)
		out << "Content-type: image/jpeg\n"
		out << "Content-Length: #{content.size}\n\n" 
		out << content
	end
end

FCGI.each do |fcgi|
	begin
		if /thumbnail=(.*)/ =~ fcgi.env['QUERY_STRING']
			Thumbnailer.new(fcgi, '32x24').run($1.urldecode)
		elsif /size=(.*)&resize=(.*)/ =~ fcgi.env['QUERY_STRING']
			Thumbnailer.new(fcgi, $1).run($2.urldecode)
		else
			Ambindextrous.new(fcgi).run
		end
	rescue Exception => e
		fcgi.out << "Status: 500\n"
		fcgi.out << "Content-type: text/plain\n\n"
		fcgi.out << "#{e}: #{e.backtrace.join("\n")}"
	end
	fcgi.finish
end
	
# vim: syntax=ruby
