text: ﻿//Copyright © 2014 Sony Computer Entertainment America LLC. See License.txt.
  
  using System;
  using System.Collections.Generic;
  using System.Windows.Forms;
  using System.Linq;
  
  using Sce.Atf;
  using Sce.Atf.VectorMath;
  using Sce.Atf.Adaptation;
  using Sce.Atf.Dom;
  using Sce.Atf.Rendering;
  
  using Vector3 = Sce.Atf.VectorMath.Vec3F;
  
  namespace LevelEditorCore
  {
      /// <summary>
      /// LevelEditor utility methods</summary>
      /// <remarks>These methods did not naturally fit into an existing class or service
      /// and were therefore added here to avoid duplicated code. They should be reviewed
      /// from time to time and moved into a more fitting class or service if possible.
      /// General, application-independent and frequently used methods should be considered
      /// for promotion into ATF.</remarks>
      public static class Util
      {
  
          /// <summary>
          /// Gets the lineage of this object, starting with the object, and ending with
          /// the root object.</summary>
          public static IEnumerable<DomNode> Lineage(object obj)
          {
              Path<object> path = obj as Path<object>;
              if (path != null)
              {
                  for (int index = path.Count - 1; index >= 0; index--)
                  {
                      DomNode node = path[index] as DomNode;
                      if (node != null)
                          yield return node;
                  }
              }
              else
              {
                  DomNode node = obj as DomNode;
                  foreach (DomNode childNode in node.Lineage)
                  {
                      yield return childNode;
                  }
              }
          }
  
          /// <summary>
          /// Gets the path of a DomNode and converts it to an AdaptablePath<object></summary>
          /// <param name="domNode">DomNode</param>
          /// <returns>Object path to the specified DomNode</returns>
          public static AdaptablePath<object> AdaptDomPath(DomNode domNode)
          {
              List<object> path = new List<object>();
              while (domNode != null)
              {
                  foreach (DomNode pnode in domNode.Lineage)
                      path.Add(pnode);
  
                  domNode = null;
                  IGame game = Adapters.As<IGame>(path.Last());
                  if (game != null && game.Parent != null)
                  {
                      DomNode gameRef = game.Parent.Cast<DomNode>();                    
                      path[path.Count - 1] = gameRef;
                      domNode = gameRef.Parent;                    
                  }
              }
              path.Reverse();            
              AdaptablePath<object> adaptablePath = new AdaptablePath<object>(path);
              return adaptablePath;
          }
  
  
         
  
          /// <summary>
          /// Conversion function for drag-drop operations</summary>
          /// <param name="sourceObject">source object</param>        
          /// <returns>IEnumerable of objects </returns>
          public static IEnumerable<object> ConvertData(object sourceObject, bool copydataobject)
          {
              IResourceService resourceService = Globals.ResourceService;
              IEnumerable<object> objectlist = sourceObject as IEnumerable<object>;
              IDataObject dataObject = sourceObject as IDataObject;
              DomNode domNode = Adapters.As<DomNode>(sourceObject);
              if (domNode != null)
              {
                  yield return domNode;
              }
              else if (objectlist != null)
              {
                  foreach (object obj in objectlist)
                      yield return obj;
              }
              else if (dataObject != null)
              {                               
                  DomNode[] domNodes = dataObject.GetData(typeof(DomNode[])) as DomNode[];
                  if (domNodes != null)
                  {
                      DomNode[] copies = copydataobject ? DomNode.Copy(domNodes) : domNodes;
                      foreach (DomNode node in copies)
                          yield return node;
                  }
  
                  object[] objects = dataObject.GetData(typeof(object[])) as object[];
                  if (objects != null)
                  {
                      foreach (object node in objects)
                          yield return node;                    
                  }
  
                  string[] files = dataObject.GetData(DataFormats.FileDrop) as string[];
                  if (files != null && resourceService != null)
                  {
                      foreach (string file in files)
                      {
                          IResource resource = resourceService.Load(new Uri(file));
                          if (resource != null)
                              yield return resource; 
                      }
                  }
              }            
          }
  
          /// <summary>
          /// Gets the palette image for the specified type</summary>
          /// <remarks>If there is no image for the specified type, it iterates
          /// through the base class hierarchy to find one</remarks>
          /// <param name="nodeType">DomNodeType</param>
          /// <param name="imageList">ImageList to use for finding the image</param>
          /// <returns>Index of the image if found, -1 if unsuccessful</returns>
          public static int GetTypeImageIndex(DomNodeType nodeType, ImageList imageList)
          {
              int imageIndex = -1;
              if (nodeType != null)
              {
                  NodeTypePaletteItem paletteItem = nodeType.GetTag<NodeTypePaletteItem>();
                  if (paletteItem != null)
                      imageIndex = imageList.Images.IndexOfKey(paletteItem.ImageName);
              }            
              return imageIndex;
          }
  
  
          /// <summary>
          /// Creates Billboard matrix from the given parameters.</summary>        
          public static void CreateBillboard(Matrix4F matrix, Vec3F objectPos, Vec3F camPos, Vec3F camUp, Vector3 camLook)
          {
  
              Vector3 look = camPos - objectPos;
              float len = look.LengthSquared;
              if (len < 0.0001f)
              {
                  look = -camLook;
              }
              else
              {
                  look.Normalize();
              }
  
              Vector3 right = Vec3F.Cross(camUp, look);
              right.Normalize();
              Vector3 up = Vec3F.Cross(look, right);
  
              matrix.M11 = right.X;
              matrix.M12 = right.Y;
              matrix.M13 = right.Z;
              matrix.M14 = 0f;
              matrix.M21 = up.X;
              matrix.M22 = up.Y;
              matrix.M23 = up.Z;
              matrix.M24 = 0f;
              matrix.M31 = look.X;
              matrix.M32 = look.Y;
              matrix.M33 = look.Z;
              matrix.M34 = 0f;
              matrix.M41 = objectPos.X;
              matrix.M42 = objectPos.Y;
              matrix.M43 = objectPos.Z;
              matrix.M44 = 1f;            
          }
  
  
          /// <summary>
          /// Creates Billboard matrix from the given parameters.</summary>        
          public static Matrix4F CreateBillboard(Vec3F objectPos, Vec3F camPos, Vec3F camUp, Vector3 camLook)
          {
              Matrix4F matrix = new Matrix4F();
              CreateBillboard(matrix, objectPos, camPos, camUp, camLook);
              return matrix;
          }
  
  
          /// <summary>
          /// Calculates required scale such that when it applied to an 
          /// object. The object maintain a constant size in pixel regardless of 
          /// its distance from camera.
          /// Used for computing size of 3d manipulators.</summary>
          /// <param name="camera"></param>
          /// <param name="objectPosW"></param>
          /// <param name="sizeInPixels"></param>
          /// <param name="viewHeight"></param>        
          public static float CalcAxisScale(Camera camera,
              Vec3F objectPosW,
              float sizeInPixels,
              float viewHeight)
          {
              float worldHeight;
              // World height on origin's z value
              if (camera.Frustum.IsOrtho)
              {
                  worldHeight = (camera.Frustum.Top - camera.Frustum.Bottom);
              }
              else
              {
                  Matrix4F view = camera.ViewMatrix;
                  Vec3F objPosV;
                  view.Transform(objectPosW, out objPosV);
                  worldHeight = 2.0f * Math.Abs(objPosV.Z) * (float)Math.Tan(camera.Frustum.FovY / 2.0f);
              }
              return sizeInPixels * (worldHeight / viewHeight);
          }
          
          public static void ZoomOnSphere(Camera cam, Sphere3F sphere)
          {
              float nearZ = cam.PerspectiveNearZ;
              // todo refactor cam.ZoomOnSphere() 
              cam.ZoomOnSphere(sphere);
              cam.PerspectiveNearZ = nearZ;
          }
  
          public static string GetFilePath(string filter,string initialDir, bool isNew)
          {
              string filePath = null;
  
              if (Globals.AutomationMode)
              {
                  filePath = Globals.AutomationName;
              }
              else
              {
                  IWin32Window owner = Globals.MEFContainer.GetExportedValue<IWin32Window>();
                  if (isNew)
                  {
                      using (SaveFileDialog dlg = new SaveFileDialog())
                      {
                          dlg.Filter = filter;
                          dlg.OverwritePrompt = true;
                          dlg.InitialDirectory = initialDir;
                          if (dlg.ShowDialog(owner) == DialogResult.OK)
                              filePath = dlg.FileName;
                      }
                  }
                  else
                  {
                      using (OpenFileDialog dlg = new OpenFileDialog())
                      {
                          dlg.Filter = filter;
                          dlg.CheckFileExists = true;
                          dlg.InitialDirectory = initialDir;
                          if (dlg.ShowDialog(owner) == DialogResult.OK)
                              filePath = dlg.FileName;
                      }
                  }
              }
              return filePath;            
          }
  
  
          /// <summary>
          /// Find all the DomNodes of the type that is equal or derived 
          /// from the given type.
          /// if exact is true then only find DomNode that have exact type.        
          /// </summary>
          /// <param name="type">DomNodeType</param>
          /// <param name="exact">if true then only consider exact match,
          /// otherwise match any type that is equal or derived from the given type</param>
          /// <returns></returns>
          public static IEnumerable<DomNode> FindAll(DomNodeType type, bool exact = false)
          {
              if (type != null)
              {
                  IGameDocumentRegistry gameDocumentRegistry = Globals.MEFContainer.GetExportedValue<IGameDocumentRegistry>();
                  if (gameDocumentRegistry != null)
                  {
                      foreach (IGameDocument doc in gameDocumentRegistry.Documents)
                      {
                          var enumerable = doc.As<IEnumerableContext>();
                          if (enumerable != null)
                          {
                              foreach (DomNode childNode in enumerable.Items.AsIEnumerable<DomNode>())
                              {
                                  if ((exact && childNode.Type == type)
                                      || (!exact && type.IsAssignableFrom(childNode.Type)))
                                      yield return childNode;
                              }
                          }
                      }
                  }
              }
          }
  
          /// <summary>
          /// Searches ancestors of the given node to look for one that is locked.
          /// This is how nodes inherit the locked status from their parents.
          /// </summary>
          public static bool AncestorIsLocked(this DomNodeAdapter adapter)
          {
              var lockable = adapter.DomNode.Ancestry.AsIEnumerable<ILockable>().FirstOrDefault();
              return (lockable != null)?lockable.IsLocked:false;
          }
  
          /// <summary>
          /// Searches ancestors of the given node to check for visibility status.
          /// This is how nodes inherit the hidden status from their parent.
          /// </summary>
          public static bool AncestorIsVisible(this DomNodeAdapter adapter)
          {
              var visible = adapter.DomNode.Ancestry.AsIEnumerable<IVisible>().FirstOrDefault();
              return (visible != null)?visible.Visible:true;
          }
      }
  }
  