require 'singleton'

class Paper
  class Comment
    def initialize(paper, who, text, succ)
      @paper = paper
      @who = who
      @text = text
      @succ = succ
      @time = Time.now
    end
    attr_reader :paper, :who, :text, :succ, :time

    def to_hash
      {'who'=>@who, 'text'=>@text, 'succ'=>@succ, 'time'=>@time}
    end
  end

  def initialize(owner, text, member)
    @owner = owner
    @text = text
    @member = member.dup
    @comment = []
    @status = nil
    @time = Time.now
    md5 = MD5.new([text, owner, member, time].inspect)
    @key =  md5.hexdigest[0,16]
  end
  attr_reader :owner, :text, :member, :comment, :status, :time, :key

  def <=>(obj)
    @time <=> obj.time
  end

  def add_comment(who, text, succ)
    raise('Bad Author') unless @member[0] == who
    @member.shift
    @comment.push(Comment.new(self, who, text, succ))

    unless succ
      while who = @member.shift
	@comment.push(Comment.new(self, who, '-', false))
      end
    end

    if succ
      @status = :succ if @member.size == 0
    else
      @status = :fail
    end
  end

  def curr
    @member[0]
  end

  def to_hash
    ary = @comment.collect { |c| c.to_hash }
    { 'owner'=>@owner, 'text'=>@text, 'member'=>@member, 'comment'=>ary,
      'status'=>@status, 'time'=>@time, 'key'=>@key, 'curr'=>curr}
  end
end

class PaperModel
  include Singleton

  def initialize
    @mutex = Mutex.new
    @item = []
    @pstore = PStore.new('paper.db')
    load
  end
  attr_reader :item

  def add(owner, text, member)
    it = Paper.new(owner, text, member)
    @mutex.synchronize do
      @item.push(it)
      @item.sort!
    end
    save
  end

  def list_by_owner(who, hash=false)
    ary = nil
    @mutex.synchronize do
      ary = @item.find_all do |item|
	item.owner == who
      end
    end
    hash ? to_hash(ary) : ary
  end

  def list_by_commentator(who, hash=false)
    ary = nil
    @mutex.synchronize do
      ary = @item.find_all do |item|
	item.curr == who
      end
    end
    hash ? to_hash(ary) : ary
  end

  def to_hash(ary)
    ary.collect do |item|
      item.to_hash
    end
  end

  def remove(key, who)
    @mutex.synchronize do
      @item.delete_if do |item|
	item.owner == who and item.key == key
      end
    end
    save
  end

  def comment(key, who, text, succ)
    @mutex.synchronize do
      it = @item.find do |item|
	item.curr == who and item.key == key
      end
      it.add_comment(who, text, succ) if it
    end
    save
  end

  def load
    @mutex.synchronize do
      @pstore.transaction do |db|
	db['item'] = [] unless db.root?('item')
	@item = db['item']
      end
      @item = [] unless @item
    end
  end

  def save
    @mutex.synchronize do
      @pstore.transaction do |db|
	db['item'] = @item
	db.commit
      end
    end
  end
end

if __FILE__ == $0
  require 'drb/drb'

  front = Paper.instance
  DRb.start_service(ARGV.shift || 'druby://localhost:7985', front)
  puts DRb.uri
  gets
end
