using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using UnityEngine;

/// <summary>
/// Test the Progress class
/// </summary>
public class TestProgress : MonoBehaviour
{
    /// <summary>
    /// A dummy serializable struct to perform test operations on
    /// </summary>
    [Serializable]
    private struct SerializableStruct
    {
        public int r, g, b;
        public float x, y, z;
    }

    /// <summary>
    /// A dummy non-serializable struct to perform test operations on
    /// </summary>
    private struct NonSerializableStruct
    {
        public int r, g, b;
        public float x, y, z;
    }

    /// <summary>
    /// Helper method
    /// </summary>
    /// <returns><c>true</c> if <c>Progress.AddOrUpdate(...)</c> throws a <c>SerializationException</c></returns>
    private bool AddNonSerializableStruct()
    {
        Progress progress = new Progress();
        NonSerializableStruct nss = new NonSerializableStruct();
        try { progress.AddOrUpdate<NonSerializableStruct>("key", nss); }
        catch (SerializationException) { return true; }
        return false;
    }

    /// <summary>
    /// Helper method
    /// </summary>
    /// <returns><c>true</c> if <c>Progress.Get(...)</c> throws a <c>KeyNotFoundException</c></returns>
    private bool AccessInvalidKey()
    {
        Progress progress = new Progress();
        try { progress.Get<int>("non-existing key"); }
        catch (KeyNotFoundException) { return true; }
        return false;
    }

    /// <summary>
    /// Helper method
    /// </summary>
    /// <returns><c>true</c> if <c>Progress.Get(...)</c> throws a <c>InvalidCastException</c></returns>
    private bool AccessInvalidType()
    {
        Progress progress = new Progress();
        progress.AddOrUpdate<int>("key", 123456789);
        try { progress.Get<double>("key"); }
        catch (InvalidCastException) { return true; }
        return false;
    }

    /// <summary>
    /// Start is called before the first frame update
    /// </summary>
    void Start()
    {
        TestNewProgress();
        TestProgressAddInvalidData();
        TestProgressAddDuplicateKey();
        TestProgressAddInt();
        TestProgressAddDouble();
        TestProgressAddString();
        TestProgressAddSerializableStruct();
        TestProgressAddNonSerializableStruct();
        TestProgressGetInvalidKey();
        TestProgressGetInvalidType();
        TestProgressUpdate();
        TestProgressGetInt();
        TestProgressGetDouble();
        TestProgressGetString();
        TestProgressGetStruct();
    }

    /// <summary>
    /// Test for creation of a new progress
    /// </summary>
    public void TestNewProgress()
    {
        Progress progress = new Progress();
        Debug.Assert(progress != null);
    }

    /// <summary>
    /// Test whether invalid data will not be added
    /// </summary>
    public void TestProgressAddInvalidData()
    {
        Progress progress = new Progress();
        Debug.Assert(!progress.AddOrUpdate<GameObject>("key", null));
    }

    /// <summary>
    /// Test whether a duplicated key will be added
    /// </summary>
    public void TestProgressAddDuplicateKey()
    {
        Progress progress = new Progress();
        progress.AddOrUpdate<int>("key 1", 0);
        Debug.Assert(progress.AddOrUpdate<int>("key 1", 1));
    }

    /// <summary>
    /// Test whether a <c>int</c> value can be added
    /// </summary>
    public void TestProgressAddInt()
    {
        Progress progress = new Progress();
        Debug.Assert(progress.AddOrUpdate<int>("key", 1));
    }

    /// <summary>
    /// Test whether a <c>double</c> value can be added
    /// </summary>
    public void TestProgressAddDouble()
    {
        Progress progress = new Progress();
        Debug.Assert(progress.AddOrUpdate<double>("key", 1.0));
    }

    /// <summary>
    /// Test whether a <c>string</c> value can be added
    /// </summary>
    public void TestProgressAddString()
    {
        Progress progress = new Progress();
        Debug.Assert(progress.AddOrUpdate<string>("key", "Hello World!"));
    }

    /// <summary>
    /// Test whether a serializable struct can be added
    /// </summary>
    public void TestProgressAddSerializableStruct()
    {
        Progress progress = new Progress();
        Debug.Assert(progress.AddOrUpdate<SerializableStruct>("key", new SerializableStruct()));
    }

    /// <summary>
    /// Test whether a non-serializable struct will throw an error
    /// </summary>
    public void TestProgressAddNonSerializableStruct()
    {
        Debug.Assert(AddNonSerializableStruct());
    }

    /// <summary>
    /// Test whether an invalid key will throw an error
    /// </summary>
    public void TestProgressGetInvalidKey()
    {
        Debug.Assert(AccessInvalidKey());
    }

    /// <summary>
    /// Test whether an invalid type will throw an error
    /// </summary>
    public void TestProgressGetInvalidType()
    {
        Debug.Assert(AccessInvalidType());
    }

    /// <summary>
    /// Test whether a value is correctly updated
    /// </summary>
    public void TestProgressUpdate()
    {
        Progress progress = new Progress();
        progress.AddOrUpdate<int>("key", 1);
        Debug.Assert(progress.Get<int>("key") == 1);
        progress.AddOrUpdate<int>("key", 2);
        Debug.Assert(progress.Get<int>("key") == 2);
    }

    /// <summary>
    /// Test whether a <c>int</c> value can be read
    /// </summary>
    public void TestProgressGetInt()
    {
        Progress progress = new Progress();
        progress.AddOrUpdate<int>("key", 1);
        Debug.Assert(progress.Get<int>("key") == 1);
    }

    /// <summary>
    /// Test whether a <c>double</c> value can be read
    /// </summary>
    public void TestProgressGetDouble()
    {
        Progress progress = new Progress();
        progress.AddOrUpdate<double>("key", 1.0);
        Debug.Assert(progress.Get<double>("key") == 1.0);
    }

    /// <summary>
    /// Test whether a <c>string</c> value can be read
    /// </summary>
    public void TestProgressGetString()
    {
        Progress progress = new Progress();
        progress.AddOrUpdate<string>("key", "Hello World!");
        Debug.Assert(progress.Get<string>("key") == "Hello World!");
    }

    /// <summary>
    /// Test whether a serializable struct can be read
    /// </summary>
    public void TestProgressGetStruct()
    {
        Progress progress = new Progress();

        int R = 1, G = 10, B = 100;
        float X = 0.1f, Y = 0.01f, Z = 0.001f;
        SerializableStruct data = new SerializableStruct { r = R, g = G, b = B, x = X, y = Y, z = Z };
        progress.AddOrUpdate<SerializableStruct>("key", data);
        SerializableStruct result = progress.Get<SerializableStruct>("key");
        Debug.Assert(result.r == R);
        Debug.Assert(result.g == G);
        Debug.Assert(result.b == B);
        Debug.Assert(result.x == X);
        Debug.Assert(result.y == Y);
        Debug.Assert(result.z == Z);
    }
}
