#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot'

# set this to true if you want to create users that appear in metafiles
# (useful for importing on devel workstations)
#
# if false, an exception is thrown instead
CREATE_MISSING_USERS = true

# all users created in the above mentioned way will have those attributes
DUMMY_USER_ATTR = {
  :password => "asdfasdf",
  :password_confirmation => "asdfasdf",
  :email => "noone@localhost"
}

### boot rails ###
Rails::Initializer.run do |config|
  config.frameworks -= [ :action_web_service, :active_resource ]
  config.load_paths << File.expand_path("#{RAILS_ROOT}/../common/lib")
end

require 'activexml'

ActiveXML::Base.config do |conf|
  conf.lazy_evaluation = true

  conf.setup_transport do |map|

    map.default_server :rest, "#{SOURCE_HOST}:#{SOURCE_PORT}"

    map.connect :project, "bssql:///", :write_through => :false
    map.connect :package, "bssql:///", :write_through => :false

    map.connect :xproject, "rest:///source/:name/_meta",
      :all => "rest:///source/:name"
    map.connect :xpackage, "rest:///source/:project/:name/_meta",
      :all => "rest:///source/:project/:name/"
  end
end


class Xproject < ActiveXML::Base
end

class Xpackage < ActiveXML::Base
end


def store_project( p )
  STDERR.print "  #{p.name} => "
  begin
    create_missing_users_for p

    p.save
    STDERR.puts "success"
    store_packages_for p
    return true
  rescue DbProject::SaveError => e
    if e.message =~ /^unable to walk on path/
      STDERR.puts "delayed"
      return false
    else
      STDERR.puts "FAILED"
      raise e
    end
  end
end

def store_packages_for( p )
  begin
    STDERR.puts "  --> storing packages for project '#{p.name}'"
    list = Xpackage.find :all, :project => p.name
    xpack = nil
    list.each_entry do |entry|
      STDERR.print "     #{entry.name} => "
      xpack = Xpackage.find entry.name, :project => p.name
      # remove devel entries
      xpack.data.each_element('devel') {|e| xpack.data.delete_element e}
      pack = Package.new( xpack.dump_xml, :project => p.name )
      
      create_missing_users_for xpack

      pack.save
      STDERR.puts "success"
    end
    STDERR.puts "  <-- fin"
  rescue Object => e
    # remove whole project to cause missing packages to be read at next run
    STDERR.puts "### store_packages exception handling"
    DbProject.find_by_name(p.name.to_s).destroy
    xmldebug xpack.dump_xml
    raise e
  end
end

@xmldebug_called = false
def xmldebug(str)
  STDERR.puts caller
  return if @xmldebug_called
  @xmldebug_called = true
  STDERR.puts "\n\n>>> Exception caused by this xml:\n"+str+"\n\n"
end

def create_missing_users_for( meta )
  return unless CREATE_MISSING_USERS
  meta.each_person do |person|
    u = User.find_by_login person.userid.to_s
    if not u
      attribs = {:login => person.userid.to_s}.merge DUMMY_USER_ATTR
      u = User.create attribs
      u.save
    end
  end
end

filter = Array.new
#filter = ["Fedora:Core6", "Fedory:Extras6"];
#filter = ['Apache:Modules', 'Apache', 'Fedora:Core5', 'Mandriva:2006', 'openSUSE:10.2', 'SUSE:SLES-9', 'SUSE:SLE-10:SDK',
#  'Java:Sun-Java-1.5', 'SUSE:SL-10.1', 'SUSE:SL-10.0', 'SUSE:SL-9.3', 'SUSE:SLE-10', 'openSUSE:Factory'
#];
#filter = %w(devel:tools:building SUSE:SLE-10:SP2:SDK home:dirkmueller:playground:kde4)

projects = Xproject.find(:all)

# maps project name to an array: [project_object, {packagename => package_object, ...}]
cache = Hash.new
restart = false

STDERR.puts "--> storing projects"
projects.each_entry do |entry|

  #if filter is set, only fetch the projects in the array
  if not filter.empty?
    next unless filter.include? entry.name
  end

  p = nil 
  begin
    p = Project.find( entry.name )
  rescue ActiveXML::Transport::NotFoundError
  end
  next if p

  backend_project = Xproject.find entry.name
  p = Project.new( backend_project.dump_xml )

  cache[entry.name] = p unless store_project(p)
end

run_count = 0;

while( cache.length > 0 )
  STDERR.puts "--> trying to store previously unsaved projects, run [#{run_count += 1}]"
  tmp = cache.values.clone
  tmp.each do |p|
    p.each_repository do |repo|
      repo.each_path do |path|
        unless cache.has_key?(path.project.to_s) or DbProject.find_by_name(path.project.to_s)
          STDERR.puts "fetching dependency #{path.project}"
          cache[path.project.to_s] = Project.new(Xproject.find(path.project.to_s).dump_xml) 
        end
      end
    end

    begin
      if store_project(p)
        cache.delete p.name.to_s
        STDERR.puts "*** cache.length: #{cache.length}"
      end
    rescue ActiveRecord::StatementInvalid => e
      raise e
    end
  end
end
