Snaparazzi/Assets/Scripts/VotingPage.cs

243 lines
9.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Firebase.Database;
using TMPro;
using UnityEngine;
public class VotingPage : MonoBehaviour
{
[Header("Scene References")]
public PropositionFrame proposition1;
public PropositionFrame proposition2;
public TextMeshProUGUI currentPromptLabel;
public TextMeshProUGUI timerLabel;
[Header("Asset References")]
public PromptList promptList;
[Header("Settings")]
public float votingTime = 20;
private Queue<Question> remainingQuestions = new Queue<Question>();
private Question currentQuestion = null;
private DatabaseReference roomRef;
private Dictionary<string, Player> currentPlayers; // All the current playing players
private DateTime endOfTimer = DateTime.MinValue;
private Action OnVoteEnded;
private bool allPlayersHaveVotedForCurrentQuestion = false;
/// <summary>
/// Is the number of questions for this game sessions
/// </summary>
private int numberOfQuestions = 0;
private void Update()
{
// If there is no question displayed or the timer is not started, return early.
if (currentQuestion == null || endOfTimer == DateTime.MinValue)
return;
if (DateTime.Now < endOfTimer)
{
TimeSpan duration = endOfTimer - DateTime.Now;
timerLabel.text = ((int)duration.TotalSeconds).ToString("D2");
if (allPlayersHaveVotedForCurrentQuestion && endOfTimer > DateTime.Now.AddSeconds(4))
{
Debug.Log("All players have voted. So I put only 4sec to look at the winner.", this);
endOfTimer = DateTime.Now.AddSeconds(4);
}
}
else
{
EndCurrentQuestion();
}
}
/// <summary>
/// End current question and timer and go to next question (or end the vote if there is no more questions)
/// </summary>
private void EndCurrentQuestion()
{
timerLabel.text = "0";
if (remainingQuestions.Count > 0)
{
currentQuestion = remainingQuestions.Dequeue();
ShowNextQuestion();
}
else
{
OnVoteEnded?.Invoke();
OnVoteEnded = null;
gameObject.SetActive(false);
for (int i = 0; i < numberOfQuestions; i++)
{
roomRef.Child("questions").Child(i.ToString()).ValueChanged -= OnQuestionDataUpdate;
}
}
}
/// <summary>
/// Initializes the voting page with necessary data, such as the room reference, current players,
/// questions, and a callback function for when the voting ends.
/// </summary>
/// <param name="_roomRef">Reference to the Firebase room database.</param>
/// <param name="_currentPlayers">Dictionary of current players.</param>
/// <param name="_questions">Dictionary of questions associated with their IDs.</param>
/// <param name="_callback_OnVoteEnded">Callback function to invoke when the voting ends.</param>
public void ShowVotingPage(DatabaseReference _roomRef, Dictionary<string, Player> _currentPlayers, Dictionary<int, Question> _questions, Action _callback_OnVoteEnded)
{
Debug.Log("Initializing voting page");
roomRef = _roomRef;
currentPlayers = _currentPlayers;
OnVoteEnded = _callback_OnVoteEnded;
allPlayersHaveVotedForCurrentQuestion = false;
endOfTimer = DateTime.MinValue;
remainingQuestions.Clear();
currentQuestion = null;
Question[] _questionArray = _questions.Values.ToArray();
numberOfQuestions = _questionArray.Length;
for (int i = 0; i < _questionArray.Length; i++)
{
remainingQuestions.Enqueue(_questionArray[i]);
roomRef.Child("questions").Child(i.ToString()).ValueChanged += OnQuestionDataUpdate;
}
gameObject.SetActive(true);
currentQuestion = remainingQuestions.Dequeue();
ShowNextQuestion();
}
/// <summary>
/// Displays the next question on the screen, updating the prompt, timer, and proposition frames.
/// </summary>
private void ShowNextQuestion()
{
// Set the current question ID in the Firebase room database.
roomRef.Child("currentQuestionId").SetValueAsync(currentQuestion.index);
// Update the prompt text based on the current question's prompt ID.
Debug.Log($"Show Question {currentQuestion.promptId}", this);
Prompt currentPrompt = promptList.GetPromptById(currentQuestion.promptId);
currentPromptLabel.text = RoomManager.Instance.myRoom.promptsLanguage == "en" ? currentPrompt.en : currentPrompt.fr;
Debug.Log($"Prompt is {currentPromptLabel.text}", this);
// Initialize the proposition frames with proposition data.
InitializeProposition(proposition1, currentQuestion.propositions[0]);
InitializeProposition(proposition2, currentQuestion.propositions[1]);
// Set the timer to end after the specified voting time.
endOfTimer = DateTime.Now.AddSeconds(votingTime);
//reset the count of voters
allPlayersHaveVotedForCurrentQuestion = false;
}
/// <summary>
/// Initializes a proposition frame with the provided proposition data, updating it if available,
/// or handling the case when there is no proposition.
/// </summary>
/// <param name="proposition">The PropositionFrame to initialize or update.</param>
/// <param name="propositionData">The Proposition data to use for initialization or update.</param>
private void InitializeProposition(PropositionFrame proposition, Proposition propositionData)
{
if (propositionData != null)
{
proposition.SetProposition(propositionData);
}
else
{
Debug.Log("User has given no proposition", this);
proposition.SetNoProposition();
}
}
/// <summary>
/// Handles updates to a question, such as when someone has voted for it.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="_questionRef">The ValueChangedEventArgs containing the updated question reference.</param>
private void OnQuestionDataUpdate(object sender, ValueChangedEventArgs _questionRef)
{
string JSON = _questionRef.Snapshot.GetRawJsonValue();
if (string.IsNullOrEmpty(JSON))
return;
Question question = Newtonsoft.Json.JsonConvert.DeserializeObject<Question>(JSON);
// If someone succeeded in voting for another question than the one displayed, ignore it.
if (question.index != currentQuestion.index)
return;
currentQuestion.propositions = question.propositions;
if (currentQuestion.propositions.Count > 0)
{
// Update the proposition voters for both propositions.
UpdatePropositionVoters(proposition1, currentQuestion.propositions[0]);
UpdatePropositionVoters(proposition2, currentQuestion.propositions[1]);
}
//every players BUT the one who have created the propositions
if (RoomManager.Instance.myRoom.NumbersOfVotersForQuestion(currentQuestion) >= currentPlayers.Count - 2)
{
allPlayersHaveVotedForCurrentQuestion = true;
}
}
/// <summary>
/// Updates the voters for a specific proposition frame based on the provided proposition data.
/// If the proposition data is not null, it retrieves the list of voters and updates the frame.
/// If there is no proposition data, it logs a message indicating that the user has not given a proposition.
/// </summary>
/// <param name="proposition">The PropositionFrame to update.</param>
/// <param name="propositionData">The Proposition data used to update voters for the proposition frame.</param>
private void UpdatePropositionVoters(PropositionFrame proposition, Proposition propositionData)
{
if (propositionData != null)
{
List<Player> voters = GetVotersForProposition(propositionData);
proposition.UpdateVoters(voters);
}
else
{
Debug.Log("User has given no proposition", this);
// Handle the case when there is no proposition
}
}
/// <summary>
/// Retrieves the list of players who have voted for a specific proposition based on the provided proposition data.
/// It checks if the proposition data contains a list of voters and then looks up player information in the current players dictionary.
/// </summary>
/// <param name="propositionData">The Proposition data for which to retrieve voters.</param>
/// <returns>The list of players who voted for the proposition.</returns>
private List<Player> GetVotersForProposition(Proposition propositionData)
{
List<Player> voters = new List<Player>();
if (propositionData.voters != null)
{
foreach (string playerId in propositionData.voters)
{
voters.Add(currentPlayers[playerId]);
Debug.Log($"updating number of voters for: {propositionData.owner.name}'s propositions. They are now {voters.Count}.");
}
}
return voters;
}
}