context: using UnityEngine;
  using System.Collections;
  using System.Collections.Generic;
  
  /// <summary>
  /// UI logic. Manages all UI, raycasting, touch/mouse events passing and uiItem event notifications. 
  /// For any tk2dUIItem to work there needs to be a tk2dUIManager in the scene
  /// </summary>
  [AddComponentMenu("2D Toolkit/UI/Core/tk2dUIManager")]
  public class tk2dUIManager : MonoBehaviour
  {
      public static double version = 1.0;
      public static int releaseId = 0; // < -10000 = alpha, other negative = beta release, 0 = final, positive = final patch
  
      /// <summary>
      ///  Used to reference tk2dUIManager without a direct reference via singleton structure (will not auto-create and only one can exist at a time)
      /// </summary>
      private static tk2dUIManager instance;
      public static tk2dUIManager Instance
      {
          get
          {
              if (instance == null)
              {
                  instance = FindObjectOfType(typeof(tk2dUIManager)) as tk2dUIManager;
                  if (instance == null) {
                      GameObject go = new GameObject("tk2dUIManager");
                      instance = go.AddComponent<tk2dUIManager>();
                  }
              }
              return instance;
          }
      }
  	
  	public static tk2dUIManager Instance__NoCreate {
  		get {
  			return instance;
  		}
  	}
  	
      /// <summary>
      /// UICamera which raycasts for mouse/touch inputs are cast from
      /// </summary>
      [SerializeField]
  #pragma warning disable 649
  	private Camera uiCamera;
  #pragma warning restore 649
  	
      public Camera UICamera
      {
          get { return uiCamera; }
          set { uiCamera = value; }
      }
  
      public Camera GetUICameraForControl( GameObject go ) {
          int layer = 1 << go.layer;
          int cameraCount = allCameras.Count;
          for (int i = 0; i < cameraCount; ++i) {
              tk2dUICamera camera = allCameras[i];
              if ((camera.FilteredMask & layer) != 0) {
                  return camera.HostCamera;
              }
          }
          Debug.LogError("Unable to find UI camera for " + go.name);
          return null;
      }
  
      static List<tk2dUICamera> allCameras = new List<tk2dUICamera>();
      List<tk2dUICamera> sortedCameras = new List<tk2dUICamera>();
  
      public static void RegisterCamera(tk2dUICamera cam) {
          allCameras.Add(cam);
      }
  
      public static void UnregisterCamera(tk2dUICamera cam) {
          allCameras.Remove(cam);
      }
  
      /// <summary>
      /// Which layer the raycast will be done using. Will ignore others
      /// </summary>
      public LayerMask raycastLayerMask = -1;
  
      private bool inputEnabled = true;
  
      /// <summary>
      /// Used to disable user input
      /// </summary>
      public bool InputEnabled
      {
          get { return inputEnabled; }
          set 
          {
              if (inputEnabled && !value) //disable current presses/hovers
              {
                  SortCameras();
  
                  inputEnabled = value;
                  if (useMultiTouch)
                  {
                      CheckMultiTouchInputs();
                  }
                  else
                  {
                      CheckInputs();
                  }
              }
              else
              {
                  inputEnabled = value;
              }
  
          }
      }
  
      /// <summary>
      /// If hover events are to be tracked. If you don't not need hover events disable for increased performance (less raycasting).
      /// Only works when using mouse and multi-touch is disabled (tk2dUIManager.useMultiTouch)
      /// </summary>
      public bool areHoverEventsTracked = true; //if disable no hover events will be tracked (less overhead), only effects if using mouse
  
      private tk2dUIItem pressedUIItem = null;
  
      /// <summary>
      /// Most recent pressedUIItem (if one is pressed). If multi-touch gets the most recent
      /// </summary>
      public tk2dUIItem PressedUIItem
      {
          get 
          {
              //if multi-touch return the last item (most recent touch)
              if (useMultiTouch)
              {
                  if (pressedUIItems.Length > 0)
                  {
                      return pressedUIItems[pressedUIItems.Length-1];
                  }
                  else
                  {
                      return null;
                  }
              }
              else
              {
                  return pressedUIItem;
              }
          }
      }
  
      /// <summary>
      /// All currently pressed down UIItems if using multi-touch
      /// </summary>
      public tk2dUIItem[] PressedUIItems
      {
          get { return pressedUIItems; }
      }
  
      private tk2dUIItem overUIItem = null; //current hover over uiItem
      private tk2dUITouch firstPressedUIItemTouch; //used to determine deltas
  
      private bool checkForHovers = true; //if internal we are checking for hovers
  
      /// <summary>
      /// Use multi-touch. Only enable if you need multi-touch support. When multi-touch is enabled, hover events are disabled.
      /// Multi-touch disable will provide better performance and accuracy.
      /// </summary>
      [SerializeField]
      private bool useMultiTouch = false;
  
      public bool UseMultiTouch
      {
          get { return useMultiTouch; }
          set 
          {
              if (useMultiTouch != value && inputEnabled) //changed
              {
                  InputEnabled = false; //clears existing
                  useMultiTouch = value;
                  InputEnabled = true;
              }
              else
              {
                  useMultiTouch = value;
              }
          }
      }
  
      //multi-touch specifics
      private const int MAX_MULTI_TOUCH_COUNT = 5; //number of touches at the same time
      private tk2dUITouch[] allTouches = new tk2dUITouch[MAX_MULTI_TOUCH_COUNT]; //all multi-touches
      private List<tk2dUIItem> prevPressedUIItemList = new List<tk2dUIItem>(); //previous pressed and still pressed UIItems
      private tk2dUIItem[] pressedUIItems = new tk2dUIItem[MAX_MULTI_TOUCH_COUNT]; //pressed UIItem on the this frame
  
      private int touchCounter = 0; //touchs counter
      private Vector2 mouseDownFirstPos = Vector2.zero; //used to determine mouse position deltas while in multi-touch
      //end multi-touch specifics
  
      private const string MOUSE_WHEEL_AXES_NAME = "Mouse ScrollWheel"; //Input name of mouse scroll wheel
  
      //for update loop
      private tk2dUITouch primaryTouch = new tk2dUITouch(); //main touch (generally began, or actively press)
      private tk2dUITouch secondaryTouch = new tk2dUITouch(); //secondary touches (generally other fingers)
      private tk2dUITouch resultTouch = new tk2dUITouch(); //which touch is selected between primary and secondary
      private tk2dUIItem hitUIItem; //current uiItem hit
      private RaycastHit hit;
      private Ray ray;
  
      //for update loop (multi-touch)
      private tk2dUITouch currTouch;
      private tk2dUIItem currPressedItem;
      private tk2dUIItem prevPressedItem;
  
      /// <summary>
      /// Only any touch began or mouse click
      /// </summary>
      public event System.Action OnAnyPress;
      /// <summary>
      /// Fired at the end of every update
      /// </summary>
      public event System.Action OnInputUpdate;
  
      /// <summary>
      /// On mouse scroll wheel change (return direction of mouse scroll wheel change)
      /// </summary>
      public event System.Action<float> OnScrollWheelChange;
  
      void SortCameras() {
          sortedCameras.Clear();
  
          int cameraCount = allCameras.Count;
          for (int i = 0; i < cameraCount; ++i) {
              tk2dUICamera camera = allCameras[i];
              if (camera != null) {
                  sortedCameras.Add( camera );
              }
          }
  
          // Largest depth gets priority
          sortedCameras.Sort( (a, b) => b.camera.depth.CompareTo( a.camera.depth ) );
      }
  
      void Awake()
      {
          if (instance == null)
          {
              instance = this;
  
              if (instance.transform.childCount != 0) {
                  Debug.LogError("You should not attach anything to the tk2dUIManager object. " +
                      "The tk2dUIManager will not get destroyed between scene switches and any children will persist as well.");
              }
  
              if (Application.isPlaying) {
                  DontDestroyOnLoad( gameObject );
              }
          }
          else
          {
              //can only be one tk2dUIManager at one-time, if another one is found Destroy it
              if (instance != this)
              {
                  Debug.Log("Discarding unnecessary tk2dUIManager instance.");
  
                  if (uiCamera != null) {
                      HookUpLegacyCamera(uiCamera);
                      uiCamera = null;
                  }
  
                  Destroy(this);
  
                  return;
              }
          } 
  
          tk2dUITime.Init();
          Setup();
      }
  
      void HookUpLegacyCamera(Camera cam) {
          if (cam.GetComponent<tk2dUICamera>() == null) {
              // Handle registration properly
              tk2dUICamera newUICam = cam.gameObject.AddComponent<tk2dUICamera>();
              newUICam.AssignRaycastLayerMask( raycastLayerMask );
          }
      }
  
      void Start() {
          if (uiCamera != null) {
              Debug.Log("It is no longer necessary to hook up a camera to the tk2dUIManager. You can simply attach a tk2dUICamera script to the cameras that interact with UI.");
              HookUpLegacyCamera(uiCamera);
              uiCamera = null;
          }
  
          if (allCameras.Count == 0) {
              Debug.LogError("Unable to find any tk2dUICameras, and no cameras are connected to the tk2dUIManager. You will not be able to interact with the UI.");
          }
      }
  
      private void Setup()
      {
          if (!areHoverEventsTracked)
          {
              checkForHovers = false;
          }
      }
  
      void Update()
      {
          tk2dUITime.Update();
  
          if (inputEnabled)
          {
              SortCameras();
  
              if (useMultiTouch)
              {
                  CheckMultiTouchInputs();
              }
              else
              {
                  CheckInputs();
              }
  
              if (OnInputUpdate != null) { OnInputUpdate(); }
  
              if (OnScrollWheelChange != null)
              {
                  float mouseWheel = Input.GetAxis(MOUSE_WHEEL_AXES_NAME);
                  if (mouseWheel != 0)
                  {
                      OnScrollWheelChange(mouseWheel);
                  }
              }
          }
      }
  
      //checks for inputs (non-multi-touch)
      private void CheckInputs()
      {
          bool isPrimaryTouchFound = false;
          bool isSecondaryTouchFound = false;
          bool isAnyPressBeganRecorded = false;
          primaryTouch = new tk2dUITouch();
          secondaryTouch = new tk2dUITouch();
          resultTouch = new tk2dUITouch();
          hitUIItem = null;
  
          if (inputEnabled)
          {
              if (Input.touchCount > 0)
              {
                  foreach (Touch touch in Input.touches)
                  {
                      if (touch.phase == TouchPhase.Began)
                      {
                          primaryTouch = new tk2dUITouch(touch);
                          isPrimaryTouchFound = true;
                          isAnyPressBeganRecorded = true;
                      }
                      else if (pressedUIItem != null && touch.fingerId == firstPressedUIItemTouch.fingerId)
                      {
                          secondaryTouch = new tk2dUITouch(touch);
                          isSecondaryTouchFound = true;
                      }
                  }
  
                  checkForHovers = false;
  
              }
              else
              {
                  if (Input.GetMouseButtonDown(0))
                  {
                      primaryTouch = new tk2dUITouch(TouchPhase.Began, tk2dUITouch.MOUSE_POINTER_FINGER_ID, Input.mousePosition, Vector2.zero, 0);
                      isPrimaryTouchFound = true;
                      isAnyPressBeganRecorded = true;
                  }
                  else if (Input.GetMouseButton(0) || Input.GetMouseButtonUp(0))
                  {
                      Vector2 deltaPosition = Vector2.zero;
                      TouchPhase mousePhase = TouchPhase.Moved;
                      if (pressedUIItem != null)
                      {
                          deltaPosition = firstPressedUIItemTouch.position - new Vector2(Input.mousePosition.x, Input.mousePosition.y);
                      }
  
                      if (Input.GetMouseButtonUp(0))
                      {
                          mousePhase = TouchPhase.Ended;
                      }
                      else if (deltaPosition == Vector2.zero)
                      {
                          mousePhase = TouchPhase.Stationary;
                      }
                      secondaryTouch = new tk2dUITouch(mousePhase, tk2dUITouch.MOUSE_POINTER_FINGER_ID, Input.mousePosition, deltaPosition, tk2dUITime.deltaTime);
                      isSecondaryTouchFound = true;
                  }
              }
          }
  
          if (isPrimaryTouchFound)
          {
              resultTouch = primaryTouch;
          }
          else if (isSecondaryTouchFound)
          {
              resultTouch = secondaryTouch;
          }
  
          if (isPrimaryTouchFound || isSecondaryTouchFound) //focus touch found
          {
              hitUIItem = RaycastForUIItem(resultTouch.position);
  
              if (resultTouch.phase == TouchPhase.Began)
              {
                  if (pressedUIItem != null)
                  {
                      pressedUIItem.CurrentOverUIItem(hitUIItem);
                      if (pressedUIItem != hitUIItem)
                      {
                          pressedUIItem.Release();
                          pressedUIItem = null;
                      }
                      else
                      {
                          firstPressedUIItemTouch = resultTouch; //just incase touch changed
                      }
                  }
  
                  if (hitUIItem != null)
                  {
                      hitUIItem.Press(resultTouch);
                  }
                  pressedUIItem = hitUIItem;
                  firstPressedUIItemTouch = resultTouch;
              }
              else if (resultTouch.phase == TouchPhase.Ended)
              {
                  if (pressedUIItem != null)
                  {
                      pressedUIItem.CurrentOverUIItem(hitUIItem);
                      pressedUIItem.UpdateTouch(resultTouch);
                      pressedUIItem.Release();
                      pressedUIItem = null;
                  }
              }
              else
              {
                  if (pressedUIItem != null)
                  {
                      pressedUIItem.CurrentOverUIItem(hitUIItem);
                      pressedUIItem.UpdateTouch(resultTouch);
                  }
              }
          }
          else //no touches found
          {
              if (pressedUIItem != null)
              {
                  pressedUIItem.CurrentOverUIItem(null);
                  pressedUIItem.Release();
                  pressedUIItem = null;
              }
          }
  
          //only if hover events are enabled and only if no touch events have ever been recorded
          if (checkForHovers)
          {
              if (inputEnabled) //if input enabled and mouse button is not currently down
              {
                  if (!isPrimaryTouchFound && !isSecondaryTouchFound && hitUIItem == null && !Input.GetMouseButton(0)) //if raycast for a button has not yet been done
                  {
                      hitUIItem = RaycastForUIItem( Input.mousePosition );
                  }
                  else if (Input.GetMouseButton(0)) //if mouse button is down clear it
                  {
                      hitUIItem = null;
                  }
              }
  
              if (hitUIItem != null)
              {
                  if (hitUIItem.isHoverEnabled)
                  {
                      bool wasPrevOverFound = hitUIItem.HoverOver(overUIItem);
  
                      if (!wasPrevOverFound && overUIItem != null)
                      {
                          overUIItem.HoverOut(hitUIItem);
                      }
                      overUIItem = hitUIItem;
                  }
                  else
                  {
                      if (overUIItem != null)
                      {
                          overUIItem.HoverOut(null);
                      }
                  }
              }
              else
              {
                  if (overUIItem != null)
                  {
                      overUIItem.HoverOut(null);
                  }
              }
          }
  
          if (isAnyPressBeganRecorded)
          {
              if (OnAnyPress != null) { OnAnyPress(); }
          }
      }
  
      //checks for inputs (multi-touch)
      private void CheckMultiTouchInputs()
      {
          bool isAnyPressBeganRecorded = false;
          int prevFingerID = -1;
          bool wasPrevTouchFound = false;
          bool isNewlyPressed = false;
  
          touchCounter = 0;
          if (inputEnabled)
          {
              if (Input.touchCount > 0)
              {
                  foreach (Touch touch in Input.touches)
                  {
  
gt:                     if (touchCounter < MAX_MULTI_TOUCH_COUNT)