void OnGUI()
        {
            // workaround for OnEnable weirdness when initialising values
            if (logo == null)
            {
                logo = AssetDatabase.LoadAssetAtPath <Texture>("Assets/DeltaDNA/Editor/Resources/Logo.png");
            }
            if (styleFoldout == null)
            {
                styleFoldout = new GUIStyle(EditorStyles.foldout)
                {
                    fontStyle = FontStyle.Bold,
                    fontSize  = 12
                }
            }
            ;

            GUILayout.Label(logo, GUILayout.ExpandWidth(false));

            GUILayout.Space(WindowHelper.HEIGHT_SEPARATOR);
            scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);

            foldoutAnalytics = EditorGUILayout.Foldout(
                foldoutAnalytics,
                "Analytics",
                true,
                styleFoldout);
            if (foldoutAnalytics)
            {
                GUILayout.Label("Required", EditorStyles.boldLabel);

                analytics.environmentKeyDev = EditorGUILayout.TextField(
                    new GUIContent(
                        "Environment key (dev)",
                        "Enter your game's development environment key"),
                    analytics.environmentKeyDev);
                analytics.environmentKeyLive = EditorGUILayout.TextField(
                    new GUIContent(
                        "Environment key (live)",
                        "Enter your game's live environment key"),
                    analytics.environmentKeyLive);
                analytics.environmentKey = EditorGUILayout.Popup(
                    new GUIContent(
                        "Selected key",
                        "Select which environment key to use for the build"),
                    analytics.environmentKey,
                    new GUIContent[] {
                    new GUIContent("Development"),
                    new GUIContent("Live")
                });
                analytics.collectUrl = EditorGUILayout.TextField(
                    new GUIContent(
                        "Collect URL",
                        "Enter your game's collect URL"),
                    analytics.collectUrl);
                analytics.engageUrl = EditorGUILayout.TextField(
                    new GUIContent(
                        "Engage URL",
                        "Enter your game's engage URL"),
                    analytics.engageUrl);

                GUILayout.Label("Optional", EditorStyles.boldLabel);

                analytics.hashSecret = EditorGUILayout.TextField(
                    new GUIContent(
                        "Hash secret",
                        "Enter your game's hash secret if hashing is enabled"),
                    analytics.hashSecret);
                EditorGUI.BeginDisabledGroup(analytics.useApplicationVersion);
                analytics.clientVersion = EditorGUILayout.TextField(
                    new GUIContent(
                        "Client version",
                        "Enter your game's version or use the Editor value by enabling the checkbox below"),
                    analytics.clientVersion);
                EditorGUI.EndDisabledGroup();
                analytics.useApplicationVersion = GUILayout.Toggle(
                    analytics.useApplicationVersion,
                    new GUIContent(
                        "Use application version",
                        "Check to use the application/bundle version as set in the Editor"));
            }

            GUILayout.Space(WindowHelper.HEIGHT_SEPARATOR);

            foldoutNotifications = EditorGUILayout.Foldout(
                foldoutNotifications,
                "Android Notifications",
                true,
                styleFoldout);
            if (foldoutNotifications)
            {
                if (!AreAndroidNotificationsInProject())
                {
                    GUILayout.Label("Configuration not available due to notification dependencies not present in project.");
                }
                else
                {
                    notifications.appId = EditorGUILayout.TextField(
                        new GUIContent(
                            "Application ID",
                            "Enter the Application ID for your application in the Firebase Console"),
                        notifications.appId);
                    notifications.senderId = EditorGUILayout.TextField(
                        new GUIContent(
                            "Sender ID",
                            "Enter the Sender ID for your application in the Firebase Console"),
                        notifications.senderId);
                    notifications.listenerService = EditorGUILayout.TextField(
                        new GUIContent(
                            "Listener Service",
                            "If you have your own implementation of the NotificationListenerService you should set the field to use your own class"),
                        notifications.listenerService);

                    notifications.notificationIcon = EditorGUILayout.TextField(
                        new GUIContent(
                            "Notification Icon",
                            "The icon for the notification should be the name of the drawable resource, for example 'icon_notification' if you have 'icon_notification.png' in the 'res/drawable' folder"),
                        notifications.notificationIcon);
                    notifications.notificationTitle = EditorGUILayout.TextField(
                        new GUIContent(
                            "Notification Title",
                            "The title should be the string literal that you would like to appear in the notification, or a localisable string resource from the 'res/values' folder such as '@string/resource_name'"),
                        notifications.notificationTitle);
                }
            }

            GUILayout.Space(WindowHelper.HEIGHT_SEPARATOR);

            GUILayout.BeginHorizontal();
            foldoutSmartAds = EditorGUILayout.Foldout(
                foldoutSmartAds,
                "SmartAds",
                true,
                styleFoldout);
            GUILayout.FlexibleSpace();
            ads.On = GUILayout.Toggle(ads.On, "Enabled");
            GUILayout.EndHorizontal();
            if (foldoutSmartAds)
            {
                GUILayout.Label("Networks", EditorStyles.boldLabel);

                EditorGUI.BeginDisabledGroup(!ads.On);

                GUILayout.BeginHorizontal();
                GUILayout.Label(
                    "Name",
                    EditorStyles.boldLabel,
                    GUILayout.Width(WindowHelper.WIDTH_LABEL));

                foreach (var handler in ads.handlers)
                {
                    GUILayout.Label(
                        handler.platformVisible,
                        EditorStyles.boldLabel,
                        GUILayout.Width(WindowHelper.WIDTH_TOGGLE));
                }
                GUILayout.EndHorizontal();

                GUILayout.BeginVertical();
                foreach (IDictionary <string, object> network in ads.networks)
                {
                    GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(0.5f));

                    GUILayout.BeginHorizontal();

                    var name = network[AdsConfigurator.NAME] as string;
                    var hint = network[AdsConfigurator.INTEGRATION] as string != "manual"
                        ? "Network works out of the box, "
                        : "Contact [email protected] for setup details, ";

                    var types = network[AdsConfigurator.TYPE] as IList <object>;
                    if (types.Contains("interstitial") && types.Contains("rewarded"))
                    {
                        hint += "supports interstitial and rewarded ads";
                    }
                    else if (types.Contains("interstitial"))
                    {
                        hint += "supports interstitial ads";
                    }
                    else
                    {
                        hint += "supports rewarded ads";
                    }

                    GUILayout.Label(
                        new GUIContent(name, hint),
                        GUILayout.Width(WindowHelper.WIDTH_LABEL));

                    foreach (var handler in ads.handlers)
                    {
                        var value = network[handler.platform] as string;
                        if (value == null)
                        {
                            // empty label to fill the space
                            GUILayout.Label("", GUILayout.Width(WindowHelper.WIDTH_TOGGLE));
                            continue;
                        }

                        ads.enabled[handler][value] = GUILayout.Toggle(
                            ads.enabled[handler].ContainsKey(value)
                                ? ads.enabled[handler][value]
                                : true,
                            new GUIContent("", name + " for " + handler.platformVisible),
                            GUILayout.Width(WindowHelper.WIDTH_TOGGLE));
                    }

                    GUILayout.EndHorizontal();
                }
                GUILayout.EndVertical();

                GUILayout.Label("Additional", EditorStyles.boldLabel);

                EditorGUI.BeginDisabledGroup(!InitialisationHelper.IsDevelopment());
                ads.debugNotifications = GUILayout.Toggle(
                    ads.debugNotifications,
                    "Enable debug notifications in development builds");
                EditorGUI.EndDisabledGroup();

                EditorGUI.EndDisabledGroup();
            }

            GUILayout.Space(WindowHelper.HEIGHT_SEPARATOR);

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button(
                    "Apply",
                    GUILayout.Width(WindowHelper.WIDTH_BUTTON)))
            {
                Apply();
            }
            GUILayout.EndHorizontal();

            EditorGUILayout.EndScrollView();
        }
        protected override void PerformCheck(IList <DDNATuple <string, Severity> > problems)
        {
            if (File.Exists(ConfigurationWindow.CONFIG))
            {
                Configuration config;
                using (var stringReader = new StringReader(File.ReadAllText(ConfigurationWindow.CONFIG))) {
                    using (var xmlReader = XmlReader.Create(stringReader)) {
                        config = new XmlSerializer(
                            typeof(Configuration), new XmlRootAttribute("configuration"))
                                 .Deserialize(xmlReader) as Configuration;
                    }
                }

                if (string.IsNullOrEmpty(config.environmentKeyDev) &&
                    string.IsNullOrEmpty(config.environmentKeyLive))
                {
                    problems.Add(DDNATuple.New(
                                     "[Analytics] Environment key has not been configured.",
                                     Severity.ERROR));
                }
                if (string.IsNullOrEmpty(config.collectUrl))
                {
                    problems.Add(DDNATuple.New(
                                     "[Analytics] Collect URL has not been configured.",
                                     Severity.ERROR));
                }
                if (string.IsNullOrEmpty(config.engageUrl))
                {
                    problems.Add(DDNATuple.New(
                                     "[Analytics] Engage URL has not been configured.",
                                     Severity.ERROR));
                }

                if (InitialisationHelper.IsDevelopment() && config.environmentKey == 1)
                {
                    problems.Add(DDNATuple.New(
                                     "[Analytics] Using live environment key for a development build.",
                                     Severity.WARNING));
                }
                else if (!InitialisationHelper.IsDevelopment() && config.environmentKey == 0)
                {
                    problems.Add(DDNATuple.New(
                                     "[Analytics] Using dev environment key for a live build.",
                                     Severity.WARNING));
                }

                if (string.IsNullOrEmpty(config.clientVersion) && !config.useApplicationVersion)
                {
                    problems.Add(DDNATuple.New(
                                     "[Analytics] Client version has not been configured.",
                                     Severity.WARNING));
                }
            }
            else
            {
                problems.Add(DDNATuple.New(
                                 "[Analytics] Application has not been configured with Platform keys and URLs.",
                                 Severity.ERROR));
            }

            var androidLibs = MenuItems.ASSETS_PATH + "/Plugins/Android";

            if (Directory.Exists(androidLibs))
            {
                var files = Directory.GetFiles(androidLibs, "*.aar");
                if (files.Length > 1)
                {
                    problems.Add(DDNATuple.New(
                                     "[Notifications] Found multiple libraries in '" + androidLibs + "' folder. Please make sure it only contains the most recent 'android-sdk-notifications' AAR.  Also remove any play-services-*.aar and support-v4-*.aar, these are now handled by Google's Play Services Resolver.",
                                     Severity.ERROR));
                }
            }

            var androidManifest = "Assets/Plugins/Android/AndroidManifest.xml";

            if (File.Exists(androidManifest))
            {
                var manifest = XDocument.Parse(File.ReadAllText(androidManifest));

                if (manifest
                    .Descendants("permissions")
                    .Where(e => e.Attribute(NotificationsConfigurator.NAMESPACE_ANDROID + "name").Value.EndsWith("C2D_MESSAGE"))
                    .Count() > 0)
                {
                    problems.Add(DDNATuple.New(
                                     "[Notifications] Found invalid C2D_MESSAGE 'permission' entry in '" + androidManifest + "'. This entry should be removed for Firebase notifications.",
                                     Severity.ERROR));
                }
                if (manifest
                    .Descendants("uses-permission")
                    .Where(e => e.Attribute(NotificationsConfigurator.NAMESPACE_ANDROID + "name").Value.EndsWith("C2D_MESSAGE"))
                    .Count() > 0)
                {
                    problems.Add(DDNATuple.New(
                                     "[Notifications] Found invalid C2D_MESSAGE 'uses-permission' entry in '" + androidManifest + "'. This entry should be removed for Firebase notifications.",
                                     Severity.ERROR));
                }
                if (manifest
                    .Descendants("receiver")
                    .Where(e => e.Attribute(NotificationsConfigurator.NAMESPACE_ANDROID + "name").Value == "com.google.android.gms.gcm.GcmReceiver")
                    .Count() > 0)
                {
                    problems.Add(DDNATuple.New(
                                     "[Notifications] Found invalid GcmReceiver 'receiver' entry in '" + androidManifest + "'. This entry should be removed for Firebase notifications.",
                                     Severity.ERROR));
                }
                if (manifest
                    .Descendants("service")
                    .Where(e => e.Attribute(NotificationsConfigurator.NAMESPACE_ANDROID + "name").Value.StartsWith("com.deltadna.android.sdk.notifications"))
                    .Count() > 0)
                {
                    problems.Add(DDNATuple.New(
                                     "[Notifications] Found invalid deltaDNA notification 'service' entry in '" + androidManifest + "'. This entry should be removed for Firebase notifications.",
                                     Severity.ERROR));
                }

                manifest
                .Descendants("meta-data")
                .ToList()
                .ForEach(e => {
                    switch (e.Attribute(NotificationsConfigurator.NAMESPACE_ANDROID + "name").Value)
                    {
                    case NotificationsConfigurator.ATTR_ICON:
                        problems.Add(DDNATuple.New(
                                         "[Notifications] Found conflicting 'meta-data' entry for '" + NotificationsConfigurator.ATTR_ICON + "' in '" + androidManifest + "'. This entry should be removed and configured through the Editor menu instead.",
                                         Severity.ERROR));
                        break;

                    case NotificationsConfigurator.ATTR_TITLE:
                        problems.Add(DDNATuple.New(
                                         "[Notifications] Found conflicting 'meta-data' entry for '" + NotificationsConfigurator.ATTR_TITLE + "' in '" + androidManifest + "'. This entry should be removed and configured through the Editor menu instead.",
                                         Severity.ERROR));
                        break;

                    case "ddna_sender_id":
                        problems.Add(DDNATuple.New(
                                         "[Notifications] Found deprecated 'meta-data' entry for 'ddna_sender_id' in in '" + androidManifest + "'. This entry should be removed and configured through the Editor menu instead.",
                                         Severity.WARNING));
                        break;

                    case "ddna_start_launch_intent":
                        problems.Add(DDNATuple.New(
                                         "[Notifications] Found deprecated 'meta-data' entry for 'ddna_start_launch_intent' in in '" + androidManifest + "'. This entry should be removed.",
                                         Severity.WARNING));
                        break;
                    }
                });
            }

            if (File.Exists(NotificationsConfigurator.MANIFEST_XML_PATH))
            {
                var manifest = XDocument.Parse(File.ReadAllText(NotificationsConfigurator.MANIFEST_XML_PATH));

                if (manifest
                    .Descendants("service")
                    .First()
                    .Attribute(NotificationsConfigurator.NAMESPACE_ANDROID + "enabled")
                    .Value == "false")
                {
                    problems.Add(DDNATuple.New(
                                     "[Notifications] Android push notifications not enabled due to disabled service.",
                                     Severity.WARNING));
                }
            }

            if (File.Exists(NotificationsConfigurator.NOTIFICATIONS_XML_PATH))
            {
                new List <string> {
                    NotificationsConfigurator.ATTR_APP_ID,
                    NotificationsConfigurator.ATTR_SENDER_ID
                }
                .Except(XDocument
                        .Parse(File.ReadAllText(NotificationsConfigurator.NOTIFICATIONS_XML_PATH))
                        .Descendants("string")
                        .Select(e => e.Attribute("name").Value))
                .ToList()
                .ForEach(e => {
                    switch (e)
                    {
                    case NotificationsConfigurator.ATTR_APP_ID:
                        problems.Add(DDNATuple.New(
                                         "[Notifications] Application ID not set for Android push notifications.",
                                         Severity.WARNING));
                        break;

                    case NotificationsConfigurator.ATTR_SENDER_ID:
                        problems.Add(DDNATuple.New(
                                         "[Notifications] Sender ID not set for Android push notifications.",
                                         Severity.WARNING));
                        break;
                    }
                });
            }
        }