﻿/* 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 System.Collections.Generic;
using UnityEngine;
using System.Linq;

namespace ItSeez3D.AvatarSdk.Core
{
	public enum PipelineType
	{
		/// <summary>
		/// Standard pipeline (bald head with the neck, supports different haircuts, supports blendshapes).
		/// "animated_face" pipeline, "base/legacy" pipeline subtype
		/// </summary>
		FACE,

		/// <summary>
		/// Face pipeline with the cartoonish stylization
		/// "animated_face" pipeline, "indie/legacy_styled" pipeline subtype
		/// </summary>
		STYLED_FACE,

		/// <summary>
		/// Pipeline that generates head with hair and shoulders (supports blendshapes)
		/// "head_1.2" pipeline, "base/mobile" pipeline subtype
		/// </summary>
		HEAD,

		/// <summary>
		/// "head_2.0" pipeline, "head/mobile" pipeline subtype
		/// </summary>
		HEAD_2_0,

		/// <summary>
		/// "head_2.0" pipeline, "bust/mobile" pipeline subtype
		/// </summary>
		BUST_2_0
	}

	/// <summary>
	/// Simple extension for getting traits of pipeline type
	/// </summary>
	public static class PipelineTypeExtensions
	{
		public static PipelineTypeTraits Traits(this PipelineType pipelineType)
		{
			return (PipelineTypeTraits)pipelineType;
		}
	}

	/// <summary>
	/// Class that responsible for keeping instances of pipeline traits
	/// </summary>
	public class PipelineTraitsFactory
	{
		private static TraitsFactory<PipelineTypeTraits> traits = null;
		public static TraitsFactory<PipelineTypeTraits> Instance
		{
			get
			{
				if(traits == null)
				{
					traits = new TraitsFactory<PipelineTypeTraits>();
					traits.TraitsDictionary = new Dictionary<PipelineType, PipelineTypeTraits>();
					traits.TraitsDictionary.Add(PipelineType.BUST_2_0, new Bust2Traits());
					traits.TraitsDictionary.Add(PipelineType.HEAD_2_0, new Head2Traits());
					traits.TraitsDictionary.Add(PipelineType.FACE, new FaceTraits());
					traits.TraitsDictionary.Add(PipelineType.HEAD, new HeadTraits());
					traits.TraitsDictionary.Add(PipelineType.STYLED_FACE, new StyledFaceTraits());
				}
				return traits;
			}
		}
	}

	/// <summary>
	/// Generalized keeper of traits (for convenient work with traits extensions, e.g. SamplePipelineTraits)
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class TraitsFactory<T> where T : PipelineTypeTraits
	{
		public Dictionary<PipelineType, T> TraitsDictionary = null;
		public bool IsHaircutSupported(string pipeline)
		{
			return TraitsDictionary.FirstOrDefault(t => t.Value.PipelineTypeName.Equals(pipeline)).Value.HaircutsSupported;
		}

		public T GetTraitsFromPipelineName(string pipelineName)
		{
			T result = null;
			result = TraitsDictionary.FirstOrDefault(element => element.Value.PipelineTypeName.Equals(pipelineName)).Value;
			if (result == null)
				Debug.LogErrorFormat("Unable to get PipelineTypeTraits for pipeline: {0}", pipelineName);
			return result;
		}
 
		public T GetTraitsFromPipelineName(string pipelineName, string pipelineSubtypeName)
		{
			T result = null;
			result = TraitsDictionary.FirstOrDefault(element => element.Value.PipelineTypeName.Equals(pipelineName) && element.Value.PipelineSubtypeName.Equals(pipelineSubtypeName)).Value;
			if (result == null)
				Debug.LogErrorFormat("Unable to get PipelineTypeTraits for pipeline: {0}, pipelineSubtype: {1}", pipelineName, pipelineSubtypeName);
			return result;
		}

		public T GetTraitsFromAvatarCode(string avatarCode)
		{
			ModelInfo modelInfo = CoreTools.GetAvatarModelInfo(avatarCode);
			return GetTraitsFromPipelineName(modelInfo.pipeline, modelInfo.pipeline_subtype);
		}

		public T GetTraits(PipelineType pipelineType)
		{
			return TraitsDictionary[pipelineType];
		}
	}

	/// <summary>
	/// Traits class contains properties of pipeline that may be used anywhere. For the sake of separation of concerns class may be extended 
	/// (in a way SamplePipelineTraits extends it for providing pipeline type's specific settings for all samples)
	/// </summary>
	public abstract class PipelineTypeTraits
	{
		public abstract string PipelineTypeName { get; }
		public abstract string PipelineSubtypeName { get; }
		public virtual string GetHaircutPlyFilename(string avatarcode, string haircutId)
		{
			return AvatarSdkMgr.Storage().GetHaircutFilename(haircutId, HaircutFile.HAIRCUT_MESH_PLY);
		}
		public virtual string GetHaircutTextureFilename(string avatarcode, string haircutId)
		{
			return AvatarSdkMgr.Storage().GetHaircutFilename(haircutId, HaircutFile.HAIRCUT_TEXTURE);
		}
		public virtual string GetHaircutPointCloudFilename(string avatarCode, string haircutId)
		{
			return AvatarSdkMgr.Storage().GetAvatarHaircutPointCloudFilename(avatarCode, haircutId);
		}
		public abstract bool HaircutsSupported { get; }
		public abstract string DisplayName { get; }
		public abstract PipelineType Type { get; }
		public static explicit operator PipelineTypeTraits(PipelineType pipelineType)
		{
			return PipelineTraitsFactory.Instance.GetTraits(pipelineType);
		}
	}

	public abstract class Head2AbstractTraits : PipelineTypeTraits
	{
		private static string BASE_GENERATED_HAIRCUT = "base/generated";
		private static string BASE_GENERATED_HAIRCUT_SHORT = "generated";
		public override string PipelineTypeName { get { return "head_2.0"; } }
		public override bool HaircutsSupported { get { return true; } }
		private static bool isNativeHaircut(string id)
		{
			return id.Equals(BASE_GENERATED_HAIRCUT) || id.Equals(BASE_GENERATED_HAIRCUT_SHORT);
		}
		public override string GetHaircutPointCloudFilename(string avatarCode, string haircutId)
		{
			if (isNativeHaircut(haircutId))
			{
				return "";
			}
			else
			{
				return AvatarSdkMgr.Storage().GetAvatarHaircutPointCloudFilename(avatarCode, haircutId);
			}
		}
		public override string GetHaircutTextureFilename(string avatarCode, string haircutId)
		{
			if (isNativeHaircut(haircutId))
			{
				return AvatarSdkMgr.Storage().GetAvatarHaircutFilename(avatarCode, haircutId, HaircutFile.HAIRCUT_TEXTURE);
			}
			else
			{
				return base.GetHaircutTextureFilename(avatarCode, haircutId);
			}
		}
		public override string GetHaircutPlyFilename(string avatarCode, string haircutId)
		{
			if (isNativeHaircut(haircutId))
			{
				return AvatarSdkMgr.Storage().GetAvatarHaircutFilename(avatarCode, haircutId, HaircutFile.HAIRCUT_MESH_PLY);
			}
			else
			{
				return base.GetHaircutPlyFilename(avatarCode, haircutId);
			}
		}
	}

	public class Head2Traits : Head2AbstractTraits
	{
		public sealed override string PipelineSubtypeName { get { return "head/mobile"; } }
		public sealed override string DisplayName { get { return "Head 2.0"; } }
		public sealed override PipelineType Type { get { return PipelineType.HEAD_2_0; } }
	}
	public class Bust2Traits : Head2AbstractTraits
	{
		public sealed override string PipelineSubtypeName { get { return "bust/mobile"; } }
		public sealed override string DisplayName { get { return "Bust 2.0"; } }
		public sealed override PipelineType Type { get { return PipelineType.BUST_2_0; } }
	}
	public class FaceTraits : PipelineTypeTraits
	{
		public sealed override string PipelineTypeName { get { return "animated_face"; } }
		public sealed override string PipelineSubtypeName { get { return "base/legacy"; } }
		public sealed override bool HaircutsSupported { get { return true; } }
		public sealed override string DisplayName { get { return "Animated Face"; } }
		public sealed override PipelineType Type { get { return PipelineType.FACE; } }
	}
	public class HeadTraits : PipelineTypeTraits
	{
		public override string PipelineTypeName { get { return "head_1.2"; } }
		public override string PipelineSubtypeName { get { return "base/legacy"; } }
		public override bool HaircutsSupported { get { return false; } }
		public override string DisplayName { get { return "Head 1.2"; } }
		public override PipelineType Type { get { return PipelineType.HEAD; } }
	}
	public class StyledFaceTraits : PipelineTypeTraits
	{
		public sealed override string PipelineTypeName { get { return "animated_face"; } }
		public sealed override string PipelineSubtypeName { get { return "indie/legacy_styled"; } }
		public sealed override bool HaircutsSupported { get { return true; } }
		public sealed override string DisplayName { get { return "Styled Face"; } }
		public sealed override PipelineType Type { get { return PipelineType.STYLED_FACE; } }
	}
}
