#!/usr/bin/env ruby

require 'misen/style-sgml'
require 'misen/util'

require 'getoptlong'
require 'fileutils'
require 'digest/sha1'

@version  = '0.2'
@verbose  = 0
@force    = false
@outpath  = "#{Dir.pwd}/generated"
@inpath   = "#{Dir.pwd}/source"
@template = "#{Dir.pwd}/templates"
@default  = 'default.html'

@datadir  = "#{Dir.pwd}/data"
@filehash = Hash.new

def printMsg(prefix, msg, descriptor = $stdout)
    descriptor.puts '%-12s %s' % [prefix + ':', msg]
end

def panic(msg)
    printMsg('PANIC', msg, $stderr)
    Kernel.exit(-1)
end

def error(msg)
    printMsg('ERROR', msg, $stderr)
end

def warn(msg)
    printMsg('WARNING', msg)
end

def info(msg)
    printMsg('INFO', msg)
end

def debug(msg, verbosity = 1)
    if(@verbose >= verbosity)
        printMsg("DEBUG(#{verbosity})", msg, $stderr)
    end
end

def generateMenu(dir, file = nil, level = 0)
    debug("call generateMenu('#{dir}', '#{file}', #{level})")
    template = '<ul<misen:class />>
                    <misen:menuentries>
                    <li><a href="<misen:url />"<misen:class />><misen:desc /></a><misen:menu /></li>
                    </misen:menuentries>
                </ul>'

    dir = dir + '/' if(!dir.empty? && dir[-1].chr != '/')
    menu = File.new("#{dir}menu.def").readlines

    data = Hash.new
    menuentries = Array.new

    menu.each { |x|
        entry = x.split(/\s*\|\s*/, 2)
        e = Hash.new
        e[:class] = (file && dir + entry[0] == file ? ' class="active"' : '')
        if(file && dir + entry[0] == file)
            e[:class] = data[:class] = ' class="active"'
        end
        e[:url]   = "/#{dir}#{entry[0]}" # TODO: make relative paths
        e[:desc]  = entry[1].strip
        if(FileTest.directory?("#{dir}#{entry[0]}") && FileTest.exists?("#{dir}#{entry[0]}/menu.def"))
            e[:menu] = generateMenu("#{dir}#{entry[0]}", file, level + 1)
        end
        menuentries << e
    }

    return '' if(menuentries.empty?)

    data[:menuentries] = menuentries

    return removeEmptyLine(Misen.expand_text(Misen::STYLE_SGML, template, data))

end

def generateFiles(dir = '')
    debug("call generateFiles('#{dir}')")
    dir = dir + '/' if(!dir.empty? && dir[-1].chr != '/')
    Dir.foreach(dir.empty? ? '.' : dir) { |x|
        next if(x =~ /^(\.|\.\.)$/)
        
        if(x =~ /\.html$/)
            if(!FileTest.exists?(@outpath + '/' + dir + x) || @filehash[dir + x] != Digest::SHA1.hexdigest(File.new(dir + x).read) || @force)
                info("Transforming #{dir}#{x}")
                data = {:style => getStylePath(dir) + 'style.css', :content => File.new("#{dir}#{x}").read, :menu => generateMenu('', dir + File.basename(x, '.html'))}
                Dir.mkdir("#{@outpath}/#{dir}") if(!FileTest.exists?("#{@outpath}/#{dir}"))
                File.open("#{@outpath}/#{dir}#{x}", "w") { |file|
                    file.print removeEmptyLine(Misen.expand_text(Misen::STYLE_SGML_EX, @source, data))
                }
                @filehash[dir + x] = Digest::SHA1.hexdigest(File.new(dir + x).read)
            else
                info("Nothing to do for #{dir}#{x}")
            end
        elsif(FileTest.directory?(dir + x))
            begin
                Dir.mkdir("#{@outpath}/#{dir}#{x}") if(!FileTest.exists?("#{@outpath}/#{dir}#{x}"))
            rescue
                panic("Could not create #{@outpath}/#{dir}#{x}: #{$!}")
            end
            generateFiles("#{dir}#{x}")
        elsif(x !~ /(\.def|\.swp)$/)
            if(!FileTest.exists?(@outpath + '/' + dir + x) || @filehash[dir + x] != Digest::SHA1.hexdigest(File.new(dir + x).read) || @force)
                info("Copying file #{dir}#{x}")
                FileUtils.cp(dir + x, @outpath + '/' + dir + x)
                @filehash[dir + x] = Digest::SHA1.hexdigest(File.new(dir + x).read)
            else
                info("No need to copy #{dir}#{x}. Already latest.")
            end
        end
    }
end

def removeEmptyLine(str)
    str.split($/).collect{ |x| x =~ /^\s*$/ ? nil : x}.compact.join($/)
end

def getPath(path)
    return path if(path[0].chr == '/')
    return "#{Dir.pwd}/#{path}"
end

def getStylePath(dir)
    newdir = ''

    dir.split('/').each {
        newdir += '../'
    }

    return newdir
end

def usage
    info("Usage: #{File.basename(__FILE__)} [options]")
    info('')
    info('%20s    %s' % ['--verbose, -v',  'Verbose output. Add v for more verbose output'])
    info('%20s    %s' % ['--inpath, -i',   'Specify where to search for input files [./source]'])
    info('%20s    %s' % ['--outpath, -o',  'Specify where to write the output files [./generated]'])
    info('%20s    %s' % ['--template, -t', 'Specify where to search for template files [./templates]'])
    info('%20s    %s' % ['--default, -d',  'The default template name [default.html]'])
    info('%20s    %s' % ['--force, -f',    'Force the new generating of all files [false]'])
    info('%20s    %s' % ['--help, -h',     'Print this screen'])
    info('%20s    %s' % ['--version, -V',  'Print the version and exit'])
end

puts "This is #{File.basename(__FILE__)} v#{@version} 2005 by Ren� Nussbaumer"
puts 

opts = GetoptLong.new(
    ['--verbose',  '-v', GetoptLong::OPTIONAL_ARGUMENT],
    ['--inpath',   '-i', GetoptLong::REQUIRED_ARGUMENT],
    ['--outpath',  '-o', GetoptLong::REQUIRED_ARGUMENT],
    ['--template', '-t', GetoptLong::REQUIRED_ARGUMENT],
    ['--default',  '-d', GetoptLong::REQUIRED_ARGUMENT],
    ['--force',    '-f', GetoptLong::NO_ARGUMENT],
    ['--help',     '-h', GetoptLong::NO_ARGUMENT],
    ['--version',  '-V', GetoptLong::NO_ARGUMENT]
)

opts.each { |arg,value|
    case arg
    when '--verbose'
        @verbose += 1
        value.each_byte { |x|
            if(x.chr == 'v')
                @verbose += 1
            end
        }
        debug("Verbosity set to: #{@verbose}")
    when '--inpath'
        @inpath = getPath(value)
    when '--outpath'
        @outpath = getPath(value)
    when '--template'
        @template = getPath(value)
    when '--default'
        @default = value
    when '--force'
        @force = true
    when '--help'
        usage
        Kernel.exit(0)
    when '--version'
        Kernel.exit(0)
    else
        warn("Unknown option #{arg}#{value && !value.empty? ? ('with value ' + value) : ''}")
    end
}

debug("Current configuration:")
debug('%-10s %s' % ['Inpath:',   @inpath])
debug('%-10s %s' % ['Outpath:',  @outpath])
debug('%-10s %s' % ['Template:', @template])
debug('%-10s %s' % ['Default:',  @default])

begin
    Dir.mkdir(@inpath)   if(!FileTest.directory?(@inpath))
    Dir.mkdir(@outpath)  if(!FileTest.directory?(@outpath))
    Dir.mkdir(@template) if(!FileTest.directory?(@template))
    Dir.mkdir(@datadir)  if(!FileTest.directory?(@datadir))
rescue
    panic("Could not create directory: #{$!}")
end

panic("#{@template}/#{@default} does not exist! Please create at least the default template.") if(!FileTest.exists?("#{@template}/#{@default}"))

@source = File.new("#{@template}/#{@default}").read

Dir.chdir(@inpath)
panic("Init directory needs a menu.def!") if(!FileTest.exists?('menu.def'))

@filehash = Marshal.load(File.new(@datadir + '/digest').read) if(FileTest.exists?(@datadir + '/digest'))
generateFiles
File.open(@datadir + '/digest', 'w') { |file|
    file.print Marshal.dump(@filehash)
}