#!/usr/bin/env ruby
# cybozu_client.rb - cybozu client interface
#
# Author: MIZOGUCHI Coji <mizoguchi.coji at gmail.com>
#
# $Id$
#
require 'kconv'
require 'uri'
require 'net/http'
require 'csv'

Net::HTTP.version_1_2

# ユーザ
class CybozuUser
  attr_reader :user_id, :name

  def initialize(user_id, name)
    @user_id = user_id
    @name = name
  end

  def hash
    @user_id
  end

  def eql?(obj)
    obj.user_id == @user_id
  end
end

# グループ
class CybozuGroup
  attr_reader :group_id, :name, :members

  def initialize(group_id, name, members = nil)
    @group_id = group_id
    @name = name
    @members = members
  end

  def hash
    @group_id
  end

  def eql?(obj)
    obj.group_id == @group_id
  end
end

# クライアント
class CybozuClient
  attr_reader :server_uri
  attr_reader :cookie

  public
  # コンストラクタ
  def initialize(server_uri)
    @server_uri = URI.parse(server_uri)
    @cookie = nil
  end

  # ログイン
  def login(user_id, password)
    params = {
      '_System'  => 'login',
      '_Login'   => '1',
      '_ID'      => user_id,
      'Password' => password,
    }

    web = Net::HTTP.new(@server_uri.host)
    res = web.post(@server_uri.request_uri, CybozuClient.build_params(params))
    @cookie = parse_cookies(res.get_fields('set-cookie'))

    if @cookie.nil?
      return false
    end

    begin
      yield
      logout
    rescue LocalJumpError => evar
    ensure
    end

    return true
  end

  # ログアウト
  def logout
    @cookie = nil
  end

  def login?
    return !@cookie.nil?
  end

  def get_session_id
    if @cookie.nil?
      nil
    else
      @cookie['Login']
    end
  end

  def set_session_id(session_id)
    @cookie = Hash.new
    @cookie['Login'] = session_id.dup
  end

  # グループリスト
  def get_group_list()
    groups = Array.new
    p request('ExternalAPIGroupsList')
    csv = CSV.parse(CybozuClient.real_request(@server_uri, 'ExternalAPIGroupsList'))
    csv.each do |line|
      groups.push CybozuGroup.new(line[1].to_i, line[2], line[3..-1].collect{|elem| elem.to_i})
    end

    groups
  end

  # ユーザリスト
  def get_user_list()
    users = Array.new
    csv = CSV.parse(request('ExternalAPIGroupsList'))
    csv.each do |line|
      users.push CybozuUser.new(line[1].to_i, line[2], line[3..-1].collect{|elem| elem.to_i})
    end

    users
  end

  # スケジュールデータ取得
  def get_schedule()
    if @cookie.nil?
      return
    end
    return CSV.parse(request('ExternalAPISchedule').toutf8)
  end

  # システム情報取得
  def get_system_info()
    if @cookie.nil?
      return
    end
    return CSV.parse(request('ExternalAPISystemInfo').toutf8)
  end

  # 移動先情報取得
  def get_where()
    if @cookie.nil?
      return
    end
    return CSV.parse(request('ExternalAPIWhere').toutf8)
  end

  # タイムカード取得
  def get_timecard(date_start, date_end)
    params = {
      'Page' => 'PersonalTimeCardEx',
      'uid' => @cookie['User'],
      'SetDate.Year' => 2006,
      'SetDate.Month' => 5,
      'SetDate.Day' => 1,
      'EndDate.Year' => 2006,
      'EndDate.Month' => 5,
      'EndDate.Day' => 31,
    }
  end

  private
  # リクエスト
  def request(func, request_params=nil)
    CybozuClient.real_request(@server_uri, func, request_params, @cookie)
  end

  def self.real_request(server_uri, func, request_params = nil, cookie = nil)
    params = request_params.dup unless request_params.nil?
    params ||= Hash.new
    params['page'] = func

    headers = CybozuClient.build_params(cookie, ';') unless cookie.nil?

    uri = server_uri.dup
    uri.query = CybozuClient.build_params(params)


    res = Net::HTTP.get(uri.host, uri.request_uri, headers)

    res.body[0..res.body.index('<font size=')-1].dup.untaint
  end

  # パラメータ文字列の生成
  def self.build_params(params, param_separator = '&')
    ret = ''
    params.each_pair do |key,value|
      unless ret == ''
        ret.concat param_separator
      end
      ret.concat "#{key}=#{value}"
    end
    ret
  end

  # set-cookieの解析
  def parse_cookies(cookies)
    if cookies.nil?
      return nil
    end

    # cookies are array
    ret = {}
    cookies.each do |c|
      cookie_elem = c.split(/;/)
      key,value = cookie_elem[0].split(/=/)
      ret[key] = value
    end
    ret
  end
end

# テスト
if __FILE__ == $0
  server_uri = 'http://cybozu.example.com/cgi-bin/ag.cgi'
  if ARGV.length < 2
    puts "usage: cybozu_client.rb <user_id> <password>"
    exit
  else
    user_id = ARGV[0].to_i
    password = ARGV[1]
  end

  client = CybozuClient.new(server_uri)

  begin
    client.login(user_id, password) do
      client.get_group_list().each do |elem|
        puts "#{elem.group_id}\t#{elem.name}\t#{elem.members.join(',')}"
      end
      puts client.get_system_info()
      puts
      puts client.get_where()
      puts
      puts client.get_schedule()
    end
  rescue =>e
    puts "Login failure: #{e}"
  end
end
