using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Realtime;
using UnityEngine.UI;
using TMPro;
using Unity.VisualScripting;
using UnityEngine.SceneManagement;
using ExitGames.Client.Photon;
using System;
using System.Reflection;
using UnityEditor;
using System.Linq;

public class Scene3Timer : MonoBehaviourPunCallbacks
{
    public GameObject Image1;
    public GameObject Image2;
    public GameObject Image3;
    public GameObject Image4;
    public GameObject Image5;
    public GameObject Sit;
    public GameObject Sit2;
    public TMP_Text timerText;

    public GameObject Rule1;
    public TMP_Text liveText1;
    public TMP_Text liveText2;
    public TMP_Text liveText3;
    public static bool RuleDescriptEnd = false;
    public TMP_Text PressEText;

    private CanvasGroup canvasGroup1;
    private CanvasGroup canvasGroup2;
    private CanvasGroup canvasGroup3;
    private CanvasGroup canvasGroup4;
    private CanvasGroup canvasGroup5;

    private CanvasGroup ruleText1;
    private CanvasGroup ruleText2;
    private CanvasGroup ruleText3;

    public float fadeDuration = 1f; // 페이드 지속 시간
    public TMP_Text DeathEnding;
    List<string> deathPlayersInfo = new List<string>();

    public GameObject Round1Question;
    public GameObject Round1Result;

    public GameObject Roundwait;

    public GameObject[] Round1Text;
    public TMP_Text[] Playername;
    public TMP_Text[] Playernumber;
    public TMP_Text resultnumber;
    public TMP_Text resultnotice;

    public GameObject[] RoundTitles;
    private bool timerEnded = false;
    public TMP_Text PlayerInputField;

    public PhotonView PV;

    public Player winner;

    public TMP_Text PlayerHealth;
    void Awake()
    {

        Image1.SetActive(false);
        Image2.SetActive(false);
        Image3.SetActive(false);
        Image4.SetActive(false);
        Image5.SetActive(false);
        canvasGroup1 = Image1.GetComponent<CanvasGroup>();
        canvasGroup2 = Image2.GetComponent<CanvasGroup>();
        canvasGroup3 = Image3.GetComponent<CanvasGroup>();
        canvasGroup4 = Image4.GetComponent<CanvasGroup>();
        canvasGroup5 = Image5.GetComponent<CanvasGroup>();
        Rule1.SetActive(false);
        liveText1.gameObject.SetActive(false);
        liveText2.gameObject.SetActive(false);
        liveText3.gameObject.SetActive(false);
        ruleText1 = liveText1.GetComponent<CanvasGroup>();
        ruleText2 = liveText2.GetComponent<CanvasGroup>();
        ruleText3 = liveText3.GetComponent<CanvasGroup>();
        Round1Question.SetActive(false);
        Round1Result.SetActive(false);

    }

    void Start()
    {
        StartCoroutine(GameSequence());

    }
    IEnumerator GameSequence()
    {
        yield return StartCoroutine(TimerCoroutine(30));
        yield return StartCoroutine(RuleDescript());
        yield return PrepareForRound();
        yield return StartCoroutine(TimerCoroutine(30));
        yield return StartCoroutine(RoundGame(2));
        yield return PrepareForRound();

        yield return StartCoroutine(TimerCoroutine(30));
        yield return StartCoroutine(RoundGame(3));
        yield return PrepareForRound();

        yield return StartCoroutine(TimerCoroutine(30));
        yield return StartCoroutine(RoundGame(4));
        yield return PrepareForRound();

        yield return StartCoroutine(TimerCoroutine(30));
        yield return StartCoroutine(RoundGame(5));
        yield return PrepareForRound();

        yield return StartCoroutine(TimerCoroutine(30));
        yield return StartCoroutine(RoundGame(6));

    }

    [PunRPC]
    void StartEndGameSequence()
    {
        StartCoroutine(EndGameSequence());
    }

    IEnumerator TimerCoroutine(int duration)
    {
        timerEnded = false;
        var wait = new WaitForSeconds(1f);

        if (PhotonNetwork.IsMasterClient)
        {
            while (duration > 0)
            {
                PV.RPC("ShowTimer", RpcTarget.All, duration);
                duration--;
                yield return wait;
            }

            PV.RPC("ShowTimer", RpcTarget.All, duration); // 마지막 시간 업데이트
            PV.RPC("NotifyTimerEnd", RpcTarget.All); // 모든 클라이언트에 타이머 종료 알림
        }
        else
        {
            // 마스터 클라이언트가 아닌 경우, 마스터 클라이언트의 타이머 종료 알림을 기다립니다.
            yield return new WaitUntil(() => timerEnded); // `timerEnded`는 타이머 종료 상태를 나타내는 bool 변수입니다.
        }

    }

    [PunRPC]
    void NotifyTimerEnd()
    {
        timerEnded = true; // 모든 클라이언트에서 타이머가 끝났음을 나타내는 변수를 true로 설정합니다.
                           // 필요한 경우, 여기에서 다음 단계로 진행하는 로직을 구현할 수도 있습니다.
    }
    [PunRPC]

    IEnumerator EndGameSequence()
    {
        PressEText.gameObject.SetActive(false);
        Image1.SetActive(true);
        StartCoroutine(FadeCanvasGroup(canvasGroup1, 0f, 1f, fadeDuration));
        yield return new WaitForSeconds(4);
        Image2.SetActive(true);
        StartCoroutine(FadeCanvasGroup(canvasGroup2, 0f, 1f, fadeDuration));
        yield return new WaitForSeconds(4);
        Image1.SetActive(false);
        Image3.SetActive(true);
        StartCoroutine(FadeCanvasGroup(canvasGroup3, 0f, 1f, fadeDuration));
        yield return new WaitForSeconds(4);
        Image2.SetActive(false);
        Image4.SetActive(true);
        StartCoroutine(FadeCanvasGroup(canvasGroup4, 0f, 1f, fadeDuration));
        yield return new WaitForSeconds(5);
        Image3.SetActive(false);
        ExitGames.Client.Photon.Hashtable playerProperties = PhotonNetwork.LocalPlayer.CustomProperties;

        int scene3order = (int)PhotonNetwork.LocalPlayer.CustomProperties["Health"];

        AssignNewMasterClient3();
        if (scene3order > 0)
        {
            SceneManager.LoadScene("Main");
        }
        else if (scene3order <= 0)
        {
            Image5.SetActive(true);
            Image4.SetActive(false);
            PhotonNetwork.LeaveRoom();
            PhotonNetwork.Disconnect();
        }

    }

    [PunRPC]
    void ShowTimer(int number)
    {
        timerText.text = number.ToString(); //타이머 갱신
    }
    [PunRPC]
    void UpdateDeathPlayersInfo(string playersInfo)
    {
        DeathEnding.text = string.Join("\n", playersInfo) + "\n심판자의 처형을 받아 장렬히 산화했습니다.";
    }
    [PunRPC]

    private IEnumerator FadeCanvasGroup(CanvasGroup cg, float start, float end, float duration)
    {
        float elapsedTime = 0f;
        while (elapsedTime < duration)
        {
            cg.alpha = Mathf.Lerp(start, end, elapsedTime / duration);
            elapsedTime += Time.deltaTime;
            yield return null;
        }
        cg.alpha = end;
    }

    private IEnumerator RuleDescript()
    {
        timerText.gameObject.SetActive(false);
        Sit.SetActive(false);
        Sit2.SetActive(false);
        Rule1.SetActive(true);
        liveText1.gameObject.SetActive(true);
        liveText2.gameObject.SetActive(true);

        StartCoroutine(FadeCanvasGroup(ruleText1, 0f, 1f, fadeDuration));
        StartCoroutine(FadeCanvasGroup(ruleText2, 0f, 1f, fadeDuration));

        yield return new WaitForSeconds(6);
        liveText2.gameObject.SetActive(false);

        liveText3.gameObject.SetActive(true);
        StartCoroutine(FadeCanvasGroup(ruleText3, 0f, 1f, fadeDuration));

        yield return new WaitForSeconds(6);
        Rule1.SetActive(false);
        RuleDescriptEnd = true;
        Round1Question.SetActive(true);
        Roundwait.SetActive(false);
        foreach (GameObject title in RoundTitles)
        {
            title.SetActive(false);
        }
        RoundTitles[0].SetActive(true);
        timerText.gameObject.SetActive(true);

    }

    public void EnterNumber()
    {
        ExitGames.Client.Photon.Hashtable playerPickNumbers = new ExitGames.Client.Photon.Hashtable();
        //플레이어가 선택한 숫자를 넣어주는 해쉬테이블생성
        string inputnumber = PlayerInputField.text.Trim();
        inputnumber = inputnumber.Replace("\u200B", "");

        int playerPickValue = int.Parse(inputnumber);

        playerPickNumbers.Add("ChooseNumber", playerPickValue);

        PhotonNetwork.LocalPlayer.SetCustomProperties(playerPickNumbers);
        foreach (GameObject textObject in Round1Text)
        {
            textObject.SetActive(false);
        }
        Roundwait.SetActive(true);

    }

    private IEnumerator RoundGame(int round)
    {
        timerText.gameObject.SetActive(false);

        Round1Question.SetActive(false);

        Round1Result.SetActive(true);
        float total = 0;
        int count = 0;

        List<Player> playersWhoChoseZero = new List<Player>(); // 0을 선택한 모든 플레이어
        List<Player> playersWhoChoseHundred = new List<Player>(); // 100을 선택한 모든 플레이어
        Dictionary<int, List<Player>> numberChoices = new Dictionary<int, List<Player>>();
      
        foreach (Player player in PhotonNetwork.PlayerList)
        {
            if (player.CustomProperties.TryGetValue("ChooseNumber", out object chooseNumberObj) && chooseNumberObj is int chooseNumber)
            {
                total += chooseNumber;
                count++;
                if (!numberChoices.ContainsKey(chooseNumber))
                {
                    numberChoices[chooseNumber] = new List<Player>();
                }
                numberChoices[chooseNumber].Add(player);
                if (chooseNumber == 0)
                {
                    playersWhoChoseZero.Add(player);
                }
                else if (chooseNumber == 100)
                {
                    playersWhoChoseHundred.Add(player);
                }
            }
        }
        bool allChoseSameNumber = numberChoices.Count == 1; // 모든 플레이어가 같은 숫자를 선택한 경우
        bool zeroAndHundredOnly = numberChoices.Keys.All(k => k == 0 || k == 100); // 0과 100만 선택된 경우
        if (count == 0)
        {
            // 모든 플레이어가 숫자를 선택하지 않았으므로 End Game Sequence를 실행
            if (PhotonNetwork.IsMasterClient)
            {
                PV.RPC("StartEndGameSequence", RpcTarget.All);
            }
                yield break; // 코루틴 종료
        }
        float average = count > 0 ? total / count : 0;
        float resultNumber = average * 0.8f; // 계산된 평균의 0.8배

        numberChoices = numberChoices.Where(kvp => kvp.Value.Count == 1).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

        Player closestPlayer = null;
        float closestDistance = float.MaxValue;
        bool exactMatch = false;
        foreach (var entry in numberChoices)
        {
            float distance = Mathf.Abs(entry.Key - resultNumber);
            if (distance < closestDistance)
            {
                closestDistance = distance;
                closestPlayer = entry.Value.First();
            }

            if (Mathf.RoundToInt(resultNumber) == Mathf.RoundToInt(entry.Key) && entry.Value.Count == 1)
            {
                exactMatch = true;
            }
        }

        // 가장 결과에 가까운 플레이어 찾기
        if (allChoseSameNumber)
        {
            for (int i = 0; i < PhotonNetwork.PlayerList.Length; i++)
            {
                Playername[i].text = PhotonNetwork.PlayerList[i].NickName;
                if (PhotonNetwork.PlayerList[i].CustomProperties.TryGetValue("ChooseNumber", out object chooseNumberObj))
                {
                    Playernumber[i].text = chooseNumberObj is int chooseNumber ? chooseNumber.ToString() : "N/A";
                }
                else
                {
                    Playernumber[i].text = "N/A";
                }
            }
            resultnumber.text = "승자 없음";
            resultnotice.text = "모두가 같은 숫자를 선택";

        }
        else if (zeroAndHundredOnly)
        {
            for (int i = 0; i < PhotonNetwork.PlayerList.Length; i++)
            {
                Playername[i].text = PhotonNetwork.PlayerList[i].NickName;
                if (PhotonNetwork.PlayerList[i].CustomProperties.TryGetValue("ChooseNumber", out object chooseNumberObj))
                {
                    Playernumber[i].text = chooseNumberObj is int chooseNumber ? chooseNumber.ToString() : "N/A";
                }
                else
                {
                    Playernumber[i].text = "N/A";
                }
            }
            resultnumber.text = "100";
            resultnotice.text = "0과 100을 선택한 플레이어만 존재, 100을 선택한 플레이어 승리";

        }
        else
        {
            if (closestPlayer != null)
            {
                resultnumber.text = resultNumber.ToString("F0");
                if (closestPlayer != null)
                {
                    resultnotice.text = $"평균 숫자 {average:F2} * 0.8 = {resultNumber:F2}로 \n평균에서 가장 가까운, {closestPlayer.NickName} 승리";
                }
                else
                {
                    resultnotice.text = "승자 없음";
                }
                for (int i = 0; i < PhotonNetwork.PlayerList.Length; i++)
                {
                    Playername[i].text = PhotonNetwork.PlayerList[i].NickName;
                    if (PhotonNetwork.PlayerList[i].CustomProperties.TryGetValue("ChooseNumber", out object chooseNumberObj))
                    {
                        Playernumber[i].text = chooseNumberObj is int chooseNumber ? chooseNumber.ToString() : "N/A";
                    }
                    else
                    {
                        Playernumber[i].text = "N/A";
                    }
                }
            }
        }
       

        yield return new WaitForSeconds(8);

        Round1Result.SetActive(false);
        if (zeroAndHundredOnly)
        {
            List<Player> winners = playersWhoChoseHundred.ToList();
            DecreaseHealthForAllExceptWinner(winners, false);

        }
        else if (allChoseSameNumber)
        {
            // 모든 플레이어가 같은 숫자를 선택한 경우
            foreach (var player in PhotonNetwork.PlayerList)
            {
                // 모든 플레이어의 체력 1 감소
                DecreaseHealthForAllExceptWinner(new List<Player>(), false);
            }
        }
        else
        {
            if (closestPlayer != null)
            {
                DecreaseHealthForAllExceptWinner(new List<Player> { closestPlayer }, exactMatch);
            }
        }
       

        Round1Question.SetActive(true);
        foreach (GameObject title in RoundTitles)
        {
            title.SetActive(false);
        }
        RoundTitles[round - 1].SetActive(true);
        timerText.gameObject.SetActive(true);
        foreach (GameObject textObject in Round1Text)
        {
            textObject.SetActive(true);

        }
        Roundwait.SetActive(false);

    }
    void AssignNewMasterClient3()
    {
        if ((int)PhotonNetwork.MasterClient.CustomProperties["Health"] <= 0)
        {
            Player[] players = PhotonNetwork.PlayerList;

            foreach (Player player in players)
            {
                if ((int)player.CustomProperties["Health"] > 0)
                {
                    PhotonNetwork.SetMasterClient(player);
                    break;
                }
            }
        }
    }
    private IEnumerator PrepareForRound()
    {
        PhotonNetwork.LocalPlayer.SetCustomProperties(new ExitGames.Client.Photon.Hashtable { { "ChooseNumber", null } });
        yield return null;
    }
    void DecreaseHealthForAllExceptWinner(List<Player> winners, bool exactMatch)
    {
        if (PhotonNetwork.IsMasterClient)
        {
            foreach (Player player in PhotonNetwork.PlayerList)
            {
                if (!winners.Contains(player))
                {
                    int healthDecreaseAmount = exactMatch ? 2 : 1;
                    int currentHealth = player.CustomProperties.ContainsKey("Health") ? (int)player.CustomProperties["Health"] : 3;
                    int newHealth = Mathf.Max(currentHealth - healthDecreaseAmount, 0);

                    ExitGames.Client.Photon.Hashtable props = new ExitGames.Client.Photon.Hashtable { { "Health", newHealth } };
                    player.SetCustomProperties(props);

                    if (newHealth <= 0)
                    {
                        string personality = player.CustomProperties["Personality"] as string;
                        string nickname = player.NickName as string;

                        deathPlayersInfo.Add("자칭 " + personality + " " + nickname + ",\n");
                        string deathPlayersStr = string.Join("\n", deathPlayersInfo);
                        PV.RPC("UpdateDeathPlayersInfo", RpcTarget.AllBuffered, deathPlayersStr);

                        PV.RPC("StartEndGameSequence", RpcTarget.All);
                    }
                }
            }
        }
    }
    
    public override void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
    {
        base.OnPlayerPropertiesUpdate(targetPlayer, changedProps);

        // 체력 정보가 변경되었는지 확인합니다.
        if (changedProps.ContainsKey("Health"))
        {
            // 변경된 체력 값을 가져옵니다.
            int updatedHealth = (int)changedProps["Health"];

            // 현재 로컬 플레이어의 체력 정보를 업데이트합니다.
            if (targetPlayer == PhotonNetwork.LocalPlayer)
            {
                PlayerHealth.text = $"현재 체력 : {updatedHealth}";
            }
        }
    }
}

완성!

테스트케이스를 만들것

  1. 10 20 50

잘작동

  1. 100 0 0

0선택한애들 다피깎

  1. 50 50 50

전부다 피 1깎임

  1. 100 0 10

평균 값 찾고 10이 승리 나머지 1깎

  1. null null null

전부승리로 다음스테이지로넘김

0 null null은 배제하자,..,.ㅋㅋㅋㅋㅋㅋ

0 null null 인 경우에도 달성, 0인놈 승리.

그러나 0 100 null 일 경우에는, null인놈이랑 0인놈 둘다 패배 아마? 이거 체크해보자.

완성!