/// <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 DragElement
        /// </summary>
        /// <param name="dragElement"></param>
        public void Destroy(DragElement dragElement)
        {
            if (!this.DragElementsCache.ContainsKey(dragElement.Id))
            {
                Object.Destroy(dragElement.gameObject);
                return;
            }

            Object.Destroy(this.DragElementsCache[dragElement.Id].gameObject);
            this.DragElementsCache.Remove(dragElement.Id);
        }
        /// <summary>
        /// Attach events and Cache specific DragElement in <see cref="DragElementsCache"/>
        /// </summary>
        /// <param name="dragElement"></param>
        public void CacheDragElement(DragElement dragElement)
        {
            PrepareDragEvents(dragElement);

            if (!this.DragElementsCache.ContainsKey(dragElement.Id))
            {
                this.DragElementsCache.Add(dragElement.Id, null);
            }

            this.DragElementsCache[dragElement.Id]          = dragElement;
            this.DragElementsCache[dragElement.Id].IsCached = true;
        }
        public static void AddDragElementOnSelected()
        {
            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;
            }

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

            Undo.RegisterCreatedObjectUndo(dragElement, "Create DragElement");
        }
        /// <summary>
        /// Add new GameObject with attached DragElement 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 DragElement.</returns>
        public DragElement AddDragElement(Transform parent, DragElement prefab = null, string name = "NewDragElement")
        {
            DragElement dragElement;

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

            dragElement.transform.SetParent(parent);
            CacheDragElement(dragElement);

            return(dragElement);
        }
        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");
        }