using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using System.Text;
using System;
using System.IO;

namespace BugseePlugin
{
    public enum BugseeSeverityLevel
    {
        Low = 1,
        Medium,
        High,
        Critical,
        Blocker
    }

    public enum BugseeLogLevel
    {
        Invalid = 0,
        Error,
        Warning,
        Info,
        Debug,
        Verbose
    }

    public enum BugseeStyle
    {
        Default,
        Dark,
        BasedOnStatusBar
    }

    public class Bugsee : MonoBehaviour
    {
        public const string ShakeToReportKey = "ShakeToReport";
        public const string MaxRecordingTimeKey = "MaxRecordingTime";
        public const string ScreenshotToReportKey = "ScreenshotToReport";
        public const string CrashReportKey = "CrashReport";
        public const string MaxFrameRateKey = "MaxFrameRate";
        public const string EndpointKey = "endpoint";
        public const string KillDetectionKey = @"BugseeKillDetectionKey";
        public const string MonitorNetworkKey = @"MonitorNetwork";
        public const string VideoEnabledKey = @"VideoEnabled";
        public const string StyleKey = @"BugseeStyle";
        public const string ReportPrioritySelectorKey = @"BugseeReportPrioritySelector";
        public const string DefaultCrashPriorityKey = @"BugseeDefaultCrashPriority";
        public const string DefaultBugPriorityKey = @"BugseeDefaultBugPriority";
        public const string EnableMachExceptionsKey = @"BugseeEnableMachExceptions";
        public const string CaptureLogsKey = @"CaptureLogs";

        private static string Serialize(object obj)
        {
            StringBuilder sb = new StringBuilder();
            var type = obj.GetType();
            if (type.IsPrimitive)
            {
                sb.Append(obj.ToString().ToLower());
            }
            else if (type == typeof(string))
            {
                sb.Append("\"" + obj.ToString() + "\"");
            }
            else if (type.IsArray)
            {
                sb.Append("[");
                int i = 0;

                foreach (object element in (obj as object[]))
                {
                    if (i++ > 0)
                        sb.Append(", ");
                    sb.Append(Serialize(element));
                }

                sb.Append("]");
            }
            else if (type.IsClass)
            {
                
                int i = 0;
                if (obj is List<object>){
                    sb.Append("[");
                    foreach (object element in (List<object>)obj)
					{
						if (i++ > 0)
							sb.Append(", ");
						sb.Append(Serialize(element));
					}
                    sb.Append("]");
                }else if (obj is Dictionary<string, object>){
                    sb.Append("{");
                    foreach (KeyValuePair<string, object> pair in (IDictionary<string, object>)obj)
                    {
                        if (i++ > 0)
                            sb.Append(", ");
                        sb.Append("\"" + pair.Key + "\": " + Serialize(pair.Value));
                    }
                    sb.Append("}");
                }else{
                    sb.Append("\"unknown type, supports only string, number, List<object> or Dictionary<string,object>\"");
                }


            }

            return sb.ToString();
        }

        private static object Deserialize(string obj)
        {
            if (obj.Length < 1) return null;

            object result = null;
            if (obj[0] == '"')
            { // string
                result = obj.Substring(1, obj.Length - 2); //Remove first and last symbol
            }
            else if (obj[0] != '{')
            { // primitive 
                if (obj.IndexOf('.') > 0)
                {
                    try
                    {
                        result = float.Parse(obj);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("{0}: Bad Parce", e);
                        return null;
                    }
                }
                else
                {
                    try
                    {
                        result = Int32.Parse(obj);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("{0}: Bad Parce", e);
                        return null;
                    }
                }
            }
            else if (obj[1] == '[')
            { //Array
                try
                {
                    result = JsonUtility.FromJson<List<object>>(obj);
                }
                catch (Exception e)
                {
                    Console.WriteLine("{0}: Bad Parce", e);
                    return null;
                }
            }
            else
            { // Dictionary
                try
                {
                    result = JsonUtility.FromJson<Dictionary<string, object>>(obj);
                }
                catch (Exception e)
                {
                    Console.WriteLine("{0}: Bad Parce", e);
                    return null;
                }
            }

            return result;
        }


#if UNITY_IPHONE && !UNITY_EDITOR
        #region iOS methods
        [DllImport("__Internal")]
        private static extern void _bugsee_launch(string appToken, string options);

        [DllImport("__Internal")]
        private static extern void _bugsee_show_report(string summary, string description, int severity);

        [DllImport("__Internal")]
        private static extern void _bugsee_pause();

        [DllImport("__Internal")]
        private static extern void _bugsee_resume();

        [DllImport("__Internal")]
        private static extern void _bugsee_registerEvent(string name, string parameters);

        [DllImport("__Internal")]
        private static extern void _bugsee_traceKey(string name, string wrapperDict);

        [DllImport("__Internal")]
        private static extern void _bugsee_upload(string summary, string description, int severity);

        [DllImport("__Internal")]
        private static extern void _bugsee_logError(string description);

        [DllImport("__Internal")]
        private static extern void _bugsee_log(string message, int level);

        [DllImport("__Internal")]
        private static extern void _bugsee_logException(string name, string reason, string frames, bool handled);

        [DllImport("__Internal")]
        private static extern void _bugsee_feedback();

        [DllImport("__Internal")]
        private static extern void _bugsee_set_def_feedback_greeting(string message);

        [DllImport("__Internal")]
        private static extern void _bugsee_assert(bool condition, string description);

        [DllImport("__Internal")]
        private static extern void _bugsee_testExceptionCrash();

        [DllImport("__Internal")]
        private static extern void _bugsee_testSignalCrash();

        [DllImport("__Internal")]
        private static extern bool _bugsee_set_attribute(string value);

        [DllImport("__Internal")]
        private static extern string _bugsee_get_attribute(string key);

        [DllImport("__Internal")]
        private static extern bool _bugsee_clear_attribute(string key);


        protected static void HandleException(string logString, string stackTrace, bool handled)
        {
            string[] stacktraceLines = stackTrace.TrimEnd('\r', '\n').Split('\n');

            _bugsee_logException("Exception", logString, Serialize(stacktraceLines), handled);
        }


        public static void OnHandleLogCallback(string logString, string stackTrace, LogType type)
        {

            if (LogType.Assert == type || LogType.Error == type)
            {
                HandleException(logString, stackTrace, false);
            }
            else if (LogType.Exception == type)
            {
                HandleException(logString, stackTrace, true);
            }

        }

        public static void OnHandleUnresolvedException(object sender, System.UnhandledExceptionEventArgs args)
        {

            if (args == null || args.ExceptionObject == null)
            {

                return;
            }

            if (args.ExceptionObject.GetType() == typeof(System.Exception))
            {
                System.Exception e = (System.Exception)args.ExceptionObject;
                HandleException(e.Source, e.StackTrace, false);
            }
        }

        public static void RegisterExceptionHandlers()
        {
            System.AppDomain.CurrentDomain.UnhandledException += OnHandleUnresolvedException;
            Application.logMessageReceived += OnHandleLogCallback;
        }

        /// <summary>
        /// Initialize Bugsee SDK with custom parameters.
        /// </summary>
        /// <param name="appToken">Application token</param>
        /// <param name="options">Dictionary of options</param>
        public static void Launch(string appToken, IDictionary<string, object> options = null)
        {
            string jsonStr = null;
            if (options != null)
            {
                jsonStr = Serialize(options);
            }

            if (Application.isMobilePlatform)
            {
                _bugsee_launch(appToken, jsonStr);
            }
        }

        /// <summary>
        /// Show Bugsee report dialog.
        /// </summary>
        /// <param name="summary">Brief summary of the issue to prefill</param>
        /// <param name="description">Description to prefill</param>
        /// <param name="severity">Severity to prefill</param>
        public static void ShowReport(string summary = "", string description = "", int severity = 3)
        {
            _bugsee_show_report(summary, description, severity);
        }

        /// <summary>
        /// Pause bugsee video and loggers
        /// </summary>
        public static void Pause()
        {
            _bugsee_pause();
        }

        /// <summary>
        /// Resume bugsee video and loggers
        /// </summary>
        public static void Resume()
        {
            _bugsee_resume();
        }

        /// <summary>
        /// Register custom event with additional parameters.
        /// </summary>
        /// <param name="name">Unique name of the event</param>
        /// <param name="parameters">Dictionary of parameters to attach to event</param>
        public static void Event(string name, IDictionary<string, object> parameters = null)
        {
            string jsonStr = null;
            if (parameters != null)
            {
                jsonStr = Serialize(parameters);
            }

            _bugsee_registerEvent(name, jsonStr);
        }

        /// <summary>
        /// Custom trace with additional parameters.
        /// </summary>
        /// <param name="name">Unique name of the trace</param>
        /// <param name="value">Value of the variable</param>
        public static void Trace(string name, object value)
        {
            string jsonStr = Serialize(value);
            _bugsee_traceKey(name, jsonStr);
        }

        /// <summary>
        /// Upload report manually.
        /// </summary>
        /// <param name="summary">Brief summary of the issue</param>
        /// <param name="description">Description of the issue</param>
        /// <param name="severity">Severity (1..5)</param>
        public static void Upload(string summary, string description = "", BugseeSeverityLevel severity = BugseeSeverityLevel.High)
        {
            _bugsee_upload(summary, description, (int)severity);
        }

        /// <summary>
        /// Upload report with error message and callstack.
        /// </summary>
        /// <param name="message">This message will be shown on the web</param>
        public static void LogError(string message)
        {
            _bugsee_logError(message);
        }

        /// <summary>
        /// Send log message to the bugsee logs tab on the web
        /// </summary>
        /// <param name="message">Log message, will show on Logs tab on the web</param>
        /// <param name="level">Level of the log message, fill the message with the different collors on the web</param>
        public static void Log(string message, BugseeLogLevel level = BugseeLogLevel.Debug)
        {
            _bugsee_log(message, (int)level);
        }

        /// <summary>
        /// Show feedback UI over the screen to comunicate with user
        /// </summary>
        public static void ShowFeedbackUI()
        {
            _bugsee_feedback();
        }

        /// <summary>
        /// Emulate native exception crash
        /// </summary>
        public static void TestExceptionCrash()
        {
            _bugsee_testExceptionCrash();
        }

        /// <summary>
        /// Set first message for Feedback UI
        /// </summary>
        public static void SetFeedbackGreetingMessage(string message)
        {
            _bugsee_set_def_feedback_greeting(message);
        }

        /// <summary>
        /// Validate assertion and upload if condition is false.
        /// </summary>
        /// <param name="condition">Condition to validate</param>
        /// <param name="summary">Brief summary of the issue</param>
        /// <param name="description">Description of the issue</param>
        /// <param name="severity">Severity (1..5)</param>
        public static void Assert(bool condition, string description = "")
        {
            if (!condition)
                _bugsee_assert(false, description);
        }

        public static bool SetAttribute(string key, object val)
        {
            string serializedVal = Serialize(val);
            string result = "{" + "\"key\":\"" + key + "\",\"value\":" + serializedVal + "}";
            return _bugsee_set_attribute(result);
        }

        public static object GetAttribute(string key)
        {
            var result = _bugsee_get_attribute(key);
            //var d = Deserialize(result);

            return result;
        }

        public static bool ClearAttribute(string key)
        {
            return _bugsee_clear_attribute(key);
        }

        #endregion
#elif UNITY_ANDROID && !UNITY_EDITOR
    #region Android methods
		//public static AndroidJavaClass bugseeAdapterClass = new AndroidJavaClass("com.bugsee.library.BugseeUnityAdapter");
		//public static AndroidJavaClass unityActivityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
		//public static AndroidJavaObject activityObj = unityActivityClass.GetStatic<AndroidJavaObject>("currentActivity");

		//private static AndroidJavaObject bugseeObj = activityObj.Get<AndroidJavaObject>("bugsee");
		//private static AndroidJavaObject adapterObj = activityObj.Get<AndroidJavaObject>("adapter");

		//private static string Serialize(IDictionary<string, object> dict)
		//{
		//	StringBuilder sb = new StringBuilder();
		//	sb.Append("{");
		//	int i = 0;
		//	foreach (KeyValuePair<string, object> pair in dict)
		//	{
		//		if (i++ > 0)
		//		sb.Append(", ");
		//		var type = pair.Value.GetType();
		//		if (type.IsPrimitive) {
		//			sb.Append ("\"" + pair.Key + "\": " + pair.Value.ToString ().ToLower ());
		//		} else if (type == typeof(string)) {
		//			sb.Append ("\"" + pair.Key + "\": " + "\"" + pair.Value.ToString () + "\"");
		//		} else if (type.IsClass) {
		//			sb.Append ("\"" + pair.Key + "\": " + Serialize ((IDictionary<string, object>)pair.Value));
		//		}
		//	}

		//	sb.Append("}");
		//	return sb.ToString();
		//}

		public static void Launch(string appToken, IDictionary<string, object> options = null) {
			//object[] args = new object[2];
			//args [0] = appToken;
			//args [1] = Serialize (options);
			//adapterObj.Call("launch",args);
		}

		public static void ShowReport(string summary = "", string description = "", int severity = 3) {}

		public static void Pause (){}
		public static void Resume (){}
		public static void Event(string name, IDictionary<string, object> parameters = null) {}
		public static void Trace(string name, object value) {}
		public static void Upload(string summary, string description = "", BugseeSeverityLevel severity = BugseeSeverityLevel.High) {}
		public static void LogError(string message) {}
		public static void Log(string message, BugseeLogLevel level = BugseeLogLevel.Debug) {}
		public static void ShowFeedbackUI () {}
		public static void SetFeedbackGreetingMessage (string message) {}
		public static void Assert(bool condition, string description = "") {}
		public static void RegisterExceptionHandlers () {}
        public static bool SetAttribute(string key, object val) { return true; }
        public static object GetAttribute(string key) { return null; }
        public static bool ClearAttribute(string key) { return true; }
#endregion
#else // Other not supported platrofms, just empty methods
    #region Other platforms
		public static void Launch (string appToken, IDictionary<string, object> options = null)
		{
		}

		public static void ShowReport (string summary = "", string description = "", int severity = 3)
		{
		}

		public static void Pause ()
		{
		}

		public static void Resume ()
		{
		}

		public static void Event (string name, IDictionary<string, object> parameters = null)
		{
		}

		public static void Trace (string name, object value)
		{
		}

		public static void Upload (string summary, string description = "", BugseeSeverityLevel severity = BugseeSeverityLevel.High)
		{
		}

		public static void LogError (string message)
		{
		}

		public static void Log (string message, BugseeLogLevel level = BugseeLogLevel.Debug)
		{
		}

		public static void ShowFeedbackUI ()
		{
		}

		public static void SetFeedbackGreetingMessage (string message)
		{
		}

		public static void Assert (bool condition, string description = "")
		{
		}

		public static void RegisterExceptionHandlers ()
		{
		}

		public static void TestExceptionCrash (){}

        public static bool SetAttribute(string key, object val) { return true; }
        public static object GetAttribute(string key) { return null; }
        public static bool ClearAttribute(string key) { return true; }
#endregion
#endif
    }
}
