﻿/* 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>, June 2019
*/

using System;
using UnityEngine;

namespace ItSeez3D.AvatarSdk.Core
{
	/// <summary>
	/// This class's goal is to struggle problems with Unity's Texture2D usage in separate thread.
	/// </summary>
	public class ImageWrapper
	{
		public const int NumberOfChannels = 3;
		public int Stride
		{
			/* There are no strides in Unity */
			get
			{
				int stride = Width * NumberOfChannels;
				/*if (stride % 4 != 0)
				{
					stride += (4 - (stride % 4));
				}*/
				return stride;
			}
		}

		public bool IsEqualSize (ImageWrapper img)
		{
			return (this.Width == img.Width && this.Height == img.Height);
		}

		public ImageWrapper(Texture2D texture)
		{
			Width = texture.width;
			Height = texture.height;
			if (texture.format == TextureFormat.RGB24)
			{
				Data = texture.GetRawTextureData();
			} else
			{
				Texture2D temp = new Texture2D(texture.width, texture.height, TextureFormat.RGB24, false);
				temp.SetPixels(texture.GetPixels());
				Data = temp.GetRawTextureData();
			}
		}

		

		public ImageWrapper(int w, int h)
		{
			Width = w;
			Height = h;
			Data = new byte[w * h * NumberOfChannels];
		}

		/// <summary>
		/// Full copy
		/// </summary>
		/// <param name="src"></param>
		public ImageWrapper(ImageWrapper src)
		{
			Width = src.Width;
			Height = src.Height;
			Data = (byte []) src.Data.Clone();
		}
		public Texture2D ToTexture2D()
		{
			var texture = new Texture2D(Width, Height, TextureFormat.RGB24, false);
			texture.LoadRawTextureData(Data);
			texture.Apply();
			return texture;
		}

		public void Resize(int w, int h)
		{
			Width = w;
			Height = h;
			Data = new byte[w * h * NumberOfChannels];
		}

		public bool TryCopyData(ImageWrapper from)
		{
			if(IsEqualSize(from))
			{
				from.Data.CopyTo(this.Data, 0);
				return true;
			}
			else
			{
				return false;
			}
		}
		public byte[] Data { get; set; }
		public int Width;
		public int Height;
	}

	public class ImageUtils 
	{
		public static void ResizeImagePyramidal(ImageWrapper srcImage, ImageWrapper dstImage)
		{
			const double pyrScale = 0.5;
			ImageWrapper resizedImage = new ImageWrapper(srcImage);

			while (Convert.ToInt32(resizedImage.Width * pyrScale) > dstImage.Width && 
				   Convert.ToInt32(resizedImage.Height * pyrScale) > dstImage.Height)
			{
				Interpolation.BicubicInterpolation(resizedImage, resizedImage, Convert.ToInt32(srcImage.Width * pyrScale), Convert.ToInt32(srcImage.Height * pyrScale));
			}
			Interpolation.BicubicInterpolation(resizedImage, dstImage);
		}

		public static AsyncRequest<ImageWrapper> DownscaleImageAsync(byte[] srcImgBuffer, int minSide)
		{
			Texture2D texture = new Texture2D(1, 1);
			texture.LoadImage(srcImgBuffer);
			int minDim = Math.Min(texture.width, texture.height);

			int textureWidth = texture.width;
			int textureHeight = texture.height;

			ImageWrapper srcImg = new ImageWrapper(texture);

			Func<ImageWrapper> scaleFunc = () =>
			{
				if (minDim > minSide)
				{
					float scale = minSide / (float)minDim;
					ImageWrapper dstImg = new ImageWrapper(Convert.ToInt32(textureWidth * scale), Convert.ToInt32(textureHeight * scale));
					ResizeImagePyramidal(srcImg, dstImg);
					return dstImg;
				}
				else
				{
					return null;
				}
			};
			AsyncRequest<ImageWrapper> request = new AsyncRequestThreaded<ImageWrapper>(() => scaleFunc(), "Resampling");
			AvatarSdkMgr.SpawnCoroutine(request.Await());
			return request;
		}
	}
}
