<?php
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';

require_once dirname(__FILE__) . '/../../../../../../include/chatengine/chatengine.php';
require_once CHEN_DIR.'/chatroom_class.php';
require_once CHEN_DIR.'/chatuser_class.php';
require_once CHEN_DIR.'/transaction.php';
require_once CHEN_DIR.'/transaction/add_room.php';
require_once CHEN_DIR.'/transaction/add_user.php';

//現在はinit.phpへの干渉を最小限にしているため、initを先にロードするとroom_classの依存性が解決できない。
require_once dirname(__FILE__) . '/../../../../../../include/init.php';
$INIT_CONF->LoadClass('ROOM_CONF');
require_once JINRO_INC.'/request_class.php';
require_once JINRO_INC.'/user_class.php';
require_once JINRO_INC.'/room_class.php';

/**
 * Test class for AddRoomTransaction.
 * Generated by PHPUnit on 2011-01-01 at 14:56:02.
 */
class AddRoomTransactionTest extends PHPUnit_Framework_TestCase {

  /**
   * @var AddRoomTransaction
   */
  protected $object;

  protected $_chen;
  public function __get($name) {
    switch($name) {
      case 'chen':
        if (!isset($this->_chen)) {
          $this->_chen = new ChatEngine('localhost', 'jinrou', 'grayran', 'satorituri');
          $this->_chen->beginTransaction();
        }
        return $this->_chen;
    }
  }

  /**
   * Sets up the fixture, for example, opens a network connection.
   * This method is called before a test is executed.
   */
  protected function setUp() {
    $this->object = new AddRoomTransaction;
  }

  /**
   * Tears down the fixture, for example, closes a network connection.
   * This method is called after a test is executed.
   */
  protected function tearDown() {
    if (isset($this->_chen)) {
      $this->_chen->rollBack();
    }
    $this->_chen = null;
  }

  /**
   * @dataProvider dp_testAllowAddRoom
   */
  public function testAllowAddRoom($rooms, $success) {
    global $ROOM_CONF;
    //稼働中の村があるとカウントが乱れるため、部屋を全て非アクティブ化している。(2011-01-19 enogu)
    $this->chen->exec('UPDATE jinrou_rooms SET is_active = 0');
    //条件が変化していると面倒なのでここで決めうつ(2011-01-19 enogu)
    $ROOM_CONF->max_active_room = 4;
    //インスタンスの挿入
    $remote_addr = $_SERVER['REMOTE_ADDR'];
    foreach($rooms as $room) {
      extract($room, EXTR_PREFIX_ALL, 'room');
      if (isset($room_builder_ip)) {
        $_SERVER['REMOTE_ADDR'] = $room_builder_ip;
      }
      list($name, $comment) = $room_args;
      $result = $this->object->_CreateRoom($this->chen, $name, $comment, 17, '', '');
      $this->assertNotSame(false, $result, '部屋の追加に失敗しました');
    }
    $added_rooms = $this->chen->GetActiveRooms();
    $this->assertEquals(count($rooms), count($added_rooms) - count($existing_rooms), '追加されている部屋の数が正しくありません');
    $_SERVER['REMOTE_ADDR'] = $remote_addr;
    //実行結果の評価
    $this->assertEquals($success, $this->object->_AllowAddRoom($this->chen), '処理に失敗しました');
  }
  public static function dp_testAllowAddRoom() {
    return array(
        array(
            array(),
            true
        ),
        array(
            array(array('args'=>array('村名', 'コメント'))),
            false
        ),
        array(
            array(array('builder_ip'=>'192.168.0.1', 'args'=>array('村名', 'コメント'))),
            true
        ),
        array(
            array(
              array('builder_ip'=>'192.168.0.1', 'args'=>array('村1', 'コメント')),
              array('builder_ip'=>'192.168.1.1', 'args'=>array('村2', 'コメント')),
              array('builder_ip'=>'192.168.2.1', 'args'=>array('村3', 'コメント')),
            ),
            true
        ),
        array(
            array(
              array('builder_ip'=>'192.168.0.1', 'args'=>array('村1', 'コメント')),
              array('builder_ip'=>'192.168.1.1', 'args'=>array('村2', 'コメント')),
              array('builder_ip'=>'192.168.2.1', 'args'=>array('村3', 'コメント')),
              array('builder_ip'=>'192.168.3.1', 'args'=>array('村4', 'デフォルトの上限')),
            ),
            false
        ),
    );
  }

  /**
   * @dataProvider dp_testCreateRoom
   */
  public function testCreareRoom($name, $comment, $max_users, $success) {
    $result = $this->object->_CreateRoom($this->chen, $name, $comment, $max_users , '', '');
    if ($success) {
      $this->assertNotSame(false, $result, print_r($this->chen->errorInfo(), true));
      $this->assertNotNull($result, print_r($this->chen->errorInfo(), true));
      $this->assertGreaterThan(0, $result->id);
      $this->assertEquals($name, $result->name);
      $this->assertEquals($comment, $result->comment);
      $this->assertEquals($max_users, $result->max_users);
    }
    else {
      $this->assertFalse($result);
    }
  }
  public static function dp_testCreateRoom() {
    return array(
        array('テスト','これは成功する',4,true),
        array(null, '村名がセットされていない時はエラー', 4, false),
        array('', '空の村名はエラー', 4, false),
        array('過疎村','村人の数を3以下にすると初日が来ない', 3, false),
        array('長すぎるテスト村901234567890123456789012345678901234567890長すぎるテスト村901234567890123456789012345678901234567890長すぎるテスト村901234567890123456789012345678901234567890長すぎるテスト村901234567890123456789012345678901234567890長すぎるテスト村901234567890123456789012345678901234567890長すぎるテス', '255文字を超える長さを指定するとエラー', 4, false),
        array('長すぎるコメント村', '255文字を超える長さを指定するとエラー123456789012345678901234567890255文字を超える長さを指定するとエラー123456789012345678901234567890255文字を超える長さを指定するとエラー123456789012345678901234567890255文字を超える長さを指定するとエラー123456789012345678901234567890255文字を超える長さを指定するとエラー123456789012345678901234567890255文字を', 4, false),
        array('コメントがない', null, 4, false),
        array('コメントがない2', '', 4, false)
    );
  }

  /**
   * @dataProvider dp_testRegisterDummyBoy
   */
  public function testRegisterDummyBoy($dummy_mode, $gm_uname, $gm_passwd, $dummy_exists, $add_success, $login_success) {
    global $SERVER_CONF, $RQ_ARGS;
    //デフォルト値を提供する
    $_POST['real_time'] = 'on';
    $_POST['real_time_day'] = 5;
    $_POST['real_time_night'] = 3;
    $_POST['not_open_cast'] = 'auto';
    $_POST['replace_human'] = '';
    $_POST['topping'] = '';
    $_POST['chaos_open_cast'] = '';
	//テスト用の値
    $_POST['gm_uname'] = $gm_uname;
    $_POST['gm_password'] = $gm_passwd;
    $_POST['gm_handle'] = 'GM';
    $_POST['gm_icon_no'] = 0;
    $_POST['dummy_boy'] = $dummy_mode == 'dummy_boy' ? 'on' : '';
    $_POST['gm_login'] = $dummy_mode == 'gm_login' ? 'on' : '';
    $_POST['special_role'] = $dummy_mode == 'quiz' ? 'quiz' : '';
    //リクエストのパース
    $RQ_ARGS = new RequestAddRoom();
    $options = $this->object->_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->object->_CreateRoom($this->chen, '身代わりあり村', 'テスト用', 17, $game_option, $option_role);
    $this->assertNotSame(false, $room, '部屋の準備に失敗しました。');
    $dummy_boy = $this->object->_RegisterDummyBoy($this->chen, $room);
    //正常に追加(またはスルー)できたかチェック
    if (!$add_success) {
      $this->assertSame(false, $dummy_boy);
      return;
    }
    //アラームの追加(この処理を入れないとOpenRoomを利用できない 2011-05-05 enogu)
    $this->object->_AddAlarm($this->chen, $room);
    $this->assertNotSame(false, $dummy_boy);
    if ($dummy_exists) {
      $this->assertNotNull($dummy_boy);
      switch ($dummy_mode) {
        case 'dummy_boy':
          $gm_uname = 'dummy_boy';
          $gm_passwd = $SERVER_CONF->system_password;
          $this->assertEquals('身代わり君', $dummy_boy->handle_name);
          $this->assertEquals(array(), $dummy_boy->role);
          break;
        case 'gm_login':
          $this->assertEquals('GM', $dummy_boy->handle_name);
          $this->assertEquals(array(), $dummy_boy->role);
          break;
        case 'quiz':
          $this->assertEquals('GM', $dummy_boy->handle_name);
          $this->assertEquals(array('quiz'), $dummy_boy->role);
          break;
      }
      //GMとしてログインできるか確認する(unameとパスワードのチェック)
      $room = $this->chen->OpenRoom($room->id, $gm_uname, $gm_passwd);
      $this->assertType($login_success ? ChatEngine::CLASS_ROOM_LOGIN : ChatEngine::CLASS_ROOM_READONLY, $room);
    }
    else {
      $this->assertNull($dummy_boy);
    }
  }
  public static function dp_testRegisterDummyBoy() {
    global $SERVER_CONF;
    return array(
      array('', '', '', false, true, false),
      array('dummy_boy', '', '', true, true, true),
      array('gm_login', 'test', 'passwd', true, true, true),
      array('quiz', 'test', 'passwd', true, true, true),
    );
  }

  /**
   * @dataProvider dp_testConvertOptions
   */
  public function testConvertOptions($options, $success) {
    //パース
    $result = $this->object->_ConvertOptions($options);
    //パース結果の評価
    if ($success) {
      $this->assertNotSame(false, $result, 'パラメータを期待通りに解釈できていません');
      //real_time関連の設定は複雑なため生でテストしている。
      //なお、いちいち$optionsをunsetしているのはスイッチ類は後でまとめてループで検査しているため
      extract($result);
      if ($options['real_time'] == 'on') {
        $expected = "real_time:{$options['real_time_day']}:{$options['real_time_night']}";
        $this->assertContains($expected, $game_option);
      }
      unset($options['real_time']);
      unset($options['real_time_day']);
      unset($options['real_time_night']);
      $this->testNoneSwitchOption($game_option, $options, 'not_open_cast');
      $this->testNoneSwitchOption($game_option, $options, 'replace_human');
      $this->testNoneSwitchOption($game_option, $options, 'special_role');
      $this->testNoneSwitchOption($game_option, $options, 'topping');
      $this->testNoneSwitchOption($game_option, $options, 'chaos_open_cast');
      foreach($options as $key => $value) {
        $src = strpos($key, 'role_') === 0 ? $option_role : $game_option;
        if ($value == 'on') {
          $this->assertContains($key, $src);
        }
      }
    }
    else {
      $this->assertSame(false, $result, '不正なパラメータを拒否できていません');
    }
  }
  /**
   * ON/OFF以外の値を取りうる要素をテストします。
   * @param array $src テスト対象の変換済みオプション配列
   * @param array $options 変換前のオプション
   * @param string $key テスト対象の要素のキー
   */
  private function testNoneSwitchOption($src, &$options, $key) {
    $item = "{$key}:{$options[$key]}";
    $this->assertContains($item, $src);
    unset($options['dummy_boy']);
  }
  public static function dp_testConvertOptions() {
    $items = array(
        'wish_role:on' => array('nullable', ':', 'on:', 'a:fail'),
        'real_time:on' => array('nullable', ':', 'on:', 'a:fail'),
        'real_time_day:5' => array('0:fail', '1:', 'a:fail'),
        'real_time_night:3' => array('0:fail', '1:', 'a:fail'),
        'wait_morning:on' => array('nullable', ':', 'on:', 'a:fail'),
        'open_vote:on' => array('nullable', ':', 'on:', 'a:fail'),
        'open_day:on' => array('nullable', ':', 'on:', 'a:fail'),
        'dummy_boy:on' => array('nullable', ':', 'on:', 'a:fail'),
        'gm_login:' => array('nullable', ':', 'on:', 'a:fail'),
        'gerd:on' => array('nullable', ':', 'on:', 'a:fail'),
        'not_open_cast:auto' => array('notnull', ':', 'full:', 'auto:', 'a:fail'),
        'role_poison:on' => array('nullable', ':', 'on:', 'a:fail'),
        'role_assassin:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_boss_wolf:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_poison_wolf:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_prossessed_wolf:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_sirius_wolf:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_cupid:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_medium:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_mania:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_decide:on' => array('nullable', ':', 'on:', 'a:fail'),
        'role_authority:on' => array('nullable', ':', 'on:', 'a:fail'),
        'role_lier:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_gentleman:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_sudden_death:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_perverseness:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_deep_sleep:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_mind_open:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_blinder:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_critical:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_joker:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_detective:' => array('nullable', ':', 'on:', 'a:fail'),
        'role_festival:' => array('nullable', ':', 'on:', 'a:fail'),
        'replace_human:' => array('notnull', ':', 'full_mania:', 'full_chiroptera:', 'full_cupid:', 'replace_human:', 'a:fail'),
        'special_role:' => array('notnull', ':', 'chaos:', 'chaosfull:', 'chaos_hyper:', 'gray_random:', 'quiz:', 'a:fail'),
        'topping:' => array('notnull', ':', 'a:', 'b:', 'c:', 'd:', 'e:', 'f:fail'),
        'chaos_open_cast:' => array('notnull', ':', 'camp:', 'role:', 'full:', 'a:fail'),
        'secret_sub_role:' => array('nullable', ':', 'on:', 'a:fail')
    );
    return self::generateOptionArray($items);
  }
  public static function generateOptionArray($options) {
    $defaults = array();
    $values = array();
    foreach ($options as $defn => $list) {
      list($key, $default) = explode(':', $defn);
      $defaults[$key] = $default;
      foreach ($list as $item) {
        if ($item == 'nullable') {
          $value = null;
          $success = '';
        }
        elseif ($item == 'notnull') {
          $value = null;
          $success = 'fail';
        }
        else {
          list($value, $success) = explode(':', $item);
        }
        $success = $success != 'fail' ? 1 : 0;
        $values[] = compact('key', 'value', 'success');
      }
    }
    $result = array();
    foreach ($values as $_value) {
      $item = $defaults;
      extract($_value);
      $item[$key] = $value;
      $result[] = array($item, $success);
    }
    return $result;
  }

  public function testAddAlarm() {
    //インスタンスの追加
    $add_room = new AddRoomTransaction();
    $room = $add_room->_CreateRoom($this->chen, 'testAddAlarm', 'アラームの追加テスト', 17, '', '');
    $success = $this->object->_AddAlarm($this->chen, $room);
    //結果の評価
    $this->assertTrue($success, 'アラームの追加に失敗しました。');
    $stmt = $this->chen->prepare("SELECT * FROM jinrou_talk WHERE room = :room_id AND channel = 'alarm'");
    $this->assertTrue($stmt->execute(array(':room_id' => $room->id)));
    $alarms = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $this->assertNotSame(false, $alarms);
    $this->assertEquals(1, count($alarms), '予期しない数のアラームメッセージが登録されました');
    extract($alarms[0], EXTR_PREFIX_ALL, 'alarm');
    $this->assertEquals(0, $alarm_internal_time, '予期しないゲーム内時間です');
    $this->assertEquals('room_open', $alarm_type, '予期しないアラームタイプです');
    $this->assertEquals('ALARM_OPEN', $alarm_sentence, '予期しないメッセージです');
  }

  /**
   * @todo Implement test__run().
   */
  public function test__run() {
    // Remove the following lines when you implement this test.
    $this->markTestIncomplete(
            'This test has not been implemented yet.'
    );
  }

}

?>
