using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using VRC.Core;

namespace VRCSDK2
{
#if UNITY_EDITOR
    public class RuntimeBlueprintCreation : RuntimeAPICreation
    {
        public GameObject waitingPanel;
        public GameObject blueprintPanel;
        public GameObject errorPanel;

        public Text titleText;
        public InputField blueprintName;
        public InputField blueprintDescription;
        public RawImage bpImage;
        public Image liveBpImage;
        public Toggle shouldUpdateImageToggle;
        public Toggle contentSex;
        public Toggle contentViolence;
        public Toggle contentGore;
        public Toggle contentOther;
        public Toggle developerAvatar;
        public Toggle sharePrivate;
        public Toggle sharePublic;
        public Toggle tagFallback;

        public UnityEngine.UI.Button uploadButton;

        private ApiAvatar apiAvatar;

        new void Start()
        {
            if (!Application.isEditor || !Application.isPlaying)
                return;

            base.Start();

            var desc = pipelineManager.GetComponent<VRC.SDKBase.VRC_AvatarDescriptor>();
            desc.PositionPortraitCamera(imageCapture.shotCamera.transform);

            Application.runInBackground = true;
            UnityEngine.XR.XRSettings.enabled = false;

            uploadButton.onClick.AddListener(SetupUpload);

            shouldUpdateImageToggle.onValueChanged.AddListener(ToggleUpdateImage);

            Login();
        }

        void LoginErrorCallback(string obj)
        {
            VRC.Core.Logger.LogError("Could not log in - " + obj, DebugLevel.Always);
            blueprintPanel.SetActive(false);
            errorPanel.SetActive(true);
        }

        void Login()
        {
            if (!ApiCredentials.Load())
                LoginErrorCallback("Not logged in");
            else
                APIUser.FetchCurrentUser(
                    delegate (ApiModelContainer<APIUser> c)
                    {
                        pipelineManager.user = c.Model as APIUser;

                        ApiAvatar av = new ApiAvatar() { id = pipelineManager.blueprintId };
                        av.Get(false,
                            (c2) =>
                            {
                                VRC.Core.Logger.Log("<color=magenta>Updating an existing avatar.</color>", DebugLevel.API);
                                apiAvatar = c2.Model as ApiAvatar;
                                pipelineManager.completedSDKPipeline = !string.IsNullOrEmpty(apiAvatar.authorId);
                                SetupUI();
                            },
                            (c2) =>
                            {
                                VRC.Core.Logger.Log("<color=magenta>Creating a new avatar.</color>", DebugLevel.API);
                                apiAvatar = new ApiAvatar();
                                apiAvatar.id = pipelineManager.blueprintId;
                                pipelineManager.completedSDKPipeline = !string.IsNullOrEmpty(apiAvatar.authorId);
                                SetupUI();
                            });
                    }, (c) => {
                        LoginErrorCallback(c.Error);
                    });
        }

        void SetupUI()
        {
            if (!ValidateAssetBundleBlueprintID(apiAvatar.id))
            {
                blueprintPanel.SetActive(false);
                errorPanel.SetActive(true);
                OnSDKPipelineError("The asset bundle is out of date.  Please rebuild the scene using 'New Build'.", "The blueprint ID in the scene does not match the id in the asset bundle.");
                return;
            }

            if (APIUser.Exists(pipelineManager.user))
            {
                waitingPanel.SetActive(false);
                blueprintPanel.SetActive(true);
                errorPanel.SetActive(false);

                if (isUpdate)
                {
                    // bp update
                    if (apiAvatar.authorId == pipelineManager.user.id)
                    {
                        titleText.text = "Update Avatar";
                        // apiAvatar = pipelineManager.user.GetBlueprint(pipelineManager.blueprintId) as ApiAvatar;
                        blueprintName.text = apiAvatar.name;
                        contentSex.isOn = apiAvatar.tags.Contains("content_sex");
                        contentViolence.isOn = apiAvatar.tags.Contains("content_violence");
                        contentGore.isOn = apiAvatar.tags.Contains("content_gore");
                        contentOther.isOn = apiAvatar.tags.Contains("content_other");
                        developerAvatar.isOn = apiAvatar.tags.Contains("developer");
                        sharePrivate.isOn = apiAvatar.releaseStatus.Contains("private");
                        sharePublic.isOn = apiAvatar.releaseStatus.Contains("public");

                        tagFallback.isOn = apiAvatar.tags.Contains("author_quest_fallback");
                        switch (pipelineManager.fallbackStatus)
                        {
                            case PipelineManager.FallbackStatus.Valid:
                                tagFallback.transform.parent.gameObject.SetActive(true);
                                tagFallback.interactable = true;
                                tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback";
                                break;
                            case PipelineManager.FallbackStatus.InvalidPerformance:
                            case PipelineManager.FallbackStatus.InvalidRig:
                                tagFallback.transform.parent.gameObject.SetActive(true);
                                tagFallback.isOn = false; // need to remove tag on this upload, the updated version is not up-to-spec
                                tagFallback.interactable = false;
                                tagFallback.GetComponentInChildren<Text>().text = "(Not valid for Fallback use)";
                                break;
                            default:
                                tagFallback.transform.parent.gameObject.SetActive(false);
                                break;
                        }

                        blueprintDescription.text = apiAvatar.description;
                        shouldUpdateImageToggle.interactable = true;
                        shouldUpdateImageToggle.isOn = false;
                        liveBpImage.enabled = false;
                        bpImage.enabled = true;

                        ImageDownloader.DownloadImage(apiAvatar.imageUrl, 0, (Texture2D obj) => bpImage.texture = obj, null);
                    }
                    else // user does not own apiAvatar id associated with descriptor
                    {
                        Debug.LogErrorFormat("{0} is not an owner of {1}", apiAvatar.authorId, pipelineManager.user.id);
                        blueprintPanel.SetActive(false);
                        errorPanel.SetActive(true);
                    }
                }
                else
                {
                    titleText.text = "New Avatar";
                    shouldUpdateImageToggle.interactable = false;
                    shouldUpdateImageToggle.isOn = true;
                    liveBpImage.enabled = true;
                    bpImage.enabled = false;
                    tagFallback.isOn = false;

                    switch (pipelineManager.fallbackStatus)
                    {
                        case PipelineManager.FallbackStatus.Valid:
                            tagFallback.transform.parent.gameObject.SetActive(true);
                            tagFallback.interactable = true;
                            tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback";
                            break;
                        case PipelineManager.FallbackStatus.InvalidPerformance:
                        case PipelineManager.FallbackStatus.InvalidRig:
                            tagFallback.transform.parent.gameObject.SetActive(true);
                            tagFallback.interactable = false;
                            tagFallback.GetComponentInChildren<Text>().text = "(Not valid for Fallback use)";
                            break;
                        default:
                            tagFallback.transform.parent.gameObject.SetActive(false);
                            break;
                    }
                }
            }
            else
            {
                waitingPanel.SetActive(true);
                blueprintPanel.SetActive(false);
                errorPanel.SetActive(false);
            }

            if (APIUser.CurrentUser != null && APIUser.CurrentUser.hasSuperPowers)
                developerAvatar.gameObject.SetActive(true);
            else
                developerAvatar.gameObject.SetActive(false);
        }

        public void SetupUpload()
        {
            uploadTitle = "Preparing For Upload";
            isUploading = true;

            string abPath = UnityEditor.EditorPrefs.GetString("currentBuildingAssetBundlePath");

            string unityPackagePath = UnityEditor.EditorPrefs.GetString("VRC_exportedUnityPackagePath");

            UnityEditor.EditorPrefs.SetBool("VRCSDK2_scene_changed", true);
            UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_sex", contentSex.isOn);
            UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_violence", contentViolence.isOn);
            UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_gore", contentGore.isOn);
            UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_other", contentOther.isOn);

            if (string.IsNullOrEmpty(apiAvatar.id))
            {
                pipelineManager.AssignId();
                apiAvatar.id = pipelineManager.blueprintId;
            }

            string avatarId = apiAvatar.id;
            int version = isUpdate ? apiAvatar.version + 1 : 1;
            PrepareVRCPathForS3(abPath, avatarId, version, ApiAvatar.VERSION);

            if (!string.IsNullOrEmpty(unityPackagePath) && System.IO.File.Exists(unityPackagePath))
            {
                VRC.Core.Logger.Log("Found unity package path. Preparing to upload!", DebugLevel.All);
                PrepareUnityPackageForS3(unityPackagePath, avatarId, version, ApiAvatar.VERSION);
            }

            StartCoroutine(UploadNew());
        }

        IEnumerator UploadNew()
        {
            bool caughtInvalidInput = false;
            if (!ValidateNameInput(blueprintName))
                caughtInvalidInput = true;

            if (caughtInvalidInput)
                yield break;

            VRC.Core.Logger.Log("Starting upload", DebugLevel.Always);

            // upload unity package
            if (!string.IsNullOrEmpty(uploadUnityPackagePath))
            {
                yield return StartCoroutine(UploadFile(uploadUnityPackagePath, isUpdate ? apiAvatar.unityPackageUrl : "", GetFriendlyAvatarFileName("Unity package"), "Unity package",
                    delegate (string fileUrl)
                    {
                        cloudFrontUnityPackageUrl = fileUrl;
                    }
                ));
            }

            // upload asset bundle
            if (!string.IsNullOrEmpty(uploadVrcPath))
            {
                yield return StartCoroutine(UploadFile(uploadVrcPath, isUpdate ? apiAvatar.assetUrl : "", GetFriendlyAvatarFileName("Asset bundle"), "Asset bundle",
                    delegate (string fileUrl)
                    {
                        cloudFrontAssetUrl = fileUrl;
                    }
                ));
            }

            if (isUpdate)
                yield return StartCoroutine(UpdateBlueprint());
            else
                yield return StartCoroutine(CreateBlueprint());

            OnSDKPipelineComplete();
        }

        private string GetFriendlyAvatarFileName(string type)
        {
            return "Avatar - " + blueprintName.text + " - " + type + " - " + Application.unityVersion + "_" + ApiWorld.VERSION.ApiVersion +
                   "_" + VRC.Tools.Platform + "_" + API.GetServerEnvironmentForApiUrl();
        }

        List<string> BuildTags()
        {
            var tags = new List<string>();
            if (contentSex.isOn)
                tags.Add("content_sex");
            if (contentViolence.isOn)
                tags.Add("content_violence");
            if (contentGore.isOn)
                tags.Add("content_gore");
            if (contentOther.isOn)
                tags.Add("content_other");

            if (APIUser.CurrentUser.hasSuperPowers)
            {
                if (developerAvatar.isOn)
                    tags.Add("developer");
            }

            if (tagFallback.isOn)
                tags.Add("author_quest_fallback");

            return tags;
        }

        protected override IEnumerator CreateBlueprint()
        {
            yield return StartCoroutine(UpdateImage(isUpdate ? apiAvatar.imageUrl : "", GetFriendlyAvatarFileName("Image")));

            ApiAvatar avatar = new ApiAvatar
            {
                id = pipelineManager.blueprintId,
                authorName = pipelineManager.user.displayName,
                authorId = pipelineManager.user.id,
                name = blueprintName.text,
                imageUrl = cloudFrontImageUrl,
                assetUrl = cloudFrontAssetUrl,
                description = blueprintDescription.text,
                tags = BuildTags(),
                unityPackageUrl = cloudFrontUnityPackageUrl,
                releaseStatus = sharePublic.isOn ? "public" : "private"
            };

            bool doneUploading = false;
            bool wasError = false;

            avatar.Post(
                (c) =>
                {
                    ApiAvatar savedBP = (ApiAvatar)c.Model;
                    pipelineManager.blueprintId = savedBP.id;
                    UnityEditor.EditorPrefs.SetString("blueprintID-" + pipelineManager.GetInstanceID().ToString(), savedBP.id);

                    AnalyticsSDK.AvatarUploaded(savedBP, false);
                    doneUploading = true;
                },
                (c) =>
                {
                    Debug.LogError(c.Error);
                    SetUploadProgress("Saving Avatar", "Error saving blueprint.", 0.0f);
                    doneUploading = true;
                    wasError = true;
                });

            while (!doneUploading)
                yield return null;

            if (wasError)
                yield return new WaitUntil(() => UnityEditor.EditorUtility.DisplayDialog("VRChat SDK", "Error saving blueprint.", "Okay"));
        }

        protected override IEnumerator UpdateBlueprint()
        {
            bool doneUploading = false;

            apiAvatar.name = blueprintName.text;
            apiAvatar.description = blueprintDescription.text;
            apiAvatar.assetUrl = cloudFrontAssetUrl;
            apiAvatar.releaseStatus = sharePublic.isOn ? "public" : "private";
            apiAvatar.tags = BuildTags();
            apiAvatar.unityPackageUrl = cloudFrontUnityPackageUrl;

            if (shouldUpdateImageToggle.isOn)
            {
                yield return StartCoroutine(UpdateImage(isUpdate ? apiAvatar.imageUrl : "", GetFriendlyAvatarFileName("Image")));
                apiAvatar.imageUrl = cloudFrontImageUrl;
            }

            SetUploadProgress("Saving Avatar", "Almost finished!!", 0.8f);
            apiAvatar.Save(
                    (c) => { AnalyticsSDK.AvatarUploaded(apiAvatar, true); doneUploading = true; },
                    (c) => {
                        Debug.LogError(c.Error);
                        SetUploadProgress("Saving Avatar", "Error saving blueprint.", 0.0f);
                        doneUploading = true;
                    });

            while (!doneUploading)
                yield return null;
        }

        void ToggleUpdateImage(bool isOn)
        {
            if (isOn)
            {
                bpImage.enabled = false;
                liveBpImage.enabled = true;
            }
            else
            {
                bpImage.enabled = true;
                liveBpImage.enabled = false;
                ImageDownloader.DownloadImage(apiAvatar.imageUrl, 0, obj => bpImage.texture = obj, null);
            }
        }

        void OnDestroy()
        {
            UnityEditor.EditorUtility.ClearProgressBar();
            UnityEditor.EditorPrefs.DeleteKey("currentBuildingAssetBundlePath");
        }
    }
#endif
}


