/*! \brief Called when a player asks to select a kart.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0   1            5                    6           N+6
 *       ---------------------------------------------------
 *  Size | 1 |      4     |          1         |     N     |
 *  Data | 4 | priv token | N (kart name size) | kart name |
 *       ---------------------------------------------------
 */
void ServerLobbyRoomProtocol::kartSelectionRequested(Event* event)
{
    NetworkString data = event->data();
    STKPeer* peer = *(event->peer);
    if (!checkDataSizeAndToken(event, 6))
        return;

    uint8_t kart_name_size = data.gui8(5);
    std::string kart_name = data.gs(6, kart_name_size);
    if (kart_name.size() != kart_name_size)
    {
        Log::error("ServerLobbyRoomProtocol", "Kart names sizes differ: told:"
                   "%d, real: %d.", kart_name_size, kart_name.size());
        return;
    }
    // check if selection is possible
    if (!m_selection_enabled)
    {
        NetworkString answer;
        answer.ai8(0x82).ai8(1).ai8(2); // selection still not started
        m_listener->sendMessage(this, peer, answer);
        return;
    }
    // check if somebody picked that kart
    if (!m_setup->isKartAvailable(kart_name))
    {
        NetworkString answer;
        answer.ai8(0x82).ai8(1).ai8(0); // kart is already taken
        m_listener->sendMessage(this, peer, answer);
        return;
    }
    // check if this kart is authorized
    if (!m_setup->isKartAllowed(kart_name))
    {
        NetworkString answer;
        answer.ai8(0x82).ai8(1).ai8(1); // kart is not authorized
        m_listener->sendMessage(this, peer, answer);
        return;
    }
    // send a kart update to everyone
    NetworkString answer;
    // kart update (3), 1, race id
    answer.ai8(0x03).ai8(1).ai8(peer->getPlayerProfile()->race_id);
    //  kart name size, kart name
    answer.ai8(kart_name.size()).as(kart_name);
    m_listener->sendMessage(this, answer);
    m_setup->setPlayerKart(peer->getPlayerProfile()->race_id, kart_name);
}
void ServerLobbyRoomProtocol::kartDisconnected(Event* event)
{
    STKPeer* peer = *(event->peer);
    if (peer->getPlayerProfile() != NULL) // others knew him
    {
        NetworkString msg;
        msg.ai8(0x02).ai8(1).ai8(peer->getPlayerProfile()->race_id);
        m_listener->sendMessage(this, msg);
        Log::info("ServerLobbyRoomProtocol", "Player disconnected : id %d",
                  peer->getPlayerProfile()->race_id);
        m_setup->removePlayer(peer->getPlayerProfile()->race_id);
        NetworkManager::getInstance()->removePeer(peer);
    }
    else
        Log::info("ServerLobbyRoomProtocol", "The DC peer wasn't registered.");
}
void ServerLobbyRoomProtocol::kartDisconnected(Event* event)
{
    STKPeer* peer = event->getPeer();
    if (peer->getPlayerProfile() != NULL) // others knew him
    {
        NetworkString msg(3);
        msg.ai8(LE_PLAYER_DISCONNECTED).ai8(1)
           .ai8(peer->getPlayerProfile()->getGlobalPlayerId());
        sendMessage(msg);
        Log::info("ServerLobbyRoomProtocol", "Player disconnected : id %d",
                  peer->getPlayerProfile()->getGlobalPlayerId());
        m_setup->removePlayer(peer->getPlayerProfile());
        // Remove the profile from the peer (to avoid double free)
        peer->setPlayerProfile(NULL);
        STKHost::get()->removePeer(peer);
    }
    else
        Log::info("ServerLobbyRoomProtocol", "The DC peer wasn't registered.");
}   // kartDisconnected
/*! \brief Called when a player asks to select a kart.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0   1            5                    6           N+6
 *       ---------------------------------------------------
 *  Size | 1 |      4     |          1         |     N     |
 *  Data | 4 | priv token | N (kart name size) | kart name |
 *       ---------------------------------------------------
 */
void ServerLobbyRoomProtocol::kartSelectionRequested(Event* event)
{
    if(m_state!=SELECTING_KARTS)
    {
        Log::warn("Server", "Received kart selection while in state %d.",
                  m_state);
        return;
    }

    const NetworkString &data = event->data();
    STKPeer* peer = event->getPeer();
    if (!checkDataSizeAndToken(event, 6))
        return;

    std::string kart_name;
    data.decodeString(5, &kart_name);
    // check if selection is possible
    if (!m_selection_enabled)
    {
        NetworkString answer(3);
        // selection still not started
        answer.ai8(LE_KART_SELECTION_REFUSED).ai8(1).ai8(2);
        sendMessage(peer, answer);
        return;
    }
    // check if somebody picked that kart
    if (!m_setup->isKartAvailable(kart_name))
    {
        NetworkString answer(3);
        // kart is already taken
        answer.ai8(LE_KART_SELECTION_REFUSED).ai8(1).ai8(0);
        sendMessage(peer, answer);
        return;
    }
    // check if this kart is authorized
    if (!m_setup->isKartAllowed(kart_name))
    {
        NetworkString answer(3);
        // kart is not authorized
        answer.ai8(LE_KART_SELECTION_REFUSED).ai8(1).ai8(1);
        sendMessage(peer, answer);
        return;
    }
    // send a kart update to everyone
    NetworkString answer(3+1+kart_name.size());
    // kart update (3), 1, race id
    uint8_t player_id = peer->getPlayerProfile()->getGlobalPlayerId();
    answer.ai8(LE_KART_SELECTION_UPDATE).ai8(1).ai8(player_id)
          .encodeString(kart_name);
    // This message must be handled synchronously on the client.
    sendSynchronousMessage(answer);
    m_setup->setPlayerKart(player_id, kart_name);
}   // kartSelectionRequested
// ----------------------------------------------------------------------------
bool StartGameProtocol::notifyEventAsynchronous(Event* event)
{
    const NetworkString &data = event->data();
    if (data.size() < 5)
    {
        Log::error("StartGameProtocol", "Too short message.");
        return true;
    }
    uint32_t token = data.gui32();
    uint8_t ready = data.gui8(4);
    STKPeer* peer = event->getPeer();
    if (peer->getClientServerToken() != token)
    {
        Log::error("StartGameProtocol", "Bad token received.");
        return true;
    }
    if (NetworkConfig::get()->isServer() && ready) // on server, player is ready
    {
        Log::info("StartGameProtocol", "One of the players is ready.");
        m_player_states[peer->getPlayerProfile()] = READY;
        m_ready_count++;
        if (m_ready_count == m_game_setup->getPlayerCount())
        {
            // everybody ready, synchronize
            Protocol *p = ProtocolManager::getInstance()
                        ->getProtocol(PROTOCOL_SYNCHRONIZATION);
            SynchronizationProtocol* protocol = 
                                static_cast<SynchronizationProtocol*>(p);
            if (protocol)
            {
                protocol->startCountdown(5000); // 5 seconds countdown
                Log::info("StartGameProtocol",
                          "All players ready, starting countdown.");
                m_ready = true;
                return true;
            }
            else
                Log::error("StartGameProtocol",
                          "The Synchronization protocol hasn't been started.");
        }   // if m_ready_count == number of players
    }
    else // on the client, we shouldn't even receive messages.
    {
        Log::error("StartGameProtocol", "Received a message with bad format.");
    }
    return true;
}   // notifyEventAsynchronous
/*! \brief Called when a player votes for the number of races in a GP.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0   1            5   6             7
 *       ------------------------------------
 *  Size | 1 |      4     | 1 |      1      |
 *  Data | 4 | priv token | 1 | races count |
 *       ------------------------------------
 */
void ServerLobbyRoomProtocol::playerRaceCountVote(Event* event)
{
    NetworkString &data = event->data();
    STKPeer* peer = event->getPeer();
    if (!checkDataSizeAndToken(event, 7))
        return;
    if (!isByteCorrect(event, 5, 1))
        return;
    uint8_t player_id = peer->getPlayerProfile()->getGlobalPlayerId();
    m_setup->getRaceConfig()->setPlayerRaceCountVote(player_id, data[6]);
    // Send the vote to everybody (including the sender)
    data.removeFront(5); // remove the token
    NetworkString other(2+data.size());
    other.ai8(1).ai8(player_id); // add the player id
    other += data; // add the data
    sendMessageToPeersChangingToken(LE_VOTE_RACE_COUNT, other);
}   // playerRaceCountVote
/*! \brief Called when a player votes for a minor race mode.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0   1            5   6                 7
 *       ----------------------------------------
 *  Size | 1 |      4     | 1 |        1        |
 *  Data | 4 | priv token | 1 | minor mode vote |
 *       ----------------------------------------
 */
void ServerLobbyRoomProtocol::playerMinorVote(Event* event)
{
    NetworkString data = event->data();
    STKPeer* peer = *(event->peer);
    if (!checkDataSizeAndToken(event, 7))
        return;
    if (!isByteCorrect(event, 5, 1))
        return;
    uint8_t player_id = peer->getPlayerProfile()->race_id;
    m_setup->getRaceConfig()->setPlayerMinorVote(player_id, data[6]);
    // Send the vote to everybody (including the sender)
    NetworkString other;
    other.ai8(1).ai8(player_id); // add the player id
    data.removeFront(5); // remove the token
    other += data; // add the data
    NetworkString prefix;
    prefix.ai8(0xc2); // prefix the token with the ype
    sendMessageToPeersChangingToken(prefix, other);
}
/*! \brief Called when a player votes for a track.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0   1            5   6            N+6 N+7                 N+8
 *       -----------------------------------------------------------
 *  Size | 1 |      4     | 1 |      N     | 1 |       1           |
 *  Data | 4 | priv token | N | track name | 1 | track number (gp) |
 *       -----------------------------------------------------------
 */
void ServerLobbyRoomProtocol::playerTrackVote(Event* event)
{
    NetworkString &data = event->data();
    STKPeer* peer = event->getPeer();
    if (!checkDataSizeAndToken(event, 8))
        return;
    std::string track_name;
    int N = data.decodeString(5, &track_name);
    if (!isByteCorrect(event, N+5, 1))
        return;
    uint8_t player_id = peer->getPlayerProfile()->getGlobalPlayerId();
    m_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name, data[N+6]);
    // Send the vote to everybody (including the sender)
    data.removeFront(5); // remove the token
    NetworkString other(2+data.size());
    other.ai8(1).ai8(player_id); // add the player id
    other += data; // add the data
    sendMessageToPeersChangingToken(LE_VOTE_TRACK, other);
    if(m_setup->getRaceConfig()->getNumTrackVotes()==m_setup->getPlayerCount())
        startGame();
}   // playerTrackVote