#--######################################################################
# Copyright (c) 2006 Logica
# 
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# 
# Onno van der Straaten:: initial implementation
#++######################################################################
# {Copyright (c) 2006 Logica}[link:files/COPYRIGHT.html]

class Wiki < Site

  has_many :versions, :foreign_key => 'wiki_id' 
  has_many :user_versions, :foreign_key => 'wiki_id'
  has_many :baseline_process_versions, :foreign_key => 'wiki_id'
  has_many :comments, :foreign_key => 'site_id'
  has_many :checkouts, :foreign_key => 'site_id'

  # Current BaselineProcess: the BaselineProcess used to create the Wiki or 
  # the BaselineProcess used last time the Wiki was updated
  belongs_to :baseline_process, :foreign_key => "baseline_process_id"

  # Baseline processes are used to create and update the Wiki
  has_many :updates, :order => 'finished_on DESC, created_on DESC'
  has_many :updates_todo, :class_name => 'Update', :order => 'created_on ASC', :foreign_key => 'wiki_id', :conditions => 'started_on is null'
  has_many :updates_done, :class_name => 'Update', :order => 'finished_on ASC', :foreign_key => 'wiki_id', :conditions => 'finished_on is not null'
  has_many :updates_inprogress, :class_name => 'Update', :order => 'started_on ASC', :foreign_key => 'wiki_id', :conditions => 'started_on is not null and finished_on is null'
  
  # Used only for Notifier.report TODO a better solution
  attr_accessor :contributors
  

  # HTML Editors remove the nowrap attribute, so we add it to the CSS-file, see Wiki.enhance_files
  DEFAULT_CSS = 'css/default.css'
  
  def status
    logger.debug("Determining status of wiki #{self.title}")
    s = 'Ready' 
    if self.updates.count == 0
      logger.debug("Wiki #{self.title} does not have any update records")
      s = 'Pending' 
    elsif self.updates_todo.count > 0
      logger.debug("Wiki #{self.title} has #{self.updates_todo.count.to_s} updates to do")
      s = 'Scheduled'
      self.updates_todo.each do |u|
        logger.debug("Update todo #{u.baseline_process.title} is started on #{u.started_on}")
        s = 'UpdateInProgress' if !u.started_on.nil?
      end
    end
    return s
  end
  
  def top_contributors
    arr = User.find(:all).collect {|u|[u, Version.count(:conditions => ['user_id = ? and baseline_process_id is null and wiki_id=?',u.id, self.id]) + 
    Comment.count(:conditions => ['user_id = ? and site_id=?',u.id, self.id]) + 
    Upload.count(:conditions => ['user_id = ?',u.id])]}
    arr = arr.sort_by{|t|-t[1]}
  end
  
  def top_monthly_contributors
    arr = User.find(:all).collect {|u|[u, Version.count(:conditions => ['user_id = ? and baseline_process_id is null and wiki_id=?',u.id, self.id]) + 
    Comment.count(:conditions => ['user_id = ? and site_id=?',u.id, self.id]) + 
    Upload.count(:conditions => ['user_id = ?',u.id])]}
    arr = arr.sort_by{|t|-t[1]}
  end

  # Method #wikify does the actual wikifying of the content. It is the second step of the two step
  # process (the first step created the Wiki record and Update record).
  # * copies content of the source Site (parent) into that folder
  # * scans the content of the baseline process if this was not done yet, see #scan4content
  # * enhances the files in that site using method #enhance_files
  # * creates a relation between the Baseline and the Site
  # NOTE: This method is typically not called directly but called via #Update.wikify
  def wikify(update)
    bp = update.baseline_process
    logger.info("Updating Wiki #{self.title} with baseline process #{bp.title}")
    raise 'The site was updated already or has a an update in progress!' if !update.first_update?
    raise "Can only update with a baseline process (static site)" if bp.wiki?
    update.update_attributes(:started_on => Time.now) # changes the wiki status to 'UpdateInProgress'
    File.makedirs(self.path)   
    logger.info("Copying files from #{bp.path} to #{self.path}")
    FileUtils.cp_r(bp.path + "/.", self.path) # How to copy the contents of a folder and not the folder [http://www.ruby-doc.org/core/classes/FileUtils.html#M001703]
    bp.scan4content if !bp.content_scanned_on
    cadmin = User.find_central_admin
    bp.pages.each do |p| 
      newp = WikiPage.new(:rel_path => p.rel_path, :site => self, :tool => 'EPFC', :status => 'New')
      self.pages << newp
      # create baseversion
      baseversion = BaselineProcessVersion.new(:baseline_update => update, :user => cadmin, :page => newp,
                      :wiki => self, :version => 0, :done => 'Y', :note => 'Automatically created',
        :baseline_process_id => bp.id) 
      newp.baseline_process_versions << baseversion  
    end
    enhance_files
    self.baseline_process = bp
    self.wikified_on = Time.now
    self.save!
  end

  # Method #update_wiki updates a Wiki with a BaselineProcess with the following steps:
  # 1. Copy content (overwriting all pages)
  # 2. Update status of EPFC pages to 'Undetermined'
  # 3. Update status of the Wiki pages, find 'Updated' and 'New' pages
  # 4. Make EPFC pages obsolete if they are not part of the new BaselineProcess
  # 5. Make Wiki pages obsolete if they have been harvested
  # 6. Enhance files
  # TODO: notify users that want to be notified (add about notify_baseline_updates column to users)
  # TODO: Change 68 - Update should continue with checkouts and should not overwrite not harvested changes
  def  update_wiki(update)
    update.update_attributes(:started_on => Time.now)    
    bp = update.baseline_process
    logger.info("Starting update of wiki #{self.title} from baseline process #{self.baseline_process.title} (#{self.baseline_process.id}) to #{bp.title} (#{bp.id})")
    logger.info("Copy update site " + bp.path + " to " + self.path)
    cadmin = User.find_central_admin
    # 1. 
    bp.copy_to(self, nil)
    
    # 2. Update status of EPFC pages to 'undetermined'  
    Page.update_all( "status = 'Undetermined'", ["tool = ? and site_id = ? ", 'EPFC', self.id, ])

    # 3. Update
    bp.scan4content if bp.content_scanned_on.nil?
    bp.pages.each do |p|
      page = Page.find_by_site_id_and_rel_path(self.id, p.rel_path)
      if page
        page.status = 'Updated'
        no = page.max_version_no + 1
      else
        page = WikiPage.new(:rel_path => p.rel_path, :site => self, :tool => 'EPFC', :status => 'New')
        self.pages << page
        no = 0
      end
      # create baseversion
      baseversion = BaselineProcessVersion.new(:baseline_update => update,:user => cadmin, :page => page,
                      :wiki => self, :version => no, :done => 'Y', :note => 'Automatically created',
        :baseline_process_id => bp.id) 
      page.baseline_process_versions << baseversion
      page.save!
    end

    # 4. 
    Page.find(:all, :conditions => ['site_id = ? and status = ?', self.id, 'Undetermined']).each do |p|
      p.status = 'RemovedOrMoved'
      p.save!
    end 
    
    # 5.
    Page.find(:all, :conditions => ['site_id = ? and tool = ?', self.id, 'Wiki']).each do |p|
      if p.harvested?
        p.status = 'Harvested'
      end
    end

    # 6.
    enhance_files

    # Change 68 - current versions not harvested
    versions = UserVersion.find(:all, :conditions => ['wiki_id =? and done <> ? and version is not null', self.id, 'Y'])
    logger.info("Found #{versions.size.to_s} versions with unharvested changes")
    pages = versions.collect{|version| version.page}.uniq
    if pages
      logger.info("Found #{pages.size.to_s} pages with unharvested changes") if pages
      snippets = Page.get_snippets
      pages.each do |page|
        logger.info("Processing page #{page.presentation_name}")
        if page.checkout
          logger.info("Page has unharvested versions, we don't need to set a current version")
        else  
          cv = page.current_version 
          unless cv.nil?
            if cv.current 
              logger.info("Page #{page.presentation_name} already has current version with id #{cv.id}, we don't need to set a current version")
            else
              logger.info("Page #{page.presentation_name} does not have a current version")          
              # set the current version equal to the last version that is not part of the update we are doing
              page.current_version = Version.find(:first, :order => 'version DESC', :conditions => ['page_id=? and version is not null and update_id is null',page.id])
            end
            page.html = page.current_version.html
            Page.enhance_file(page.path, snippets)
          end
        end
      end
    end
    self.baseline_updated_on = Time.now
    self.baseline_process = bp
    self.save!
  end
  
  def url(absolute = false, request_host = ENV['EPFWIKI_HOST'])
    #--
    # TODO anomaly, the following line shouldn't be necessary but is, in some cases. 
    # When this method is called from page.url request_host will be nil    
    #++
    request_host = ENV['EPFWIKI_HOST'] if request_host.nil? 

    logger.debug("url for site #{self.title}, absolute #{absolute.inspect}, request_host #{request_host.inspect}")
    s = "/#{ENV['EPFWIKI_WIKIS_FOLDER']}/#{self.folder}/"
    s = "http://#{request_host}#{s}" if absolute
    logger.debug("returning: #{s}")
    s
  end
  
  def validate_on_create
      logger.info("Folder #{ENV['EPFWIKI_WIKIS_PATH']}/#{self.folder} should not exists already")
      errors.add(:folder, 'already exists') if  (!self.folder.blank? && File.exists?("#{ENV['EPFWIKI_WIKIS_PATH']}/#{self.folder}")) || !Wiki.find_by_folder(self.folder).nil?
      if self.title == 'Templates'
        if Wiki.find(:first, :conditions => ['title = ?','Templates'])
          errors.add(:title, ' "Templates" has been used. There can only be one Wiki with that name.') 
        end
      end 
  end

  def users
    return User.find(:all, :conditions => ['exists (select * from versions vsn where vsn.wiki_id = ? and vsn.user_id = users.id and baseline_process_id is null) or exists (select * from da_texts cmt where cmt.user_id = users.id and cmt.site_id = ?) or exists (select * from uploads where uploads.user_id = ?)', id, id, id])
  end

  #######
  private
  #######

  def enhance_files

    # CSS enhancement making it more robust layout of html, which shouldn't influence how the page is displayed
    css = IO.readlines(self.path + '/' + DEFAULT_CSS).join + "\n"
    [/.pageTitle .*?\{.*?\}/m, /.expandCollapseLink \{.*?\}/m].each do |regex|
      match = regex.match(css)
      if match
        new_css_snip = match[0].gsub('}', "\nwhite-space: nowrap;}") 
        css = css.gsub(match[0], new_css_snip) 
      else
        logger.info("CSS snippet not found, #{DEFAULT_CSS} changed in newer release of EPF?")
      end
    end
    f = File.new(self.path + '/' + DEFAULT_CSS, 'w')
    f.puts(css)
    f.close
    snippets = Page.get_snippets # TODO rename to snippets
    self.files_wikifiable.each do |path|
      Page.enhance_file(path, snippets)
    end
  end  
  
end