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

namespace BugseePlugin
{
    public enum BugseeFrameRate
    {
        Low = 1,
        Medium,
        High
    }

    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 struct BugseeReport
    {
        public string type;
        public BugseeSeverityLevel severityLevel;
    }

    public struct BugseeAttachment
    {
        public string name;
        public string filename;
        public byte[] data;
    }

    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";

        public delegate List<BugseeAttachment> AttachmentForReport(BugseeReport report);
        public static event AttachmentForReport OnAttachmentForReport;

        private static bool isBugseeEnabled = false;

        private void Awake()
        {
            gameObject.name = "bgs_gameObject";
        }

        // We have the copy of this method in Xamarin JsonSerializer class. There are unit tests for this class.
        private static string Serialize(object obj)
        {
            if (obj == null)
                return null;
                
            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.IsEnum){
                int val = (int)obj;
                sb.Append(val.ToString());
            }else if (type.IsArray)
            {
                var castedValue = obj as Array;
                var linearValues = castedValue.GetEnumerator();
                SerializeMultidimensionalArray(sb, castedValue, linearValues, 0);
            }
            else if (obj is IList)
            {
                int i = 0;
                sb.Append("[");
                foreach (object element in (IList)obj)
                {
                    if (i++ > 0)
                        sb.Append(", ");
                    sb.Append(Serialize(element));
                }
                sb.Append("]");
            }
            else if (obj is IDictionary)
            {
                int i = 0;
                sb.Append("{");
                var enumerator = ((IDictionary)obj).GetEnumerator();
                while (enumerator.MoveNext())
                {
                    if (i++ > 0)
                    {
                        sb.Append(", ");
                    }
                    sb.Append("\"" + enumerator.Key + "\": " + Serialize(enumerator.Value));
                }
                sb.Append("}");
            }
            else
            {
                sb.Append("\"" + obj.ToString() + "\"");
            }

            return sb.ToString();
        }
        
        private static void SerializeMultidimensionalArray(StringBuilder stringBuilder, Array array, System.Collections.IEnumerator linearValues, int dimension)
        {
            stringBuilder.Append("[");
            for (int index = 0; index < array.GetLength(dimension); index++)
            {
                if (index > 0)
                {
                    stringBuilder.Append(", ");
                }
                if (dimension == array.Rank - 1)
                {
                    linearValues.MoveNext();
                    stringBuilder.Append(Serialize(linearValues.Current));
                } else
                {
                    SerializeMultidimensionalArray(stringBuilder, array, linearValues, dimension + 1);
                }
            }
            stringBuilder.Append("]");
        }

        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;
        }

        private static void OnSceneChanged(Scene fromScene, Scene toScene)
        {
            Dictionary<string, object> trace = new Dictionary<string, object>
            {
                { "name", toScene.name },
                { "index", toScene.buildIndex }
            };

            Trace("___scene", trace);
        }

#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);

        [DllImport("__Internal")]
        private static extern void _bugsee_set_email(string email);

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

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

        [DllImport("__Internal")]
        private static extern void _bugsee_setAttachmentsForReport(string attachments);

        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 LogException(Exception exception)
        {
            HandleException(exception.Source, exception.StackTrace, true);
        }        
        
        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);
            }
        }

        private 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">Custom parameters</param>
        public static void Launch(string appToken, BugseeLaunchOptions options = null)
        {
            var launchOptions = options == null ? IOSLaunchOptions.GetDefaultOptions() : options;
            var serializedOptions = launchOptions.SerializeOptions();
            Launch(appToken, serializedOptions);
        }
        
        /// <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)
        {
            if (isBugseeEnabled)
            {
                Debug.Log("Bugsee already launched, check your code, you might be calling it twice!");
                return;
            }

            string jsonStr = null;
            if (options != null)
            {
                jsonStr = Serialize(options);
            }

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

            SceneManager.activeSceneChanged += OnSceneChanged;
            RegisterExceptionHandlers();
            isBugseeEnabled = true;
        }

        /// <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 = "", BugseeSeverityLevel severity = BugseeSeverityLevel.High)
        {
            _bugsee_show_report(summary, description, (int)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>
        /// Emulate native signal crash
        /// </summary>
        public static void TestSignalCrash() {
            _bugsee_testSignalCrash();
        }

        /// <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="description">Description of the issue</param>
        public static void Assert(bool condition, string description = "")
        {
            if (!condition)
                _bugsee_assert(false, description);
        }

        /// <summary>
        /// Set user attribute
        /// </summary>
        /// <param name="key">Name of attribute</param>
        /// <param name="val">Attribute value</param>
        public static bool SetAttribute(string key, object val)
        {
            string serializedVal = Serialize(val);
            string result = "{" + "\"key\":\"" + key + "\",\"value\":" + serializedVal + "}";
            return _bugsee_set_attribute(result);
        }

        /// <summary>
        /// Get user attribute
        /// </summary>
        /// <param name="key">Name of attribute</param>
        public static object GetAttribute(string key)
        {
            var result = _bugsee_get_attribute(key);
            //var d = Deserialize(result);

            return result;
        }

        /// <summary>
        /// Clear attribute with given key
        /// </summary>
        /// <param name="key">Name of attribute</param>
        public static bool ClearAttribute(string key)
        {
            return _bugsee_clear_attribute(key);
        }

        /// <summary>
        /// Set users email
        /// </summary>
        /// <param name="email">email</param>
        public static void SetEmail(string email) {
            _bugsee_set_email(email);
        }

        public static string GetEmail() { 
            return _bugsee_get_email(); 
        }

        public static void ClearEmail() {
            _bugsee_clear_email();
        }

        #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 const String VALUE_NAME = "value";
        
        public static void LogException(Exception exception) 
        {
            HandleException(exception, true);
        }
        
        private static void HandleException(Exception exception, bool handled)
        {
            if (exception == null)
                return;
                
            AndroidJavaObject exceptionInfo = GetExceptionInfo(exception);
            bugseeAdapterClass.CallStatic("logException", exceptionInfo, handled);
        }

        private static AndroidJavaObject GetExceptionInfo(Exception exception) 
        {
            // Note: we can't set null to AndroidJavaObject's field. Otherwise we will get NullReferenceException.
            AndroidJavaObject exceptionInfo = new AndroidJavaObject("com/bugsee/library/data/CrashInfo$ExceptionInfo");
            if (exception.GetType().FullName != null) {
                exceptionInfo.Set<String>("name", exception.GetType().FullName);
            }
            if (exception.Message != null) {
                exceptionInfo.Set<String>("reason", exception.Message);
            }
            var frames = GetFrames(exception.StackTrace);
            if (frames != null) {
                exceptionInfo.Set<AndroidJavaObject>("frames", frames);
            }
            if (exception.InnerException != null) 
            {
                exceptionInfo.Set<AndroidJavaObject>("cause", GetExceptionInfo(exception.InnerException));
            }
            return exceptionInfo;
        }
        
        private static AndroidJavaObject GetFrames(String stackTrace)
        {
            if (String.IsNullOrEmpty(stackTrace))
                return null;

            AndroidJavaObject lastFrame = null;
            string[] stacktraceLines = stackTrace.TrimEnd('\r', '\n').Split('\n');
            var frames = new AndroidJavaObject("java.util.ArrayList");
            for (int i = 0; i < stacktraceLines.Length; i++)
            {
                if (stacktraceLines[i] == null)
                    continue;
                    
                AndroidJavaObject frameInfo = new AndroidJavaObject("com/bugsee/library/data/CrashInfo$FrameInfo");
                frameInfo.Set<String>("trace", stacktraceLines[i]);
                frameInfo.Set<Int32>("count", 1);
                if ((lastFrame != null) && String.Equals(lastFrame.Get<String>("trace"), (frameInfo.Get<String>("trace"))))
                {
                    // Is this a recursion? Lets not accumulate everything but
                    // increase a counter on the last frame
                    lastFrame.Set<Int32>("count", lastFrame.Get<Int32>("count") + 1);
                }
                else
                {
                    // Add new frame to the list
                    frames.Call<bool>("add", frameInfo);
                    lastFrame = frameInfo;
                }
            }
            return frames;
        }
        
        public static void OnHandleUnresolvedException(object sender, System.UnhandledExceptionEventArgs args)
        {
            if (args == null || args.ExceptionObject == null)
                return;

            var exception = args.ExceptionObject as Exception;
            if (exception != null)
            {
                HandleException(exception, false);
            }
        }
        
        /// <summary>
        /// Initialize Bugsee SDK with custom parameters.
        /// </summary>
        /// <param name="appToken">Application token</param>
        /// <param name="options">Custom parameters</param>
        public static void Launch(string appToken, BugseeLaunchOptions options = null)
        {
            var launchOptions = options == null ? AndroidLaunchOptions.GetDefaultOptions() : options;
            var serializedOptions = launchOptions.SerializeOptions();
            Launch(appToken, serializedOptions);
        }
        
        public static void Launch(string appToken, IDictionary<string, object> options) {
            var wrapperInfo = new Dictionary<string, object>();
            wrapperInfo.Add("type", "UNITY");
            wrapperInfo.Add("version", BugseePluginVersion.BUGSEE_PLUGIN_VERSION);
            wrapperInfo.Add("build", BugseePluginVersion.BUGSEE_PLUGIN_BUILD);
            wrapperInfo.Add("unity_version", Application.unityVersion);
            options.Add("wrapper_info", wrapperInfo);
            
            bugseeAdapterClass.CallStatic("launch", activityObj, appToken, Serialize(options));
            if (!isBugseeEnabled) 
            {
                RegisterExceptionHandlers();
                SceneManager.activeSceneChanged += OnSceneChanged;
                isBugseeEnabled = true;
            }
        }

        public static void ShowReport(string summary = "", string description = "", BugseeSeverityLevel severity = BugseeSeverityLevel.High) 
        {
            bugseeAdapterClass.CallStatic("showReportDialog", summary, description, (int)severity);
        }

        public static void Pause()
        {
            bugseeAdapterClass.CallStatic("pause");
        }
        
        public static void Resume()
        {
            bugseeAdapterClass.CallStatic("resume");
        }
        
        public static void Event(string name, IDictionary<string, object> parameters = null) 
        {
            bugseeAdapterClass.CallStatic("event", name, Serialize(parameters));
        }
        
        public static void Trace(string name, object value) 
        {
            string serializedVal = Serialize(value);
            string result = "{\"" + VALUE_NAME + "\":" + serializedVal + "}";
            bugseeAdapterClass.CallStatic("trace", name, result);
        }
        
        public static void Upload(string summary, string description = "", BugseeSeverityLevel severity = BugseeSeverityLevel.High) 
        {
            bugseeAdapterClass.CallStatic("upload", summary, description, (int)severity);
        }
        
        public static void LogError(string message) 
        {
            // Not implemented in native Android Bugsee library.
        }
        
        public static void Log(string message, BugseeLogLevel level = BugseeLogLevel.Debug) 
        {
            bugseeAdapterClass.CallStatic("log", message, (int)level);
        }
        
        public static void ShowFeedbackUI() 
        {
            bugseeAdapterClass.CallStatic("showFeedbackActivity");
        }
        
        public static void SetFeedbackGreetingMessage (string message) 
        {
            bugseeAdapterClass.CallStatic("setDefaultFeedbackGreeting", message);
        }
        
        public static void Assert(bool condition, string description = "") 
        {
            // Not implemented in native Android Bugsee library.
        }
        
        private static void RegisterExceptionHandlers() 
        {
            System.AppDomain.CurrentDomain.UnhandledException += OnHandleUnresolvedException;
        }
        
        public static bool SetAttribute(string key, object val) 
        {
            if (key == null || key.Length == 0)
                return false;
            
            string serializedVal = Serialize(val);
            string result = "{\"" + VALUE_NAME + "\":" + serializedVal + "}";
            bugseeAdapterClass.CallStatic("setAttribute", key, result);
            return true;
        }
        
        public static object GetAttribute(string key) 
        { 
            string strValue = bugseeAdapterClass.CallStatic<string>("getAttribute", key); 
            if (strValue == null)
                return null;
                
            return DeserializeValue(strValue);
        }
        
        public static bool ClearAttribute(string key) 
        { 
            if (key == null || key.Length == 0)
                return false;
                
            bugseeAdapterClass.CallStatic("clearAttribute", key); 
            return true;
        }
        
        public static void SetEmail(string email) 
        {
            bugseeAdapterClass.CallStatic("setEmail", email);
        }
        
        public static string GetEmail() 
        { 
            return bugseeAdapterClass.CallStatic<string>("getEmail"); 
        }
            
        public static void ClearEmail() 
        {
            bugseeAdapterClass.CallStatic("setEmail", null);
        }

        public static void TestExceptionCrash ()
        {
            try {
                bugseeAdapterClass.CallStatic("throwException");
            } catch (Exception ex) 
            {
                LogException(ex);
            }
        }
        
        public static void TestSignalCrash() {}

        private static object DeserializeValue(string jsonString)
        {
            int prefixLength = "{\"value\":".Length;
            int postfixLength = 1;
            if (jsonString == null || jsonString.Length <= prefixLength + postfixLength)
                return jsonString; // Too short to be in valid format.

            string valueString = jsonString.Substring(prefixLength, jsonString.Length - prefixLength - postfixLength);
            // Don't try to parse complex data structures - they were already converted to string in SetAttribute() method (at least on Android).
            if (valueString.StartsWith("{") || valueString.StartsWith("["))
                return valueString;

            if (valueString.Length > 2 && valueString.StartsWith("\"") && valueString.EndsWith("\""))
                return valueString.Substring(1, valueString.Length - 2);

            bool boolValue;
            if (Boolean.TryParse(valueString, out boolValue))
                return boolValue;

            int intValue;
            if (Int32.TryParse(valueString, out intValue))
                return intValue;

            long longValue;
            if (Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue))
                return longValue;

            var cultures = new List<CultureInfo>() { CultureInfo.InvariantCulture, CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture };
            float floatValue;
            foreach (var culture in cultures)
            {
                if (Single.TryParse(valueString, NumberStyles.Number, culture, out floatValue))
                    return floatValue;
            }

            double doubleValue;
            foreach (var culture in cultures)
            {
                if (Double.TryParse(valueString, NumberStyles.Number, culture, out doubleValue))
                    return doubleValue;
            }

            // Unknown format.
            return valueString;
        }
        
        #endregion
#else // Other not supported platrofms, just empty methods
        #region Other platforms
        
        protected static void HandleException(string logString, string stackTrace, bool handled)
        {
        }
        
        /// <summary>
        /// Initialize Bugsee SDK with custom parameters.
        /// </summary>
        /// <param name="appToken">Application token</param>
        /// <param name="options">Custom parameters</param>
        public static void Launch(string appToken, BugseeLaunchOptions options = null)
        {
        }
        
        /// <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)
        {
            if (!isBugseeEnabled) // remove warnign from console
            {
                isBugseeEnabled = true;
            }
        }

        /// <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 = "", BugseeSeverityLevel severity = BugseeSeverityLevel.High)
        {
        }

        /// <summary>
        /// Pause bugsee video and loggers
        /// </summary>
        public static void Pause ()
        {
        }
        /// <summary>
        /// Resume bugsee video and loggers
        /// </summary>
        public static void 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)
        {
        }
        /// <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)
        {
        }
        /// <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)
        {
        }
        /// <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)
        {
        }
        /// <summary>
        /// Upload report with exception.
        /// </summary>
        /// <param name="exception">This exception (it's callstack and name) will be shown on the web</param>
        public static void LogException(Exception exception)
        {
        }
        /// <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)
        {
        }

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

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

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

        private static void RegisterExceptionHandlers ()
        {
        }

        /// <summary>
        /// Emulate native exception crash
        /// </summary>
        public static void TestExceptionCrash (){}
        /// <summary>
        /// Emulate native signal crash
        /// </summary>
        public static void TestSignalCrash() {}
        /// <summary>
        /// Set user attribute
        /// </summary>
        /// <param name="key">Name of attribute</param>
        /// <param name="val">Attribute value</param>
        public static bool SetAttribute(string key, object val) { return true; }
        /// <summary>
        /// Get user attribute
        /// </summary>
        /// <param name="key">Name of attribute</param>
        public static object GetAttribute(string key) { return null; }
        /// <summary>
        /// Clear attribute with given key
        /// </summary>
        /// <param name="key">Name of attribute</param>
        public static bool ClearAttribute(string key) { return true; }

        /// <summary>
        /// Set users email
        /// </summary>
        /// <param name="email">email</param>
        public static void SetEmail(string email) {}
        public static string GetEmail() { return null; }
        public static void ClearEmail() {}
        #endregion
#endif


    private static string GetPathBasedOnOS()
    {
        if (Application.isEditor)
            return "file://" + Application.persistentDataPath + "/";
//        else if (Application.isWebPlayer)
//            return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/";
        else if (Application.isMobilePlatform || Application.isConsolePlatform)
            return Application.persistentDataPath;
        else // For standalone player.
            return "file://" + Application.persistentDataPath + "/";
    }

    static public string CreateDirectory()
    {
        // Choose the output path according to the build target.
        string outputPath = Path.Combine(GetPathBasedOnOS(), "bugsee_tmp");
        if (!Directory.Exists(outputPath))
            Directory.CreateDirectory(outputPath);

        return outputPath;
    }

    public void AttachmentsForReport(string reportInfoRaw)
    {
            string result = null;
            if (OnAttachmentForReport != null)
            {
                 BugseeReport report;
                var values = reportInfoRaw.Split(',');
                report.type = values[0];
                report.severityLevel = (BugseeSeverityLevel)Int32.Parse(values[1]);
                var attachments = OnAttachmentForReport(report);

                if (attachments.Count > 0)
                {
                    var dirPath = CreateDirectory();

                    int i = 0;
                    result = "[";
                    foreach (var attachment in attachments){
                        if (attachment.data.Length < 1) continue;

                        var aName = attachment.name;
                        var aFilename = attachment.filename;

                        if (aName == null) aName = i.ToString();
                        if (aFilename == null) aFilename = name;
    
                        var toFile = dirPath + "/attachment_" + i.ToString();

                        var isWrited = true;
                        try
                        {
                            FileStream fileStream = new FileStream(toFile, FileMode.Create);
                            BinaryWriter writer = new BinaryWriter(fileStream);
                            writer.Write(attachment.data);
                            writer.Close();
                        }catch (Exception e){
                            isWrited = false;
                            Debug.Log("Attachment write exception - " + e);
                        }

                        if (isWrited){
                            if (i++ > 0) result += ", ";
                            var escapedPath = Uri.EscapeUriString(toFile);
                            result += "{\"name\":\"" + aName + "\",\"filename\":\"" + aFilename + "\",\"path\":\"" + escapedPath + "\"}";
                        }

                    }

                    result += "]";
                }
            }

        // Completion result must be sended anyway, even if it's null
#if UNITY_IPHONE && !UNITY_EDITOR
        _bugsee_setAttachmentsForReport(result);
#endif
    }

    }
}
