﻿/* Copyright (C) Itseez3D, Inc. - All Rights Reserved
* You may not use this file except in compliance with an authorized license
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* UNLESS REQUIRED BY APPLICABLE LAW OR AGREED BY ITSEEZ3D, INC. IN WRITING, SOFTWARE DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED
* See the License for the specific language governing permissions and limitations under the License.
* Written by Itseez3D, Inc. <support@avatarsdk.com>, April 2017
*/

using ItSeez3D.AvatarSdk.Cloud;
using ItSeez3D.AvatarSdk.Core;
using ItSeez3D.AvatarSdkSamples.Core;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;

namespace ItSeez3D.AvatarSdkSamples.Cloud
{
	public class AvatarViewerWebgl : AvatarViewer
	{
		public Toggle faceToggle;

		// Code of the head avatar for WebGL demo
		private string headAvatarCode;

		// Code of the faca avatar for WebGL demo
		private string faceAvatarCode;

		private static AsyncRequest<AvatarData> faceAvatarRequest = null;

		private bool controlsEnabled = true;

#if UNITY_WEBGL
		[DllImport("__Internal")]
		private static extern void downloadFileInBrowser(byte[] buffer, int size, string fileName);
#endif

		public static void SetSceneParams(SceneParams sceneParams, AsyncRequest<AvatarData> avatarRequest)
		{
			initParams = sceneParams;
			faceAvatarRequest = avatarRequest;
		}

		public void OnPipelineCheckboxChanged(bool isChecked)
		{
			if (isChecked)
			{
				currentAvatarCode = faceToggle.isOn ? faceAvatarCode : headAvatarCode;
				StartCoroutine(ShowAvatar(currentAvatarCode));
			}
		}

		public void DownloadButtonClick()
		{
			StartCoroutine(PrepareObjModel());
		}

		protected override void InitilizeUIControls()
		{
			downloadButton.gameObject.SetActive(true);
			settingsPanel.SetActive(initParams.showSettings);
			animationsPanel.SetActive(initParams.useAnimations);
			blendshapesPanel.SetActive(!initParams.useAnimations);
			if (faceAvatarRequest != null)
			{
				headAvatarCode = currentAvatarCode;
				StartCoroutine(WaitFaceAvatarCalculations(faceAvatarRequest));
			}
			animations = animations.GetRange(0, 6);
		}

		protected override void ChangeControlsInteractability(bool isEnabled)
		{
			controlsEnabled = isEnabled;
			base.ChangeControlsInteractability(isEnabled);

			if (isEnabled && !faceAvatarRequest.IsDone && !faceAvatarRequest.IsError)
				faceToggle.interactable = false;
		}

		private IEnumerator WaitFaceAvatarCalculations(AsyncRequest<AvatarData> avatarRequest)
		{
			pipelinesPanel.SetActive(true);
			yield return avatarRequest;
			if (!avatarRequest.IsError)
			{
				faceAvatarCode = avatarRequest.Result.code;

				if (controlsEnabled)
					faceToggle.interactable = true;

				// preload haircuts previews beforehand
				var haircutsIdsRequest = GetHaircutsIdsAsync(faceAvatarCode);
				yield return haircutsIdsRequest;
				if (haircutsIdsRequest.Result != null)
					haircutsSelectingView.InitItems(faceAvatarCode, haircutsIdsRequest.Result.ToList(), avatarProvider);
			}
		}

		private IEnumerator PrepareObjModel()
		{
			ChangeControlsInteractability(false);
			AsyncRequest request = PrepareObjModelAsync();
			yield return Await(request, false);
			ChangeControlsInteractability(true);
		}

		private AsyncRequest PrepareObjModelAsync()
		{
			AsyncRequest request = new AsyncRequest(AvatarSdkMgr.Str(Strings.PreparingObjModel));
			AvatarSdkMgr.SpawnCoroutine(PrepareObjModelFunc(request));
			return request;
		}

		private IEnumerator PrepareObjModelFunc(AsyncRequest request)
		{
			yield return null;

			IPersistentStorage storage = AvatarSdkMgr.Storage();
			string objDirPath = storage.GetAvatarSubdirectory(currentAvatarCode, AvatarSubdirectory.OBJ_EXPORT);
			IOUtils.CleanDirectory(objDirPath);

			string modelFileName = Path.Combine(objDirPath, "model.obj");
			string textureFilename = Path.Combine(objDirPath, "model.png");
			TexturedMesh headMesh = new TexturedMesh(headObject.GetComponent<SkinnedMeshRenderer>().sharedMesh, storage.GetAvatarFilename(currentAvatarCode, AvatarFile.TEXTURE));

			yield return null;

			if (haircutObject != null)
			{
				string haircutName = GetCurrentHaircutName();
				string haircutTextureFile = HaircutsPersistentStorage.Instance.GetHaircutMetadata(haircutName, currentAvatarCode).Texture;
				var haircutRecoloring = GetComponent<HaircutRecoloring>();
				TexturedMesh haircutMesh = new TexturedMesh(haircutObject.GetComponent<MeshFilter>().mesh, new Texture2D(0,0));
				haircutMesh.texture = ImageUtils.RecolorTexture(haircutTextureFile, haircutRecoloring.CurrentColor, haircutRecoloring.CurrentTint);
				headMesh = MeshUtils.MergeMeshes(headMesh, haircutMesh);
			}

			ObjWriter.WriteMeshDataToObj(modelFileName, headMesh.mesh.vertices, headMesh.mesh.triangles, headMesh.mesh.uv, textureFilename);
			ImageUtils.SaveTextureToFile(headMesh.texture, textureFilename);

			var createZipRequest = CoreTools.ZipDirectoryAsync(objDirPath);
			yield return request.AwaitSubrequest(createZipRequest, 1.0f);
			if (request.IsError)
			{
				Debug.LogError("Unable to create ZIP archive");
				yield break;
			}
			byte[] archiveBytes = createZipRequest.Result;
#if UNITY_WEBGL
			downloadFileInBrowser(archiveBytes, archiveBytes.Length, "model.zip");
#elif UNITY_EDITOR || UNITY_STANDALONE_WIN
			string objZipFilePath = Path.Combine(objDirPath, "model.zip");
			File.WriteAllBytes(objZipFilePath, archiveBytes);
			System.Diagnostics.Process.Start(objDirPath);
#endif
			request.IsDone = true;
		}
	}
}
