using librsync.net;
using Marro.PacManUdon;
using System;
using System.Drawing.Text;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;


enum TestBallMode
{
    UseNetworkTime,
    UseNetworkDt,
    UseUnityDt,
}

public class TestBall : SyncedObject
{
    [SerializeField] private Transform start;
    [SerializeField] private Transform end;

    [SerializeField] private TestBallMode mode;

    private const float LoopTime = 1f;
    private const float MaxUp = 0.7f;
    private const float UpPerPress = 0.4f;
    private const float DownPerSecond = 1f;

    private float sumOfDt;

    private float amountUp = 0;
    private float loopOffset = 0;

    private float[] jumps;
    private int jumpsIndex;

    public void Initialize(NetworkManager networkManager)
    {
        this.networkManager = networkManager;

        sumOfDt = networkManager.SyncedTime;

        amountUp = 0;
        loopOffset = 0;

        jumps = new float[10];
        jumpsIndex = 0;
    }
    
    public override void SyncedUpdate()
    {
        SetProgress(GetProgress()); // A quick test that these methods work correctly

        DeltaUp(-DownPerSecond * networkManager.SyncedDeltaTime);

        UpdateProgress();

        float progress = GetProgress();
        transform.position = Vector3.Lerp(start.position, end.position, progress) + amountUp * MaxUp * Vector3.up;
    }

    private void DeltaUp(float delta)
    {
        amountUp = Mathf.Clamp(amountUp + delta, 0, 1);
    }

    private void Jump()
    {
        DeltaUp(UpPerPress);

        jumps[jumpsIndex++] = GetProgress();
        jumps[jumpsIndex++] = amountUp;

        if (jumpsIndex == jumps.Length)
        {
            jumpsIndex = 0;
        }
    }

    private void SetProgress(float progress)
    {
        var currentTimestamp = GetCurrentTimestamp();
        loopOffset = (currentTimestamp - progress) * LoopTime;
    }

    private float GetProgress()
    {
        var currentTimestamp = GetCurrentTimestamp();
        //Debug.Log($"CurrentTimeStamp for mode {mode}: {currentTimestamp}");
        return (currentTimestamp - loopOffset) % LoopTime / LoopTime;
    }

    private float GetCurrentTimestamp()
    {
        switch (mode)
        {
            case TestBallMode.UseNetworkTime:
                return networkManager.SyncedTime;
            case TestBallMode.UseNetworkDt:
            case TestBallMode.UseUnityDt:
                return sumOfDt;
            default:
                Debug.LogError($"({nameof(TestBall)}) Unknown mode {mode}!");
                return 0;
        }
    }

    private void UpdateProgress()
    {
        switch (mode)
        {
            case TestBallMode.UseNetworkDt:
                sumOfDt += networkManager.SyncedDeltaTime;
                break;
            case TestBallMode.UseUnityDt:
                if (!networkManager.IsEventUpdate)
                {
                    sumOfDt += Time.fixedDeltaTime;
                }
                break;
        }
    }

    public void UpButtonPressed()
    {
        Jump();
        Debug.Log($"({nameof(TestBall)}) Up button pressed, jumped up at {GetProgress()} to {amountUp}.");
    }

    public override void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType)
    {
        if (eventType == NetworkEventType.FullSync)
        {
            Debug.Log($"({nameof(TestBall)}) Sending sync data at progress {GetProgress()} and amountUp {amountUp}.");
            data.Append(amountUp, ref index);
            data.Append(GetProgress(), ref index);
        }
    }

    public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType)
    {
        if (eventType == NetworkEventType.FullSync)
        {
            amountUp = data.ReadFloat(ref index);
            SetProgress(data.ReadFloat(ref index));
            //Debug.Log($"({nameof(TestBall)}) Received sync event, synced to progress {GetProgress()} and amountUp {amountUp}.");
        }
        else
        {
            //Debug.Log($"({nameof(TestBall)}) Received up event, jumped up at {GetProgress()} from {amountUp}.");
            Jump();
            //Debug.Log($"({nameof(TestBall)}) Received up event, jumped up at {GetProgress()} to {amountUp}.");
        }

        return true;
    }

    public void WriteDebugOutput(TMP_InputField debugOutput)
    {
        string jumpsText = "";
        for (int i = 0; i < jumps.Length; i += 2)
        {
            jumpsText += $"{i/2}. ({jumps[i]}, {jumps[i + 1]}), ";
        }

        debugOutput.text += $"{nameof(TestBall)} {mode}:\n" +
            $"Progress: {GetProgress()}\n" +
            $"Up: {amountUp}\n" +
            $"SumOfDt: {sumOfDt}\n" +
            $"Jumps: {jumpsText}\n" +
            $"\n";
    }
}
