require 'cgi'
require 'date'

class String
        def escapeLineBreaks
                self.gsub(/\\/,"\\s").gsub(/\n/,"\\n").gsub(/\r/,"")
        end
        def unescapeLineBreaks
                self.gsub(/\\n/,"\n").gsub(/\\s/,"\\").gsub(/\r/,"")
        end
        def encodeHTML
                CGI.escapeHTML(self)
        end
        def decodeHTML
                CGI.unescapeHTML(self)
        end
end

class Tiddler

        @@main_fields = %w[tiddler modifier modified created tags].map{|f| f.to_sym}

        @@ts = Time.now

        def self.get_time
                # so they are nicely ordered in Timeline
                @@ts += 60
                @@ts.utc.strftime("%Y%m%d%H%M%S")
        end

        @@defaults = {
                        :modified => Tiddler.get_time,
                        :created => Tiddler.get_time,
                        :modifier => 'MonkeyPirate',
                        :tags => '',
                        :tiddler => '',
        }

        def with_defaults(fields,file_name)
                @@defaults.merge({:tiddler=>name_from_file(file_name)}).merge(fields)
        end

        attr_accessor :fields, :body, :raw

        def initialize
        end

        def from_div(div)
                @raw = div
                @fields = {}
                match_data = div.match(/<div([^>]+)>([^<]+)<\/div>/)
                field_string = match_data[1]
                @body = match_data[2].unescapeLineBreaks.decodeHTML
                field_string.scan(/ (\w+)="([^"]+)"/) do |name,value|
                        @fields[name.to_sym] = value
                end
                self
        end

        def from_scratch(body,fields={})
                @fields = with_defaults(fields)
                @body = body
                @raw = ""
                self
        end

        def name_from_file(file_name)
                if (file_name)
                        file_name.split("/").last.split(".").first
                else
                        ''
                end
        end

        def from_file(file_name,fields={})
                @raw = ""
                @body = File.read(file_name)
                @body.gsub!(/\$\$date\$\$/,File.stat(file_name).mtime.strftime('%d-%b-%Y'))
                @body.gsub!(/\$\$version\$\$/,File.stat(file_name).mtime.strftime('%Y.%m.%d')[1..-1].gsub(/0/,''))
                @fields = with_defaults(fields,file_name)
                {:tiddler=>name_from_file(file_name)}.update(@fields) # guess name from file
                @fields[:modified] = Tiddler.get_time
                case File.extname(file_name)
                        when ".js"
                                @fields[:tags] = "systemConfig"
                        when ".html"
                                @fields[:tags] = "html"
                        when ".css"
                                @fields[:tags] = "css"
                end
                case File.basename(file_name)
                        when "MptwUpgradeURL.tiddler"
                                @fields[:tags] = "feed"
                end
                self
        end

        def append_content(new_content)
                @body += new_content
        end

        def to_div
                "<div"+
                        (@@main_fields.inject("") do |output,field|
                                %{#{output} #{field}="#{@fields[field]}"}
                        end)+
                        (@fields.keys.map{|f|f.to_s}.sort.reject{|f|@@main_fields.include?(f)}.inject("") do |output,field|
                                %{#{output} #{field}="#{@fields[field]}"}
                        end)+
                        ">#{@body.escapeLineBreaks.encodeHTML}</div>"
        end

end


class TiddlyWiki

        attr_accessor :orig_tiddlers, :tiddlers

        def initialize(&block)
                @tiddlers = []
                if block
                        instance_eval &block
                end
        end

        def source_file(file_name)
                @empty_file = file_name
                @raw = File.read(@empty_file)
                @orig_tiddlers = [12]#get_orig_tiddlers
        end

        @@store_regexp = /^(.*<div id="storeArea">\n?)(.*\n)(<\/div>.*)$/m

        def pre_store
                @raw.sub(@@store_regexp,'\1')
        end

        def store
                @raw.sub(@@store_regexp,'\2')
        end

        def post_store
                @raw.sub(@@store_regexp,'\3')
        end

        def tiddler_divs
                store.to_a
        end

        def get_orig_tiddlers
                tiddler_divs.inject([]) do |tiddlers,tiddler_div|
                        tiddlers << Tiddler.new.from_div(tiddler_div)
                end
        end

        def tiddler_titles
                @tiddlers.map do |t|
                        t.fields[:tiddler]
                end
        end

        def add_tiddler(tiddler)
                @tiddlers << tiddler
                tiddler
        end

        def add_tiddler_from_file(file_name)
                add_tiddler Tiddler.new.from_file("#{file_name}")
        end

        def add_tiddlers(file_names)
                file_names.reject{|f| f.match(/^#/)}.each do |f|
                        add_tiddler_from_file(f)
                end
        end

        def get_tiddler(tiddler_title)
                @tiddlers.select{|t| t.fields[:tiddler] == tiddler_title}.first
        end

        def to_s
                "#{pre_store}#{store_to_s}#{post_store}"
        end

        def store_to_s
                @tiddlers.sort_by{|t| t.fields[:tiddler]}.inject(""){ |out,t|out << t.to_div << "\n"}
        end

        def store_to_file(file_name)
                File.open(file_name,"w") { |f| f << "<xmp><div id=\"storeArea\">\n#{store_to_s}</div></xmp>" }
                puts "Wrote store only to '#{file_name}'"
        end

        def to_file(file_name)
                File.open(file_name,"w") { |f| f << to_s }
                puts "Wrote tw file to '#{file_name}'"
        end

        def package_as(file_name,package_file_names)
                new_tiddler = add_tiddler Tiddler.new.from_file(file_name)
                new_tiddler.append_content(package(package_file_names))
        end

        def package(file_names)
                "//{{{\nmerge(config.shadowTiddlers,{\n\n"+
                ((file_names.map do |f|
                        Tiddler.new.from_file(f)
                end).map do |t|
                        t.fields[:tiddler] + ":[\n " +
                                        t.body.dump.gsub(/\\t/,"\t").gsub(/\\n/,"\",\n \"") + "\n].join(\"\\n\")"
                end).join(",\n\n")+
                "\n\n});\n//}}}\n"
        end


end

def make_tw(&block)
        TiddlyWiki.new(&block)
end