require "pstore"
require "thread"
require "singleton"

class Enquete
  attr_reader :question
  attr_accessor :yes, :no

  def initialize(question)
    @question = question
    @yes = 0
    @no = 0
  end

  def inc_yes
    @yes = @yes + 1
  end

  def inc_no
    @no = @no + 1
  end

  def all
    return @yes + @no
  end

  def percent
    return 0.0 if self.all == 0
    return (@yes / self.all.to_f) * 100
  end
end

class EnqueteDB
  include Singleton

  class NoEnqueteException < RuntimeError; end
  
  @@filename = "enquete.db"
  def self.filename
    @@filename
  end

  def self.filename=(file)
    @@filename = file
  end
  
  def self.db_file
    self.filename
  end
  
  def initialize
    @mutex = Mutex.new
    @db = PStore.new(@@filename)
  end

  def to_a
    in_db do
      return @db.roots.sort.map{|num| @db[num]}
    end
  end

  def num_of_enquetes
    in_db do
      return @db.roots.size
    end
  end

  def [](num)
    in_db do
      begin
	return @db[num]
      rescue
	raise NoEnqueteException, "enquete of num #{num} doesn't exist."
      end
    end
  end

  def latest
    raise NoEnqueteException if @db.transaction { @db.roots.empty? }

    in_db do
      return @db[@db.roots.size]
    end
  end

  def normalize_num(num)
    all = self.num_of_enquetes
    return all unless num

    if num >= all
      return all
    elsif num <= 1
      return 1
    else
      return num
    end
  end

  def add_enquete(enquete)
    in_db do
      num = @db.roots.size
      @db[num+1] = enquete
      @db.commit
    end
  end

  def new_enquete(question)
    enq = Enquete.new(question)
    self.add_enquete(enq)
  end

  def inc_yes(enq_num)
    inc_yes_or_no(enq_num, :inc_yes)
  end

  def inc_no(enq_num)
    inc_yes_or_no(enq_num, :inc_no)
  end

  def inc_yes_or_no(enq_num, method)
    in_db do
      if @db.root?(enq_num)
	enq = @db[enq_num]
	enq.send(method)
	@db[enq_num] = enq
	@db.commit
      else
	raise NoEnqueteException, "enquete of num #{enq_num} doesn't exist."
      end
    end
  end

  def in_db
    @mutex.synchronize do
      @db.transaction do
	yield
      end
    end
  end
end

if __FILE__ == $0
  require 'drb/drb'
  
  front = EnqueteDB.instance
  DRb.start_service(ARGV.shift || 'druby://localhost:7981', front)
  puts DRb.uri
  gets
end
