require 'div/div'
require 'div/tofusession'
require 'singleton'
require 'div/js'

class DipApp
  include Singleton

  DIP_URI = 'druby://localhost:12345'
  
  @@front = DRbObject.new(nil, DIP_URI)

  def self.front=(ro)
    @@front = ro
  end
  
  def initialize
    @front = @@front
  end
  
  def server(phrase)
    @front.server(phrase)
  end
end

class DipDiv < Div::Div
  set_erb('dip.erb')

  def initialize(session)
    super(session)
    @front = DipApp.instance
    @login = DipLoginDiv.new(session, self)
    @list = DipListDiv.new(session, self)
    @send = DipSendDiv.new(session, self)
    @msg = DipMsgDiv.new(session, self)
    @server = nil
  end
  attr_reader :front, :server

  def server=(s)
    @server = s
    @msg.reset_hidden
  end
  
  def make_message_to(addr, msg=nil)
    return unless @server
    @send.send_to(addr, msg)
  end

  def has_unopened?
    return false unless @server
    return @server.msg_holder.has_unopened?
  end
end

class DipListDiv < Div::Div
  set_erb('dip_list.erb')
  
  SORT_KEYS = %w(nickname group addr absence)

  def initialize(session, dip_div)
    @dip = dip_div
    @sort_key = 'addr'
    super(session)
  end

  def do_sort(context, params)
    key, = params['key']
    @sort_key = key if SORT_KEYS.include?(key)
  end
  
  def do_send(context, params)
    addr, = params['ipaddr']
    @dip.make_message_to(addr) if addr
  end

  def do_update_recent(context, params)
    @dip.server.update_recent
  end
end

class DipLoginDiv < Div::Div
  set_erb('dip_login.erb')
  
  def initialize(session, dip_div)
    @dip = dip_div
    super(session)
  end
  
  def do_login(context, params)
    phrase, = params['phrase']
    begin
      @dip.server = @dip.front.server(phrase)
    rescue
      @dip.server = nil
      @message = "oops! bad pass-phrase."
    end
  end
end

class DipSendDiv < Div::Div
  set_erb('dip_send.erb')
  
  def initialize(session, dip_div)
    @dip = dip_div
    @addr = nil
    @message = nil
    super(session)
  end

  def server
    @dip.server
  end
  
  def send_to(addr, msg=nil)
    @addr = nil
    @message = nil
    return unless server
    return unless server.members.include?(addr)
    @addr = addr
    @message = msg
  end

  def to_addr
    return nil unless @addr
    info = server.member(@addr)
    return @addr unless info
    "#{info['nickname']} #{info['group']} (#{info['addr']})"
  end
  
  def do_send(context, params)
    return unless server
    msg = params['message']
    return unless msg
    msg = msg.join('')
    return if msg == ''
    server.send_msg(@addr, msg, true)
    @addr = nil
    @message = nil
  end
end

class DipMsgDiv < Div::Div
  set_erb('dip_msg.erb')
  
  def initialize(session, dip_div)
    @dip = dip_div
    super(session)
    @hidden = {}
    @focus = nil
  end

  def reset_hidden
    @hidden = {}
  end

  def do_open(context, params)
    return unless @dip.server
    key, = params['key']
    return unless key
    return if key == ''
    msg = @dip.server.msg_holder[key]
    return unless msg
    msg.open unless msg.sent?
    @hidden.delete(key)
    @dip.make_message_to(msg.from_addr)
  end

  def do_hide(context, params)
    return unless @dip.server
    key, = params['key']
    return unless key
    return if key == ''
    @hidden[key] = true
  end

  def do_show(context, params)
    reset_hidden
  end

  def do_send(context, params)
    key, = params['key']
    return unless key
    return if key == ''
    msg = @dip.server.msg_holder[key]
    return unless msg
    @dip.make_message_to(msg.from_addr, quote_message(msg.message))
  end

  def do_focus(context, params)
    @focus = nil
    addr ,= params['ipaddr']
    return unless addr
    return if addr == ''
    @focus = addr
  end
  
  def quote_message(msg)
    str = ""
    msg.each do |line|
      str << "> #{line}"
    end
    str
  end
end

class DipBiff < Div::Div
  set_erb('biff.erb')
  
  def initialize(session, dip)
    super(session)
    @dip = dip
  end
end

class DipArrive < Div::Div
  set_erb('arrive.erb')
  
  def initialize(session, dip)
    super(session)
    @dip = dip
    @interval = 10000
  end
  attr_reader :interval
end

class BaseDiv < Div::Div
  set_erb('base.erb')

  def initialize(session)
    super(session)
    @dip = DipDiv.new(session)
    @arrive = DipArrive.new(session, @dip)
  end
  attr_reader :dip
end

class DipTofuSession < Div::TofuSession
  def initialize(bartender, hint=nil)
    super(bartender, hint)
    @base = BaseDiv.new(self)
    @biff = DipBiff.new(self, @base.dip)
  end

  def do_GET(context)
    update_div(context)

    return if do_inner_html(context)

    context.res_header('content-type', 'text/html; charset=euc-jp')
    p context.req_path_info
    case context.req_path_info
    when '/biff'
      context.res_body(@biff.to_html(context))
    else
      context.res_body(@base.to_html(context))
    end
  end
end

if __FILE__ == $0
  require 'drb/drb'
  require 'tofu/proxy'
  
  tofu = Tofu::Bartender.new(DipTofuSession)
  DRb.start_service('druby://localhost:7642', tofu)
  DRb.thread.join
end
