#!ruby -Ku

require "css.tab.rb"

class CSS
	
	attr_accessor :ruleset, :media, :pages, :font_faces, :imports, :charset
	attr_reader :source
	def initialize(css)
		@source     = css.freeze
		@charset    = ""
		@imports    = []
		@font_faces = []
		@pages      = []
		@media      = []
		@ruleset    = []
		
		@yydebug   = $DEBUG
		parse
	end
	
	def to_s
		ret = ""
		ret << "@charset #{@charset};\n" if @charset
		@imports.each do |imp|
			ret << imp.to_s
		end
		@font_faces.each do |ff|
			ret << ff.to_s
		end
		@pages.each do |pg|
			ret << pg.to_s
		end
		@media.each do |medium|
			ret << medium.to_s
		end
		@ruleset.each do |rs|
			ret << rs.to_s
		end
		ret
	end
	
	private
	def on_error(t, val, vstack)
		raise ParseError, "ParseError on value #{val.inspect} in line #{@line_no}"
	end

	
	def parse
		require "strscan"
		@line_no = 1
		@error   = []

		@yydebug = $DEBUG
		@q = []
		s = StringScanner.new(@source)
		until s.empty?
			token = nil
			SCAN.each do |reg|
				token = s.scan(CSS.const_get(reg))
				if token
					token = [reg, token]
					break
				end
			end
			if s.matched?
				@q << token
			else
				chr = s.getch
				@q << [chr, chr]
			end
		end
		@q << [false, "end"]
		do_parse
	end

	def next_token
		ret = @q.shift
		while ret[0] == :COMMENT
			countup_line_no(ret[1])
			ret = @q.shift
		end
		countup_line_no(ret[1])
		ret
	end
	
	def countup_line_no(token)
		@line_no += token.scan(/\r?\n|\r/).size if token
	end
	
	
	
	class Rule < Array
		attr_accessor :selectors
		
		def initialize(selectors, declarations=[])
			@selectors = selectors.collect {|s| s.sub(/^\s*(.+?)\s*$/, "\\1")}
			self.replace(declarations)
		end
		
		def inspect
			decs = self.collect {|d| d.inspect}.join(", ")
			"#<#{self.class} `#{@selectors.join(", ")}' `#{decs}'>"
		end
		
		def to_s
			ret = @selectors.join(",\n")
			ret += " {\n"
			self.each do |dec|
				ret += dec.to_s + ";\n"
			end
			ret += "}\n"
		end
	end
	
	class FontFace < Array
		def initialize(declarations=[])
			self.replace(declarations)
		end
		
		def inspect
			decs = self.collect {|d| d.inspect}.join(", ")
			"#<#{self.class} `#{decs}'>"
		end
		
		def to_s
			ret = "@font-face {\n"
			self.each do |dec|
				ret += dec.to_s + ";\n"
			end
			ret += "}\n"
		end
	end
	
	class Media < Array
		attr_accessor :media
		
		def initialize(media, declarations=[])
			@media = media.collect {|s| s.sub(/^\s*(.+?)\s*$/, "\\1")}
			self.replace(declarations)
		end
		
		def inspect
			decs = self.collect {|d| d.inspect}.join(", ")
			"#<#{self.class} `#{@media.join(", ")}' `#{decs}'>"
		end
		
		def to_s
			ret = @media.join(", ")
			ret += " {\n"
			self.each do |dec|
				ret += dec.to_s + ";\n"
			end
			ret += "}\n"
		end
	end
	
	class Page < Array
		attr_accessor :name
		
		def initialize(name=nil, declarations=[])
			@name = name
			self.replace(declarations)
		end
		
		def inspect
			decs = self.collect {|d| d.inspect}.join(", ")
			name = @name ? "`#{@name}'" : ""
			"#<#{self.class} #{name} `#{decs}'>"
		end
		
		def to_s
			ret = "@page #{@name} {\n"
			self.each do |dec|
				ret += dec.to_s + ";\n"
			end
			ret += "}\n"
		end
	end
	
	class Declaration
		
		attr_accessor :name, :values, :important
		def initialize(name, values, important=false)
			@name, @values, @important = name, values, important
		end
		
		def inspect
			"#<#{self.class} `#{self.to_s}'>"
		end
		
		def to_s
			"#{@name}: #{@values.join(" ")}#{@important ? " !important" : ""}"
		end
		
		def important?
			@important
		end
	end
	
	class Import
		
		attr_accessor :media, :uri
		def initialize(uri, media=nil)
			@uri = uri
			@media = media
		end
		
		def inspect
			"#<#{self.class} `#{self.to_s}'>"
		end
		
		def to_s
			media = @media ? " `#{@media.join(", ")}'" : ""
			"@import #{uri}#{media};"
		end
	end
end
#p HASH
#p HASH =~ "#aaaaaaaaa"
#p Regexp.last_match[0]
#__END__



class CSS
	class Declaration
		def to_xml
			ret = ""
			important = @important ? " important=\"yes\"" : ""
			ret << "<declaration property=\"#{@name}\"#{important}>"
			@values.each do |v|
				ret << "<value>#{v}</value>"
			end
			ret << "</declaration>"
		end
	end
	
	class Rule
		def to_xml
			ret = "<ruleset>"
			@selectors.each do |s|
				ret << "<selector>#{s}</selector>"
			end
			self.each do |dec|
				ret << dec.to_xml
			end
			ret << "</ruleset>"
		end
	end
	
	class Media
		def to_xml
			ret = "<media>"
			@media.each do |medium|
				ret << "<apply>#{medium}</apply>"
			end
			self.each do |dec|
				ret << dec.to_xml
			end
			ret << "</media>"
		end
	end
	
	class FontFace
		def to_xml
			ret = "<font-face>"
			self.each do |dec|
				ret << dec.to_xml
			end
			ret << "</font-face>"
		end
	end
	
	class Page
		def to_xml
			ret = "<page name=\"#{@name}\">"
			self.each do |dec|
				ret << dec.to_xml
			end
			ret << "</page>"
		end
	end
end



require "cgi"

@cgi = CGI.new

path_info = @cgi.path_info ? @cgi.path_info : ""
path_info.gsub(/\.\./, "")


css = File.open(".#{path_info}") {|f| f.read}
#css = File.open("base.css") {|f| f.read}
gone = 0
begin
	start = Time.now
	css = CSS.new(css)
	gone = Time.now - start
rescue ParseError => e
	puts @cgi.header({"type" => "text/plain"})
	puts e.message
	exit
end
puts @cgi.header({"type" => "text/xml"})


out = "<css xmlns=\"http://lowreal.net/ns/css/\">"
out << "<charset>#{css.charset}</charset>"
css.imports.each do |imp|
	out << "<import url=\"#{imp.uri.gsub(/&/, "&amp;").gsub(/</, "&lt;").gsub(/>/, "&gt;").gsub(/"/, "&quot;")}\">"
	if imp.media
		imp.media.each do |medium|
			out << "<apply>#{medium}</apply>"
		end
	end
	out << "</import>"
end

css.font_faces.each do |ff|
	out << ff.to_xml
end

css.pages.each do |pg|
	out << pg.to_xml
end

css.ruleset.each do |rule|
	out << rule.to_xml
end

css.media.each do |medium|
	out << medium.to_xml
end

out << "<!-- #{gone} -->"
out << "</css>"

puts out
