require 'yaml'
require 'rscm/changes'
require 'rscm/abstract_scm'

module DamageControl
  module Visitor
    # Visitor that saves each ChangeSet in a folder with the name
    # of each ChangeSet's +identifier+.
    #
    # Is also able to load changesets.
    #
    class YamlPersister

      def initialize(changesets_dir)
        @changesets_dir = changesets_dir
      end

      def visit_changesets(changesets)
      end

      def visit_changeset(changeset)
        changeset_file = "#{@changesets_dir}/#{changeset.identifier.to_s}/changeset.yaml"
        dir = File.dirname(changeset_file)
        FileUtils.mkdir_p(dir)
        File.open(changeset_file, "w") do |io|
          YAML::dump(changeset, io)
        end
      end

      def visit_change(change)
      end

    #### Non-visitor methods (for loading)

      # Loads +prior+ number of changesets upto +last_changeset_identifier+.
      # +last_changeset_identifier+ should be the dirname of the folder containing 
      # the last changeset.
      #
      def load_upto(last_changeset_identifier, prior)
        Log.info "Loading #{prior} changesets from #{@changesets_dir} (from #{last_changeset_identifier} and down)"
        last = identifiers.index(last_changeset_identifier)

        changesets = RSCM::ChangeSets.new
        if(last)
          first = last - prior + 1
          first = 0 if first < 0

          identifiers[first..last].each do |identifier|
            changeset_yaml = "#{@changesets_dir}/#{identifier.to_s}/changeset.yaml"
            Log.info "Loading changesets from #{changeset_yaml}"
            begin
              changesets.add(YAML::load_file(changeset_yaml))
            rescue Exception => e
              # Sometimes the yaml files get corrupted
              Log.error "Error loading changesets file: #{File.expand_path(changeset_yaml)}"
              # Todo: delete it and schedule it for re-retrieval somehow.
            end
          end
        end
        changesets
      end

      # Returns a sorted array of Time or int representing the changeset directories.
      #
      def identifiers
        # This is pretty quick - even with a lot of directories.
        # TODO: the method is called 5 times for a page refresh!
        dirs = Dir["#{@changesets_dir}/*"].find_all {|f| File.directory?(f) && File.exist?("#{f}/changeset.yaml")}
        # Turn them into ints so they can be sorted.
        dirs.collect { |dir| File.basename(dir).to_identifier }.sort
      end

      # Returns the identifier of the latest changeset.
      #
      def latest_identifier
        identifiers[-1]
      end
    end
  end
end