/// <summary>
        /// Attach on specific DragElement all officially drag events.
        /// </summary>
        /// <param name="dragElement">Specific DragElement for preparing.</param>
        private void PrepareDragEvents(DragElement dragElement)
        {
            dragElement.OnBeginDragCallback = () => { LoadBeginDragEvents(dragElement); };

            dragElement.OnDragCallback = () => LoadDragEvents();

            dragElement.OnEndDragCallback = () => LoadEndDragEvents();

            void LoadBeginDragEvents(DragElement element)
            {
                this.SelectedDragElement = element;
                this.SelectedDragElement.TransformCache.SetParent(this.transform);

                if (this.HoveredDropObject != null)
                {
                    this.LastDropObject = this.HoveredDropObject;
                }

                this.onBeginDrag.Invoke();
            }

            void LoadDragEvents()
            {
                this.onDrag.Invoke();
            }

            void LoadEndDragEvents()
            {
                this.onEndDrag.Invoke();

                if (this.HoveredDropObject != null)
                {
                    if (this.HoveredDropObject.isEmpty)
                    {
                        this.SelectedDragElement.TransformCache.SetParent(this.HoveredDropObject.TransformCache);
                        this.SelectedDragElement.TransformCache.localPosition = Vector2.zero;
                        this.SelectedDragElement.SetLastParent();
                        this.HoveredDropObject.isEmpty = false;
                        LastDropObjectSetEmpty(true);
                    }
                    else
                    {
                        this.SelectedDragElement.ReturnToLastParent();
                        LastDropObjectSetEmpty(false);
                    }
                }
                else if (this.HoveredDropObject == null)
                {
                    this.onDropElementOutside.Invoke();
                    LastDropObjectSetEmpty(true);
                }


                this.LastSelectedDragElement = this.SelectedDragElement;
                this.SelectedDragElement     = null;
            }
        }
        /// <summary>
        /// Destroy GameObject and remove from cache specific instance of DropObject
        /// </summary>
        /// <param name="dropObject"></param>
        public void Destroy(DropObject dropObject)
        {
            if (!this.DropObjectsCache.ContainsKey(dropObject.Id))
            {
                Object.Destroy(dropObject.gameObject);
                return;
            }

            Object.Destroy(this.DropObjectsCache[dropObject.Id].gameObject);
            this.DropObjectsCache.Remove(dropObject.Id);
        }
        public static void AddDropObjectOnSelected()
        {
            DragAndDrop dragAndDrop = Selection.activeGameObject.transform.root.GetComponent <DragAndDrop>();

            if (dragAndDrop == null)
            {
                DragAndDrop.Log("Don't have [DragAndDrop] Component in scene. Please assign and try again", DragAndDrop.LogType.Error);
                return;
            }

            DropObject dropObject = dragAndDrop.AddDropObject(Selection.activeGameObject.transform);

            Undo.RegisterCreatedObjectUndo(dropObject, "Create DropObject");
        }
        /// <summary>
        /// Attach events and Cache specific DragElement in <see cref="DropObjectsCache"/>
        /// </summary>
        /// <param name="dropObject"></param>
        public void CacheDropObject(DropObject dropObject)
        {
            PrepareDropEvents(dropObject);

            Guid id = dropObject.Id;

            if (!this.DropObjectsCache.ContainsKey(id))
            {
                this.DropObjectsCache.Add(id, null);
            }

            this.DropObjectsCache[id]          = dropObject;
            this.DropObjectsCache[id].IsCached = true;
        }
        /// <summary>
        /// Attach on specific DropObject all officially drag events.
        /// </summary>
        /// <param name="dropObject">Specific DropObject for preparing.</param>
        private void PrepareDropEvents(DropObject dropObject)
        {
            dropObject.OnPointerEnterCallback = () => { LoadPointerEnterEvents(dropObject); };

            dropObject.OnPointerExitCallback = () => { LoadPointerExitEvents(); };

            void LoadPointerEnterEvents(DropObject оbject)
            {
                this.HoveredDropObject = оbject;
            }

            void LoadPointerExitEvents()
            {
                this.HoveredDropObject = null;
            }
        }
        /// <summary>
        /// Add new GameObject with attached DropObject on specific place in hierarchy.
        /// </summary>
        /// <param name="parent">Parent for new GameObject.</param>
        /// <param name="prefab">Original object to copy.</param>
        /// <param name="name">Specific name for new Game object.</param>
        /// <returns>Created instance of DropObject.</returns>
        public DropObject AddDropObject(Transform parent, DropObject prefab = null, string name = "NewDropObject")
        {
            DropObject dropObject;

            if (prefab == null)
            {
                dropObject = new GameObject(name, typeof(RectTransform), typeof(DropObject), typeof(Image)).GetComponent <DropObject>();
            }
            else
            {
                dropObject      = Instantiate(prefab, parent);
                dropObject.name = name;
            }

            dropObject.transform.SetParent(parent);
            CacheDropObject(dropObject);

            return(dropObject);
        }
        public static void CreateDragAndDropOnSelected()
        {
            GameObject go = new GameObject("DragAndDropPanel", typeof(RectTransform), typeof(DragAndDrop));

            go.transform.SetParent(Selection.activeGameObject.transform);

            RectTransform rectTransform = go.GetComponent <RectTransform>();

            rectTransform.localScale = Vector2.one;
            rectTransform.sizeDelta  = new Vector2(400, 200);

            DragAndDrop dragAndDrop = go.GetComponent <DragAndDrop>();

            GameObject dropObjectContainer = new GameObject("DropObjectContainer", typeof(GridLayoutGroup));

            dropObjectContainer.transform.SetParent(dragAndDrop.transform);
            rectTransform            = dropObjectContainer.GetComponent <RectTransform>();
            rectTransform.localScale = Vector2.one;
            rectTransform.sizeDelta  = new Vector2(400, 200);


            DragElement dragElement = dragAndDrop.AddDragElement(dragAndDrop.transform);

            dragElement.transform.localScale     = Vector2.one;
            dragElement.transform.localPosition += new Vector3(0, -200, 0);
            Image image = DragAndDrop.AddMissingComponent <Image>(dragElement.gameObject);

            image.color = Color.cyan;


            DropObject dropObject = dragAndDrop.AddDropObject(dropObjectContainer.transform);

            dropObject.transform.localScale = Vector2.one;
            image       = DragAndDrop.AddMissingComponent <Image>(dropObject.gameObject);
            image.color = Color.magenta;

            Undo.RegisterCreatedObjectUndo(go, "Create DragAndDrop");
        }