/** Saves the replay data stored in the internal data structures.
 */
void ReplayRecorder::Save()
{
#ifdef DEBUG
    printf("%d frames, %d removed because of frequency compression\n",
           m_count, m_count_skipped_time);
#endif
    FILE *fd = openReplayFile(/*writeable*/true);
    if(!fd)
    {
        Log::error("ReplayRecorder", "Can't open '%s' for writing - can't save replay data.",
                   getReplayFilename().c_str());
        return;
    }

    Log::info("ReplayRecorder", "Replay saved in '%s'.\n", getReplayFilename().c_str());

    World *world   = World::getWorld();
    unsigned int num_karts = world->getNumKarts();
    fprintf(fd, "Version:  %d\n",   getReplayVersion());
    fprintf(fd, "difficulty: %d\n", race_manager->getDifficulty());
    fprintf(fd, "track: %s\n",      world->getTrack()->getIdent().c_str());
    fprintf(fd, "Laps: %d\n",       race_manager->getNumLaps());

    unsigned int max_frames = (unsigned int)(  stk_config->m_replay_max_time 
                                             / stk_config->m_replay_dt      );
    for(unsigned int k=0; k<num_karts; k++)
    {
        fprintf(fd, "model: %s\n", world->getKart(k)->getIdent().c_str());
        fprintf(fd, "size:     %d\n", m_count_transforms[k]);

        unsigned int num_transforms = std::min(max_frames,
                                               m_count_transforms[k]);
        for(unsigned int i=0; i<num_transforms; i++)
        {
            const TransformEvent *p=&(m_transform_events[k][i]);
            fprintf(fd, "%f  %f %f %f  %f %f %f %f\n",
                    p->m_time,
                    p->m_transform.getOrigin().getX(),
                    p->m_transform.getOrigin().getY(),
                    p->m_transform.getOrigin().getZ(),
                    p->m_transform.getRotation().getX(),
                    p->m_transform.getRotation().getY(),
                    p->m_transform.getRotation().getZ(),
                    p->m_transform.getRotation().getW()
                );
        }   // for i
        fprintf(fd, "events: %d\n", (int)m_kart_replay_event[k].size());
        for(unsigned int i=0; i<m_kart_replay_event[k].size(); i++)
        {
            const KartReplayEvent *p=&(m_kart_replay_event[k][i]);
            fprintf(fd, "%f %d\n", p->m_time, p->m_type);
        }
    }
    fclose(fd);
}   // Save
//-----------------------------------------------------------------------------
bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay)
{
    // custom_replay is true when full path of filename is given
    m_custom_replay_file = custom_replay;

    char s[1024], s1[1024];
    if (StringUtils::getExtension(fn) != "replay") return false;
    FILE *fd = fopen(custom_replay ? fn.c_str() :
        (file_manager->getReplayDir() + fn).c_str(), "r");
    if (fd == NULL) return false;
    ReplayData rd;

    rd.m_filename = fn;

    fgets(s, 1023, fd);
    unsigned int version;
    if (sscanf(s,"version: %u", &version) != 1)
    {
        Log::warn("Replay", "No Version information "
                  "found in replay file (bogus replay file).");
        fclose(fd);
        return false;
    }
    if (version != getReplayVersion())
    {
        Log::warn("Replay", "Replay is version '%d'", version);
        Log::warn("Replay", "STK version is '%d'", getReplayVersion());
        Log::warn("Replay", "Skipped '%s'", fn.c_str());
        fclose(fd);
        return false;
    }

    while(true)
    {
        fgets(s, 1023, fd);
        core::stringc is_end(s);
        is_end.trim();
        if (is_end == "kart_list_end") break;
        char s1[1024];

        if (sscanf(s,"kart: %s", s1) != 1)
        {
            Log::warn("Replay", "Could not read ghost karts info!");
            break;
        }
        rd.m_kart_list.push_back(std::string(s1));
    }

    int reverse = 0;
    fgets(s, 1023, fd);
    if(sscanf(s, "reverse: %d", &reverse) != 1)
    {
        Log::warn("Replay", "Reverse info found in replay file.");
        fclose(fd);
        return false;
    }
    rd.m_reverse = reverse != 0;

    fgets(s, 1023, fd);
    if (sscanf(s, "difficulty: %u", &rd.m_difficulty) != 1)
    {
        Log::warn("Replay", " No difficulty found in replay file.");
        fclose(fd);
        return false;
    }

    fgets(s, 1023, fd);
    if (sscanf(s, "track: %s", s1) != 1)
    {
        Log::warn("Replay", "Track info not found in replay file.");
        fclose(fd);
        return false;
    }
    rd.m_track_name = std::string(s1);
    Track* t = track_manager->getTrack(rd.m_track_name);
    if (t == NULL)
    {
        Log::warn("Replay", "Track '%s' used in replay not found in STK!",
        rd.m_track_name.c_str());
        fclose(fd);
        return false;
    }

    fgets(s, 1023, fd);
    if (sscanf(s, "laps: %u", &rd.m_laps) != 1)
    {
        Log::warn("Replay", "No number of laps found in replay file.");
        fclose(fd);
        return false;
    }

    fgets(s, 1023, fd);
    if (sscanf(s, "min_time: %f", &rd.m_min_time) != 1)
    {
        Log::warn("Replay", "Finish time not found in replay file.");
        fclose(fd);
        return false;
    }
    fclose(fd);
    m_replay_file_list.push_back(rd);

    assert(m_replay_file_list.size() > 0);
    // Force to use custom replay file immediately
    if (custom_replay)
        m_current_replay_file = m_replay_file_list.size() - 1;

    return true;

}   // addReplayFile
/** Saves the replay data stored in the internal data structures.
 */
void ReplayRecorder::save()
{
    if (m_incorrect_replay || !m_complete_replay)
    {
        MessageQueue::add(MessageQueue::MT_ERROR,
            _("Incomplete replay file will not be saved."));
        return;
    }

#ifdef DEBUG
    Log::debug("ReplayRecorder", "%d frames, %d removed because of"
        "frequency compression", m_count, m_count_skipped_time);
#endif
    const World *world           = World::getWorld();
    const unsigned int num_karts = world->getNumKarts();
    float min_time = 99999.99f;
    for (unsigned int k = 0; k < num_karts; k++)
    {
        if (world->getKart(k)->isGhostKart()) continue;
        float cur_time = world->getKart(k)->getFinishTime();
        if (cur_time < min_time)
            min_time = cur_time;
    }

    int day, month, year;
    StkTime::getDate(&day, &month, &year);
    std::string time = StringUtils::toString(min_time);
    std::replace(time.begin(), time.end(), '.', '_');
    std::ostringstream oss;
    oss << Track::getCurrentTrack()->getIdent() << "_" << year << month << day
        << "_" << num_karts << "_" << time << ".replay";
    m_filename = oss.str();

    FILE *fd = openReplayFile(/*writeable*/true);
    if (!fd)
    {
        Log::error("ReplayRecorder", "Can't open '%s' for writing - "
            "can't save replay data.", getReplayFilename().c_str());
        return;
    }

    core::stringw msg = _("Replay saved in \"%s\".",
        (file_manager->getReplayDir() + getReplayFilename()).c_str());
    MessageQueue::add(MessageQueue::MT_GENERIC, msg);

    fprintf(fd, "version: %d\n",    getReplayVersion());
    for (unsigned int real_karts = 0; real_karts < num_karts; real_karts++)
    {
        const AbstractKart *kart = world->getKart(real_karts);
        if (kart->isGhostKart()) continue;

        // XML encode the username to handle Unicode
        fprintf(fd, "kart: %s %s\n", kart->getIdent().c_str(),
                StringUtils::xmlEncode(kart->getController()->getName()).c_str());
    }

    fprintf(fd, "kart_list_end\n");
    fprintf(fd, "reverse: %d\n",    (int)race_manager->getReverseTrack());
    fprintf(fd, "difficulty: %d\n", race_manager->getDifficulty());
    fprintf(fd, "track: %s\n",      Track::getCurrentTrack()->getIdent().c_str());
    fprintf(fd, "laps: %d\n",       race_manager->getNumLaps());
    fprintf(fd, "min_time: %f\n",   min_time);

    unsigned int max_frames = (unsigned int)(  stk_config->m_replay_max_time 
                                             / stk_config->m_replay_dt      );
    for (unsigned int k = 0; k < num_karts; k++)
    {
        if (world->getKart(k)->isGhostKart()) continue;
        fprintf(fd, "size:     %d\n", m_count_transforms[k]);

        unsigned int num_transforms = std::min(max_frames,
                                               m_count_transforms[k]);
        for (unsigned int i = 0; i < num_transforms; i++)
        {
            const TransformEvent *p  = &(m_transform_events[k][i]);
            const PhysicInfo *q      = &(m_physic_info[k][i]);
            const KartReplayEvent *r = &(m_kart_replay_event[k][i]);
            fprintf(fd, "%f  %f %f %f  %f %f %f %f  %f  %f  %f %f %f %f  %d %d %d %d %d\n",
                    p->m_time,
                    p->m_transform.getOrigin().getX(),
                    p->m_transform.getOrigin().getY(),
                    p->m_transform.getOrigin().getZ(),
                    p->m_transform.getRotation().getX(),
                    p->m_transform.getRotation().getY(),
                    p->m_transform.getRotation().getZ(),
                    p->m_transform.getRotation().getW(),
                    q->m_speed,
                    q->m_steer,
                    q->m_suspension_length[0],
                    q->m_suspension_length[1],
                    q->m_suspension_length[2],
                    q->m_suspension_length[3],
                    r->m_nitro_usage,
                    (int)r->m_zipper_usage,
                    r->m_skidding_state,
                    (int)r->m_red_skidding,
                    (int)r->m_jumping
                );
        }   // for i
    }
    fclose(fd);
}   // save