#if UNITY_EDITOR
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace ConjureOS.MetadataWindow
{
    public class ConjureArcadeMetadataValidator
    {
        // String errors
        private const string EmptyStringError = "Text field cannot be empty. ";
        private const string CharLimitExceededError = "The field should contain less than {0} characters. ";
        private const string VersionFormatError = "The version doesn't match the required format 'x.y.z'.";
        private const string DevelopersCharLimitExceedError = "One or more names exceed the maximum characters limit of {0}.";

        // Min/max player errors
        private const string IncorrectPlayerValueError = "Incorrect value. The number of players can't be 0 or lower. ";
        private const string MinPlayerHigherThanMaxError = "Minimum number of players can't be higher than the maximum. ";
        private const string MaxPlayerLowerThanMinError = "Maximum number of players can't be lower than the minimum. ";

        // Image errors
        private const string MissingImageError = "No image was selected. ";
        private const string InvalidAspectRatioError = "Invalid aspect ratio. Aspect ratio of the image should be {0}:{1}. ";
        private const string InvalidFileSizeError = "Invalid file size. File size should be lower than {0} MB. ";
        private const string InvalidImagePathError = "Invalid path. Chosen picture should be placed inside the 'Assets/' folder. ";

        // Other errors
        private const string SameGenreSelectedMultipleTimeError = "Same genre is selected twice or more";

        // Metadata error messages
        private string gameTitleErrorMessage = "";
        public string GameTitleErrorMessage => gameTitleErrorMessage;

        private string versionErrorMessage = "";
        public string VersionErrorMessage => versionErrorMessage;

        private string descriptionErrorMessage = "";
        public string DescriptionErrorMessage => descriptionErrorMessage;

        private string developersErrorMessage = "";
        public string DevelopersErrorMessage => developersErrorMessage;

        private string minNumPlayerErrorMessage = "";
        public string MinNumPlayerErrorMessage => minNumPlayerErrorMessage;

        private string maxNumPlayerErrorMessage = "";
        public string MaxNumPlayerErrorMessage => maxNumPlayerErrorMessage;

        private string thumbnailErrorMessage = "";
        public string ThumbnailErrorMessage => thumbnailErrorMessage;

        private string gameplayImageErrorMessage = "";
        public string GameplayImageErrorMessage => gameplayImageErrorMessage;

        private string genresErrorMessage = "";
        public string GenresErrorMessage => genresErrorMessage;

        
        private List<int> developersErrorIndex = new List<int>();   // Used to indicate which index have an error
        public List<int> DevelopersErrorIndex => developersErrorIndex;

        private int errorCount = -1; // Starts at -1 because no validation have been done yet
        public int ErrorCount => errorCount;


        /// <summary>
        /// Validate the specified game metadata
        /// </summary>
        /// <param name="metadata">The metadata to be validated</param>
        /// <returns>Return true if metadata are valid. Otherwise, returns false.</returns>
        public bool ValidateMetadata(ConjureArcadeMetadata metadata)
        {
            errorCount = 0;

            // Validate text fields
            ValidateTextField(metadata.GameTitle, ConjureArcadeMetadata.GameTitleLimit, out gameTitleErrorMessage);
            ValidateVersion(metadata.Version);
            ValidateTextField(metadata.Description, ConjureArcadeMetadata.DescriptionLimit, out descriptionErrorMessage);

            // Validate player fields
            ValidateNumPlayerFields(metadata.MinNumPlayer, metadata.MaxNumPlayer);

            // Validate image fields
            Texture2D thumbnailTexture = metadata.Thumbnail;
            ValidateImageField(thumbnailTexture, ConjureArcadeMetadata.RequiredThumbnailAR, ConjureArcadeMetadata.MaxThumbnailFileSize, out thumbnailErrorMessage);

            Texture2D gameplayImageTexture = metadata.GameplayImage;
            ValidateImageField(gameplayImageTexture, ConjureArcadeMetadata.RequiredGameplayImageAR, ConjureArcadeMetadata.MaxGameplayImageFileSize, out gameplayImageErrorMessage);

            // Other validations
            ValidateDevelopersList(metadata.Developers);
            ValidateGenresList(metadata.Genres);

            return errorCount == 0;
        }

        private void ValidateTextField(string text, int limit, out string outErrorMessage)
        {
            outErrorMessage = "";

            // Check for empty string
            if (string.IsNullOrWhiteSpace(text))
            {
                outErrorMessage = EmptyStringError;
                errorCount++;
                return;
            }

            // Check if string exceeds char limit
            if (limit > 0)
            {
                if (text.Length > limit)
                {
                    outErrorMessage = string.Format(CharLimitExceededError, limit);
                    errorCount++;
                }
            }
        }

        private void ValidateVersion(string version)
        {
            versionErrorMessage = "";

            // Required format is "x.y.z"
            Regex rx = new Regex(@"^[a-zA-Z0-9]+\.[a-zA-Z0-9]+\.[a-zA-Z-0-9]+$");
            var matches = rx.Match(version);
            if (!matches.Success)
            {
                versionErrorMessage += VersionFormatError;
                errorCount++;
            }
        }

        private void ValidateNumPlayerFields(int minNumPlayer, int maxNumPlayer)
        {
            minNumPlayerErrorMessage = "";
            maxNumPlayerErrorMessage = "";

            if (minNumPlayer <= 0)
            {
                minNumPlayerErrorMessage += IncorrectPlayerValueError;
                errorCount++;
            }

            if (maxNumPlayer <= 0)
            {
                maxNumPlayerErrorMessage += IncorrectPlayerValueError;
                errorCount++;
            }

            if (minNumPlayer > maxNumPlayer)
            {
                minNumPlayerErrorMessage += MinPlayerHigherThanMaxError;
                maxNumPlayerErrorMessage += MaxPlayerLowerThanMinError;
                errorCount += 2;
            }
        }

        private void ValidateImageField(Texture2D image, float[] desiredRatio, float maxFileSize, out string outErrorMessage)
        {
            outErrorMessage = "";

            if (image == null)
            {
                outErrorMessage += MissingImageError;
                errorCount++;
                return;
            }

            // Verifying file size and aspect ratio
            string imageRelativePath = AssetDatabase.GetAssetPath(image);
            if (imageRelativePath.StartsWith("Assets/"))
            {
                // We check and remove "Assets" at the start of the relative path because
                // Application.dataPath already gives us the path to the assets folder
                string imageAbsolutePath = Application.dataPath + imageRelativePath.Substring(6);

                long length = new System.IO.FileInfo(imageAbsolutePath).Length;
                if (length / 1024f > maxFileSize)
                {
                    // File is bigger
                    outErrorMessage += string.Format(InvalidFileSizeError, maxFileSize);
                    errorCount++;
                }

                // Verifying aspect ratio
                float delta = 0.00001f;
                float[] originalImageDimension = GetOriginalImageDimension(imageRelativePath);
                float ratioW = originalImageDimension[0] / desiredRatio[0];
                float ratioH = originalImageDimension[1] / desiredRatio[1];
                if (Mathf.Abs(ratioW - ratioH) > delta)
                {
                    outErrorMessage += string.Format(InvalidAspectRatioError, desiredRatio[0], desiredRatio[1]);
                    errorCount++;
                }
            }
            else
            {
                // Images should be located inside the Assets folder
                outErrorMessage += InvalidImagePathError;
                errorCount++;
            }
        }

        private void ValidateGenresList(GameGenre[] genres)
        {
            genresErrorMessage = "";

            // Check if a genre has been selected twice or more
            for (int i = 0; i < genres.Length - 1; i++)
            {
                for (int j = 0; j < genres.Length; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }

                    if (genres[i].selectedGenre == genres[j].selectedGenre)
                    {
                        genresErrorMessage += SameGenreSelectedMultipleTimeError;
                        errorCount++;
                        return;
                    }
                }
            }
        }

        private float[] GetOriginalImageDimension(string assetPath)
        {
            int width = 0;
            int height = 0;
            TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;

            if (importer != null)
            {
                object[] args = new object[2] { 0, 0 };
                MethodInfo mi = typeof(TextureImporter).GetMethod("GetWidthAndHeight", BindingFlags.NonPublic | BindingFlags.Instance);
                mi.Invoke(importer, args);

                width = (int)args[0];
                height = (int)args[1];
            }

            return new float[] { width, height };
        }

        /// <summary>
        /// Validate a developers list
        /// </summary>
        /// <param name="developers">The developers list to be analyzed</param>
        public void ValidateDevelopersList(string[] developers)
        {
            developersErrorIndex.Clear();
            developersErrorMessage = "";
            for (int i = 0; i < developers.Length; i++)
            {
                if (developers[i].Length > ConjureArcadeMetadata.DevelopersNameLimit)
                {
                    developersErrorMessage += string.Format(DevelopersCharLimitExceedError, ConjureArcadeMetadata.DevelopersNameLimit);
                    developersErrorIndex.Add(i);
                    errorCount++;
                }
            }
        }

        /// <summary>
        /// </summary>
        /// <returns>Returns the validation state based on the current amount of errors detected</returns>
        public MetadataValidationStateType GetValidationStateType()
        {
            if (errorCount < 0)
            {
                // Smaller than 0, which means data haven't been verified yet
                return MetadataValidationStateType.NotVerified;
            }
            else if (errorCount == 0)
            {
                // No error, means it's a success
                return MetadataValidationStateType.Validated;
            }
            else
            {
                // Errors were detected
                return MetadataValidationStateType.Failed;
            }
        }
    }
}
#endif