using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Management;
using UnityEngine;


/// <summary>
/// Benchmark class
/// </summary>
public class DeformationTime
{
    public bool IsValid { get; private set; } = false;

    public int DeformationCount { get; private set; }

    public int DeformationTimeSum { get; private set; }

    public int DeformationMinTime { get; private set; } = int.MaxValue;

    public int DeformationMaxTime { get; private set; } = int.MinValue;

    public float AverageDeformationTime
    {
        get
        {
            return (float)DeformationTimeSum / DeformationCount;
        }
    }

    public void AddTime(int deformationTime)
    {
        IsValid = true;
        DeformationMinTime = Math.Min(deformationTime, DeformationMinTime);
        DeformationMaxTime = Math.Max(deformationTime, DeformationMaxTime);
        DeformationCount++;
        DeformationTimeSum += deformationTime;
    }
}

public class PerformanceTest : MonoBehaviour {

    public MeshFilter[] meshes;

    public static PerformanceTest Instance { get; private set; }

    public GrabDeformation grabDeformation;

    private FPSCounter _counter;

    public Transform deformationPoint;

    public DeformationManager deformationManager;

    public Transform startPosition;

    public Transform endPositionGrab;

    public Transform endPositionSmoothing;

    private bool deforming = false;

    public Transform endPositionInflate;

    public int numberOfRepetitions = 1;

    private void Start()
    {
        if (Instance == null)
        {
            Instance = this;
        }

        _counter = GetComponent<FPSCounter>();
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.G))
        {
            StartCoroutine(StartPerformanceTest());
        }

        
    }

    private DeformationTime deformationTime;


    public void LogDeformationTime(int deformationTimeInMS)
    {
        if (deformationTime != null)
            deformationTime.AddTime(deformationTimeInMS);
    }

    public IEnumerator StartPerformanceTest()
    {
        string path = Application.dataPath;
        string directoryPath = Path.Combine(path, "Export");
        Directory.CreateDirectory(directoryPath);

        string filePath = Path.Combine(directoryPath, "performance.txt");

        using (StreamWriter newFile = new StreamWriter(filePath, true))
        {
            string line;

            line = "-------------------------------------";
            newFile.WriteLine(line);

            line = "OS : " + SystemInfo.operatingSystem;
            newFile.WriteLine(line);

            line = "device : " + SystemInfo.deviceName;
            newFile.WriteLine(line);

            line = "processor : " + SystemInfo.processorType + " Core count : " + SystemInfo.processorCount;
            newFile.WriteLine(line);

            line = "graphics : " + SystemInfo.graphicsDeviceName + " Video memory : " + SystemInfo.graphicsMemorySize;
            newFile.WriteLine(line);
        }

        foreach (var meshFilter in meshes)
        {
            deformationManager.SetNewMesh("test mesh", meshFilter);
            yield return new WaitForSecondsRealtime(0.5f);

            using (StreamWriter newFile = new StreamWriter(filePath, true))
            {
                string line = "Results for mesh with " + meshFilter.mesh.triangles.Length / 3 + " triangles and " + meshFilter.mesh.vertexCount + " vertices";
                newFile.WriteLine(line);
                newFile.WriteLine();
            }

            StartTestSmoothing();

            while (deforming)
            {
                yield return null;
            }

            StartTestGrabOnGPU();

            while (deforming)
            {
                yield return null;
            }

            StartTestGrabOnCPU();

            while (deforming)
            {
                yield return null;
            }

            StartTestInflate();

            while (deforming)
            {
                yield return null;
            }

        }

        

    }

    public void StartTestGrabOnCPU()
    {
        deforming = true;
        deformationManager.SetDeformationByName(typeof(GrabDeformation));
        grabDeformation.computeOnCpu = true;
        deformationManager.CurrentIntensity = 3;
        deformationManager.AreaOfEffect = (0.08f);
        StartCoroutine(MoveDeformationPoint("Grab deform on CPU", numberOfRepetitions, startPosition, endPositionGrab));
    }

    public void StartTestGrabOnGPU()
    {
        deforming = true;
        deformationManager.SetDeformationByName(typeof(GrabDeformation));
        grabDeformation.computeOnCpu = false;
        deformationManager.CurrentIntensity = (3);
        deformationManager.AreaOfEffect = (0.08f);
        StartCoroutine(MoveDeformationPoint("Grab deform on GPU", numberOfRepetitions, startPosition, endPositionGrab));
    }

    public void StartTestSmoothing()
    {
        deforming = true;
        deformationManager.SetDeformationByName(typeof(SmoothingDeformation));
        deformationManager.CurrentIntensity = (1);
        deformationManager.AreaOfEffect = (0.06f);
        StartCoroutine(MoveDeformationPoint("Smoothing", numberOfRepetitions, startPosition, endPositionSmoothing));
    }

    public void StartTestInflate()
    {
        deforming = true;
        deformationManager.SetDeformationByName(typeof(InflateDeformation));
        deformationManager.CurrentIntensity = (1);
        deformationManager.AreaOfEffect = (0.02f);
        StartCoroutine(MoveDeformationPoint("Inflate", numberOfRepetitions, startPosition, endPositionInflate, 0.0004f));
    }

    private IEnumerator MoveDeformationPoint(string deformation, int count, Transform start, Transform end, float speed = 0.0005f)
    {
        deformationTime = new DeformationTime();
        _counter.ResetCounter();
        _counter.StartCounter();
        

        for (int i = 0; i < count; i++)
        {
            deformationPoint.transform.position = start.position;
            _counter.StopCounter();
            yield return null;
            yield return null;
            _counter.StartCounter();

            deformationManager.StartDeforming();
            while (deformationPoint.position != end.position)
            {
                deformationPoint.position = Vector3.MoveTowards(deformationPoint.position, end.position, speed);
                yield return null;
            }
            deformationManager.StopDeforming();
            _counter.StopCounter();
            yield return new WaitForSecondsRealtime(0.2f);
            deformationManager.UndoStack.StepBack();
            yield return null;
            _counter.StartCounter();

        }
        
       
        
        Debug.Log("AverageFPS = " + _counter.AverageFPS);
        SaveToFile(deformation);
        deforming = false;
        yield return null;
    }


    private void SaveToFile(string deformation)
    {
        string path = Application.dataPath;
        string directoryPath = Path.Combine(path, "Export");
        Directory.CreateDirectory(directoryPath);

        string filePath = Path.Combine(directoryPath, "performance.txt");

        using (StreamWriter newFile = new StreamWriter(filePath, true))
        {

            string line;
            newFile.WriteLine();
            line = deformation;
            newFile.WriteLine(line);

            line = "AverageFPS = " + _counter.AverageFPS;
            newFile.WriteLine(line);


            if (deformationTime != null && deformationTime.IsValid)
            {
                line = "Average MS of one deformation = " + deformationTime.AverageDeformationTime;
                newFile.WriteLine(line);
                line = "Minimal MS of one deformation = " + deformationTime.DeformationMinTime;
                newFile.WriteLine(line);
                line = "Maximal MS of one deformation = " + deformationTime.DeformationMaxTime;
                newFile.WriteLine(line);
            }


        }
    }

    
}
