#!/usr/bin/ruby
require './op.rb'
require './mc.rb'
require './ma.rb'
require './cl.rb'

Dir.mkdir($home) if !File.exist? $home

$db = "test"
$master = $master ? BDB::MC::ENVID : BDB::EID_INVALID

$env = BDB::MC.new($home, $totalsites, $priority, $timeout)

Thread.abort_on_exception = true

def display_message(mess)
   puts "===> #{mess}"
end

def elections(tiout = 2, max = 5)
   display_message("Beginning an election")
   count = 0
   begin
      $master = $env.rep_elect *$env.param
   rescue
      if count >= max
	 display_message("Forcing the election")
	 $master = BDB::MC::ENVID
      else
	 display_message("retrying ...")
	 count += 1
	 sleep tiout
	 retry
      end
   end
   if $master == BDB::MC::ENVID
      display_message("Won the election")
   else
      display_message("The new master is #{$env.mach_name($master)}")
   end
end

def hm_loop(sock, host, addr)
   eid = $env.add_mach(sock, host, addr)
   display_message("new client #$me -- #{host}:#{addr}")
   Thread.new do
      election = nil
      while true
         record, control = $env.comm(eid)
	 if ! record
	    break if $master == BDB::MC::ENVID || $master != eid
	    $master = BDB::EID_INVALID
	    elections(1, 2)
	    if $master == BDB::MC::ENVID
	       do_master
	    end
	    break
	 end
	 res, cdata, envid = $env.rep_process_message(control, record, eid)
	 case res
	 when BDB::REP_NEWSITE
	    display_message("REP_NEWSITE #{cdata}")
	    if cdata != $me
	       begin
		  connect_site(cdata.split(/:/))
	       rescue
		  break
	       end
	    end
	 when BDB::REP_HOLDELECTION
	    display_message("REP_HOLDELECTION")
	    if $master != BDB::MC::ENVID
	       if election
		  election.join
		  election = nil
	       end
	       election = Thread.new do
		  elections
		  if $master == BDB::MC::ENVID
		     $env.rep_start nil, BDB::REP_MASTER
		  end
		  election = nil
	       end
	    end
	 when BDB::REP_NEWMASTER
	    display_message("REP_NEWMASTER #{envid}")
	    $master = envid
	    do_master() if envid == BDB::MC::ENVID
	 when 0
	 when BDB::REP_DUPMASTER
	    display_message("REP_DUPMASTER")
	    $env.rep_start $me, BDB::REP_CLIENT
	    elections
	    if $master == BDB::MC::ENVID
	       do_master()
	    else
	       do_client()
	    end
	 else
            display_message("error #{res} #{cdata} #{envid}")
	 end
      end
      $env.remove_mach(eid)
      election.join if election
	 
   end
end

def connect_site(site)
   return if $me == site.join(":")
   s = TCPSocket.open(site[0], site[1])
   hm_loop(s, site[0], site[1])
end

connect_thread = Thread.new do
   server = TCPserver.open($port)
   loop do
      ns = server.accept
      addr = ns.peeraddr
      hm_loop(ns, addr[2], addr[1])
   end
end

connect_all = Thread.new do
   others = $others.dup
   while ! others.empty?
      todo = []
      others.each do |site|
         begin
            connect_site(site)
         rescue
            todo << site
         end
      end
      others = todo
      sleep 1
   end
end

if $master == BDB::MC::ENVID
   do_master
else
   do_client
end

connect_all.join
connect_thread.join
