//
// StateRecorder Implementation
//

#include "StateRecorder.hpp"

#include <fstream>
#include <iostream>
#include <stdexcept>

namespace shardok {
namespace ml {

void StateRecorder::RecordStateView(
        const net::eagle0::shardok::api::GameStateView& view,
        PlayerId currentPlayer) {
    // Serialize the protobuf to bytes
    std::vector<uint8_t> serialized(view.ByteSizeLong());
    if (!view.SerializeToArray(serialized.data(), static_cast<int>(serialized.size()))) {
        throw std::runtime_error("Failed to serialize GameStateView");
    }

    // Extract round number from the view
    const int roundNumber = view.current_round();

    // Create and store record
    states_.emplace_back(std::move(serialized), currentPlayer, roundNumber);
}

void StateRecorder::SetGameOutcome(PlayerId winner) {
    winner_ = winner;

    // Label all states based on outcome
    for (auto& state : states_) {
        if (winner == -1) {
            // Draw - nobody won
            state.currentPlayerWon = false;
        } else {
            // Label based on whether this state's player won
            state.currentPlayerWon = (state.currentPlayer == winner);
        }
    }
}

void StateRecorder::WriteToFile(const std::string& filename) const {
    if (winner_ == -1) { throw std::runtime_error("Cannot write to file: game outcome not set"); }

    std::ofstream file(filename, std::ios::binary);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file for writing: " + filename);
    }

    // Write number of records
    const auto numRecords = static_cast<uint32_t>(states_.size());
    file.write(reinterpret_cast<const char*>(&numRecords), sizeof(numRecords));

    // Write each record
    for (const auto& state : states_) {
        // Write size of serialized view
        const auto viewSize = static_cast<uint32_t>(state.serializedStateView.size());
        file.write(reinterpret_cast<const char*>(&viewSize), sizeof(viewSize));

        // Write serialized view bytes
        file.write(
                reinterpret_cast<const char*>(state.serializedStateView.data()),
                static_cast<std::streamsize>(viewSize));

        // Write current player (as int8_t to save space)
        const auto player = static_cast<int8_t>(state.currentPlayer);
        file.write(reinterpret_cast<const char*>(&player), sizeof(player));

        // Write round number
        const auto round = static_cast<int32_t>(state.roundNumber);
        file.write(reinterpret_cast<const char*>(&round), sizeof(round));

        // Write label (bool)
        file.write(reinterpret_cast<const char*>(&state.currentPlayerWon), sizeof(bool));
    }

    file.close();

    if (!file.good()) { throw std::runtime_error("Error writing to file: " + filename); }
}

}  // namespace ml
}  // namespace shardok
