<?php
class AddRoomTransaction extends Transaction {
  /**
   * 新しい部屋を建てられるかどうかを示す値を取得します。
   * @param ChatEngine $db データベース接続を提供するChatEngineオブジェクト
  */
  function _AllowAddRoom($db) {
    global $ROOM_CONF;
    if (($rooms = $db->GetActiveRooms()) === false) {
      return false;
    }
    if (count($rooms) < $ROOM_CONF->max_active_room) {
      foreach($rooms as $room) {
        if ($room->builder_ip == $_SERVER['REMOTE_ADDR']) {
          return false;
        }
      }
      return true;
    }
    return false;
  }
  /**
   * 名前と説明文を指定して新しいチャットルームを作成します。
   * @param ChatEngine $db データベース接続を提供するChatEngineオブジェクト
   * @param string $name 部屋の名前
   * @param string $comment 部屋の説明文
   * @param integer $max_users 部屋の最大人数
   * @param string $game_option ゲームオプション
   * @param string $option_role 配役オプション
   * @return ChatRoom|bool 部屋の作成に成功した場合ChatRoomオブジェクト、それ以外の場合false
   */
  function _CreateRoom($db, $name, $comment, $max_users, $game_option, $option_role) {
    if (!(validateText($name, false, 1, 255) && validateText($comment, false, 1, 255))) {
      return false;
    }
    elseif ($max_users < 4) {
      //4人未満だと初日の朝の時点で村狼となり勝負にならない
      return false;
    }
    $sql = <<<SQL
INSERT INTO jinrou_rooms (name, comment, max_users, builder_ip, game_option, option_role, built_time)
VALUES (:name, :comment, :max_users, :builder_ip, :game_option, :option_role, NOW())
SQL;
    $stmt = $db->prepare($sql);
    $bindings = array(
      ':name' => $name,
      ':comment' => $comment,
      ':max_users' => $max_users,
      ':builder_ip' => $_SERVER['REMOTE_ADDR'],
      ':game_option' => $game_option,
      ':option_role' => $option_role,
    );
    if ($stmt !== false && $stmt->execute($bindings)) {
      $id = $db->lastInsertId();
      $result = $db->query("SELECT * FROM jinrou_rooms WHERE id = {$id}");
      if ($result !== false) {
        $rooms = $result->fetchAll(PDO::FETCH_CLASS, ChatEngine::CLASS_ROOM, array($db));
        if ($rooms !== false && 0 < count($rooms)) {
          return $rooms[0];
        }
      }
    }
    return false;
  }

  /**
   * 身代わり君を部屋に追加します。
   * @param ChatEngine $db データベース接続を提供するChatEngineオブジェクト
   * @param ChatRoom $room 追加する部屋を表すChatRoomオブジェクト
   * @return ChatUser|boolean 追加された身代わり君のプレイヤー情報。身代わり君が必要ない場合nullが返ります。
   * また、失敗した場合、このメソッドはfalseを返します。
   */
  function _RegisterDummyBoy($db, $room) {
    global $SERVER_CONF, $RQ_ARGS;
    $options = $room->game_option;
    $is_dummy = in_array('dummy_boy', $options);
    $is_quiz = in_array('special_role:quiz', $options);
    $is_gm = $is_quiz || in_array('gm_login', $options);

    $add_user = new AddUserTransaction();
    if ($is_dummy) {
      $add_user->uname = 'dummy_boy';
      $add_user->passwd = $SERVER_CONF->system_password;
      $add_user->handle_name = '身代わり君';
      $add_user->icon_no = 0;
    }
    elseif ($is_gm) {
      $add_user->uname = $RQ_ARGS->gm_uname;
      $add_user->passwd = $RQ_ARGS->gm_password;
      $add_user->handle_name = $RQ_ARGS->gm_handle;
      $add_user->icon_no = $RQ_ARGS->gm_icon_no;
    }
    else {
      return null;
    }
    $add_user->room_no = $room->id;
    $add_user->profile = '人狼?そんなのいるわけないじゃん。';
    $add_user->sex = 'male'; //一応ゲルト君に合わせておく (2011-01-18 enogu)
    if ($is_quiz) {
      $add_user->role = 'quiz';
    }
    else {
      $add_user->role = '';
    }
    return $add_user->__run($db);
  }

  /**
   * 村作成を通知するメッセージを追加します。
   * @param ChatEngine $db データベース接続を提供するChatEngineオブジェクト
   * @param ChatRoom $room 追加する部屋を表すChatRoomオブジェクト
   * @return boolean 追加に成功した場合true、それ以外の場合falseが返されます。
   */
  function _AddAlarm($db, $room) {
    $sql = <<<SQL
INSERT INTO jinrou_talk (room, player, internal_time, posted_time, channel, type, volume, sentence)
VALUES (:room_id, 0, 0, UNIX_TIMESTAMP(), 'alarm', 'room_open', '', 'ALARM_OPEN')

SQL;
    if (($stmt = $db->prepare($sql)) !== false) {
      $stmt->bindValue(':room_id', $room->id, PDO::PARAM_INT);
      return $stmt->execute();
    }
    return false;
  }

  /**
   * フォームによってリクエストされたオプションを内部形式に変換します。
   * @param array $options オプションを格納した連想配列
   */
  function _ConvertOptions($options) {
    $result = array('game_option' => array(), 'option_role' => array());
    if (intval($options['real_time_day']) == 0 || intval($options['real_time_night']) == 0) {
      echo "invalid format : day={$options['real_time_day']}, night={$options['real_time_night']}";
      return false;
    }
    try {
      self::_StoreOption($result, self::_ConvertNoneSwitch($options, 'real_time', array(null, '', 'on', true, false), 'real_time_day', 'real_time_night'));
      self::_StoreOption($result, self::_ConvertNoneSwitch($options, 'not_open_cast', array('', 'full', 'auto')));
      self::_StoreOption($result, self::_ConvertNoneSwitch($options, 'replace_human', array('', 'full_mania', 'full_chiroptera', 'full_cupid', 'replace_human')));
      self::_StoreOption($result, self::_ConvertNoneSwitch($options, 'special_role', array('', 'chaos', 'chaosfull', 'chaos_hyper', 'gray_random', 'quiz')));
      self::_StoreOption($result, self::_ConvertNoneSwitch($options, 'topping', array('', 'a', 'b', 'c', 'd', 'e')));
      self::_StoreOption($result, self::_ConvertNoneSwitch($options, 'chaos_open_cast', array('', 'camp', 'role', 'full')));
      foreach ($options as $key => $value) {
          self::_StoreOption($result, self::_ParseSwitch($value) ? $key : false);
      }
    }
    catch (Exception $e) {
      echo "an error has ocurred when parsing {$key}:", $e->getMessage(), "\n";
      return false;
    }
    return $result;
  }

  private static function _StoreOption(&$result, $value) {
    if (!empty($value)) {
      if (strpos($value, 'role_') === 0) {
        $result['option_role'][] = $value;
      }
      else {
        $result['game_option'][] = $value;
      }
    }
  }

  private static function _ConvertNoneSwitch(&$source, $title, $expected) {
    $value = isset($source[$title]) ? $source[$title] : null;
    unset($source[$title]);
    if (isset($expected)) {
      if (!in_array($value, $expected, true)) {
        throw new Exception("{$value} is unexpected value for {$title}.");
      }
    }
    $keys = array_slice(func_get_args(), 3);
    if (count($keys) == 0) {
      $values = array($title, $value);
    }
    else {
      $values = array($title);
      foreach($keys as $key) {
        $values[] = $source[$key];
        unset($source[$key]);
      }
      //入力候補を事前にフィルタできるため、ここではキャッチしない。
      if (!self::_ParseSwitch($value)) {
        $values = array();
      }
    }
    return implode(':', $values);
  }

  private static function _ParseSwitch($value) {
    if (is_bool($value)) {
      return $value;
    }
    elseif ($value == 'on') {
      return true;
    }
    elseif (!empty($value)) {
      //Note:予期しない値が入っている
      throw new Exception("{$value} is invalid.");
    }
    return false;
  }

  function __run($db) {
    global $RQ_ARGS;
    if ($this->_AllowAddRoom($db)) {
      $options = $this->_ConvertOptions(array_diff_key(
        $RQ_ARGS->ToArray(),
        array_flip(array('name', 'comment', 'max_user', 'gm_uname', 'gm_handle', 'gm_password'))
      ));
      $game_option = implode(',', $options['game_option']);
      $option_role = implode(',', $options['option_role']);
      $room = $this->_CreateRoom($db, $this->name, $this->comment, $this->max_user, $game_option, $option_role);
      if ($room !== false) {
        $dummy_boy = _RegisterDummyBoy($db, $room);
        if ($dummy_boy !== false && $this->_AddAlarm($db, $room)) {
          return $room;
        }
      }
    }
    return false;
  }
}
