require "factory.rb"
require "model.rb"
require "config.rb"
require "html.rb"


#================================================
# Classes that do the actual content generation
# via templates.  They generate their content
# based on data obtained from the store.
#================================================


def getStore
  Factory.forName(Config::STORE_CLASS).new
end


#------------------------------------------------
# TemplateContent - represents generic content 
#------------------------------------------------
class TemplateContent
  # The attribs method serves up key/value pairs for replacement into
  # templates.  You can introduce new content types by simply subclassing
  # this class and implementing attribs (or just having any class implemnt
  # the attribs  method).  Then, you must ensure that the appropriate 
  # template contains the variable(s) served up by your new class.
  def attribs
    raise "Content subclasses must return a hash of attributes names and values."
  end
end


#------------------------------------------------
# DatedEntry - represents a dated weblog entry.
#------------------------------------------------
class DatedEntry < TemplateContent
  include Comparable

  def initialize(entryData)
    @entryData = entryData
  end

  def <=>(other)
    date <=> other.date
  end

  def attribs
    { "entry_id"       => @entryData.id,
      "entry_title"    => @entryData.title,
      "entry_text"     => @entryData.text,
      "entry_date"     => @entryData.date,
      "entry_category" => @entryData.category,
      "script_root"    => Config::SITE_SCRIPT,
      "base_url"       => @entryData.baseUrl
    }
  end

  def to_s
    puts title
    puts "--------------------"
    puts content
  end  

end


# ----- TODO following two classes should be refactored into one -------

#------------------------------------------------
# LogEntries - a collection of log entry content
#------------------------------------------------
class LogEntries < TemplateContent
  def initialize(path)
    @path = path
  end

  def attribs
    { "log_entry_content" => collectLogEntries() }
  end
  
  def collectLogEntries
    entryContent = ""
    getStore().getLogEntries(@path, Config::MAX_ENTRIES).each { |entry|
      entryContent << getStore().getEntryTemplate().apply(DatedEntry.new(entry).attribs)
    }
    entryContent
  end
end


#------------------------------------------------
# SidebarEntries - a collection of sidebar content 
#------------------------------------------------
class SidebarEntries < TemplateContent
  def initialize(path)
    @path = path
  end

  def attribs
    { "sidebar_content" => collectSideboxes() }
  end
  
  def collectSideboxes
    barContent = ""
    getStore().getAllSidebarEntries(@path).each { |box|
      barContent << getStore().getSideboxTemplate().apply(DatedEntry.new(box).attribs)
    }
    barContent
  end
end


#========= The following seem too filesystem-dependent to be here - consider moving ========

#------------------------------------------------
# Categories - a collection of category 
# (subdirectory) content.
#------------------------------------------------
class Categories < TemplateContent
  def initialize(filePath, relPath)
    @filePath, @relPath = filePath, relPath
  end

  def attribs
    { "category_content" => collectCategories() }
  end

  def collectCategories
    catContent = "none"
    categories = getStore().getAllCategories(@filePath)
    if categories.size < 1
      catContent
    else
      catContent = ""
      categories.each { |cat|
          caption = cat.displayName.gsub(/(\w ?)+\//, "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;")
          caption.sub!(/\//, "")
          catContent << HTML::internalLink(caption, cat.fileName) 
          catContent << " (#{cat.numEntries})" << "<br>"
      }
      catContent
    end
  end
end


#------------------------------------------------
# BreadCrumbs - lets the user know where she is
# on the site.
#------------------------------------------------
class BreadCrumbs < TemplateContent
  def initialize(relPath)
    @relPath = relPath
  end

  def attribs
    { "breadcrumb_content" => Config::WEBLOG_TITLE + @relPath.gsub("/+$", "") }
  end
end


#------------------------------------------------
# Miscellaneous - emits values for various
# attributes.
#------------------------------------------------
class Miscellaneous < TemplateContent
  def initialize(relPath)
    @relPath = relPath
  end

  def attribs
    { "weblog_title" => Config::WEBLOG_TITLE,
      "current_day" => Time.now.day.to_s, 
      "base_url" => Config::SITE_BASE_URL,
      "current_mon" => Time.now.mon.to_s, 
      "current_yr" => Time.now.year.to_s,
      "script_root"    => Config::SITE_SCRIPT,
      "relative_path" => @relPath }
  end
end


#------------------------------------------------
# Body - a composition of elements that make up
# the body of the HTML page we're constructing.
# Body serves up the attributes of all the things 
# that it composes.
#------------------------------------------------
class Body < TemplateContent
  def initialize(filePath, relPath)
    @filePath, @relPath = filePath, relPath
  end

  def attribs
    attrs = Hash.new
    attrs.update(LogEntries.new(@filePath).attribs)
    attrs.update(SidebarEntries.new(@filePath).attribs)
    attrs.update(Categories.new(@filePath, @relPath).attribs)
    attrs.update(BreadCrumbs.new(@relPath).attribs)
    attrs.update(Miscellaneous.new(@relPath).attribs)
    attrs
  end  

end


#----------------------------------------------------
# Document - represents the HTML document to be
# returned.
#----------------------------------------------------
class Document
  def initialize(environment)
    @env = environment
  end 

  def generateContent
    body = Body.new(@env.getCurrentFilePath, @env.getCurrentRelPath)
    content = getStore().getBodyTemplate().apply(body.attribs)
    print content
  end
end


#------------------------------------------------
# Carries out the highest level of publishing duties.
#------------------------------------------------
class Publisher

  # Publishes based on runtime environment
  def publish(environment)
    Document.new(environment).generateContent
  end
end
