module Awestruct
  class Dependencies

    attr_reader :page
    attr_reader :dependencies
    attr_reader :key_dependencies
    attr_reader :dependents
    attr_reader :key_dependents
    attr_reader :content_hash
    attr_reader :key_hash
    attr_reader :has_changed_content
    attr_reader :has_changed_keys


    def self.track_dependencies=(bol)
      @track_dependencies = bol
    end

    def self.should_track_dependencies
      @track_dependencies
    end

    def self.top_page
      @pages ||= []
      @pages.first
    end

    def self.push_page(page)
      $LOG.debug "push #{page.output_path}" if $LOG.debug?
      if ( top_page.nil? )
        $LOG.debug "clearing dependencies" if $LOG.debug?
        page.dependencies.clear
      else
        $LOG.debug "adding page as a dependency to top_page" if $LOG.debug?
        top_page.dependencies.add_dependency( page )
      end
      @pages.push( page )
    end

    def self.pop_page
      page = @pages.pop
      $LOG.debug "pop #{page.output_path} #{@pages.empty?}" if $LOG.debug?
    end


    def self.track_dependency(dep)
      return if top_page.nil?
      return if top_page == dep
      $LOG.debug "dep #{top_page.relative_source_path} - #{dep.relative_source_path}" if $LOG.debug?
      top_page.dependencies.add_dependency(dep)
    end

    def self.track_key_dependency(dep, key)
      return if !Awestruct::Dependencies.should_track_dependencies
      return if top_page.nil?
      return if top_page == dep
      $LOG.debug "dep key #{top_page.relative_source_path} - #{dep.relative_source_path} -> #{key}" if $LOG.debug?
      $LOG.debug "callers #{Kernel.caller}" if $LOG.debug?
      top_page.dependencies.add_key_dependency(dep)
    end


    def initialize(page)
      @page = page
      @dependencies = Set.new
      @key_dependencies   = Set.new
      @dependents   = Set.new
      @key_dependents   = Set.new
      @content_hash = nil
      @key_hash = nil
      @has_changed_content = false
      @has_changed_keys = false
    end

    def key_hash=(key)
      $LOG.debug "key_hash #{key}" if $LOG.debug?
      if @key_hash.nil?
        @has_changed_keys = false
      else
        if key.eql? @key_hash
          @has_changed_keys = false
        else
          @has_changed_keys = true
        end
      end
      @key_hash = key
    end

    def content_hash=(key)
      $LOG.debug "content_hash #{key}" if $LOG.debug?
      if @content_hash.nil?
        @has_changed_content = false
      else
        if key.eql? @content_hash
          @has_changed_content = false
        else
          @has_changed_content = true
        end
      end
      @content_hash = key
    end

    def <<(dep)
      add_dependency( dep )
    end

    def add_dependency(dep)
      return if dep.nil?
      return if @page.do_not_track_dependencies
      return if @page.output_path.nil?
      return if dep == @page
      $LOG.debug "adding dependency #{dep.source_path} to #{page.source_path}" if $LOG.debug?
      @dependencies << dep
      dep.dependencies.add_dependent( page )
    end

    def add_key_dependency(dep)
      return if dep.nil?
      return if @page.do_not_track_dependencies
      return if @page.output_path.nil?
      return if dep == @page
      @key_dependencies << dep
      dep.dependencies.add_key_dependent( page )
    end

    def add_dependent(dep)
      return if dep.nil?
      @dependents << dep
    end

    def add_key_dependent(dep)
      return if dep.nil?
      @key_dependents << dep
    end

    def remove_dependent(dep)
      return if dep.nil?
      @dependents.delete( dep )
    end

    def clear
      @dependencies.clear
      @dependents.each do |d|
        if (d.instance_of? Awestruct::Dependencies)
          d.remove_dependent( page )
        else
          d.dependencies.remove_dependent( page )
        end
      end
    end

    def persist!
      return if  page.output_path.nil? || page.output_path == ''
      file = File.join( @page.site.config.dir.to_s, '.awestruct', 'dependency-cache', page.output_path )
      $LOG.debug "store #{file}" if $LOG.debug?
      FileUtils.mkdir_p( File.dirname( file ) )
      File.open( file, 'w' ) do |file|
        file.puts "ch:#{@content_hash}"
        file.puts "kh:#{@key_hash}"
        @dependencies.collect{|e| e.relative_source_path }.uniq.each do |d|
          file.puts "c:#{d}" unless d.nil?
        end
        @key_dependencies.collect{|e| e.relative_source_path }.uniq.each do |d|
          file.puts "k:#{d}" unless d.nil?
        end
      end
    end

    def load!
      return if  page.output_path.nil? || page.output_path == ''
      file = File.join( @page.site.config.dir, '.awestruct', 'dependency-cache', page.output_path )
      $LOG.debug "load #{file}" if $LOG.debug?
      if ( File.exist?( file ) )
        File.open( file, 'r' ) do |file|
          file.each_line do |line|
            type, path = line.split(':')
            path ||= ""
            path.strip!
            if type.eql? 'c' or type.eql? 'k'
              d = find_page_by_path( path )
              unless d.nil?
                add_dependency( d ) if 'c'.eql? type
                add_key_dependency( d ) if 'k'.eql? type
              end
            else
              self.content_hash = path if 'ch'.eql? type
              self.key_hash = path if 'kh'.eql? type
            end
          end
        end
        return true
      end
      false
    end

    def find_page_by_path(path)
      page.site.pages_by_relative_source_path[ path ]
    end

  end
end
