#!/usr/bin/env ruby.ruby2.3
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"
}

require "activexml/activexml"

### boot rails ###
Rails::Initializer.run do |config|
  config.frameworks -= [ :action_web_service, :active_resource ]

  config.action_controller.session = {
    session_key: "_frontend_session",
    secret:      "ad9712p8349zqmowiefzhiuzgfp9s8f7qp83947p98weap9866e7"
  }
end

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

  conf.setup_transport do |map|
    map.default_server :rest, "#{CONFIG['source_protocol']}://#{CONFIG['source_host']}:#{CONFIG['source_port']}"

    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::Node
end

class Xpackage < ActiveXML::Node
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 Project::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"
    Project.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 Project.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
