/**
     * <summary>
     * @implementation ISelectable
     *
     * On selected
     * </summary>
     *
     * <returns>
     * void
     * </returns>
     */
    public virtual void OnSelected()
    {
        // Stop selection through the UI.
        if (ComponentPanelsUI.OnUI)
        {
            return;
        }

        // Unselect units
        if (UnitsManagerBehaviour.SelectedUnits != null)
        {
            UnitsManagerBehaviour.UnselectGameObjects();
        }

        // Unselect resource
        if (ResourcesManagerBehaviour.SelectedResource != null)
        {
            ResourcesManagerBehaviour.UnselectResource();
        }

        this.UI.SetActive(true);

        GameObject health = this.UI.transform.Find("Health").gameObject;

        health.SetActive(true);

        // Show building action component
        this.buildingActionComponent.SetActive(true);
    }
    private void Awake()
    {
        UnitsManagerBehaviour.SelectedUnits = new List <GameObject>();
        UnitsManagerBehaviour.Visibles      = new List <GameObject>();

        UnitsManagerBehaviour.Self = this;
    }
    /**
     * <summary>
     * Select a given resource
     * </summary>
     *
     * <param name="resource">resource</param>
     *
     * <returns>
     * void
     * </returns>
     */
    public static void SelectResource(GameObject resource)
    {
        ResourcesManagerBehaviour.UnselectResource();

        // Unselect units
        if (UnitsManagerBehaviour.SelectedUnits != null)
        {
            UnitsManagerBehaviour.UnselectGameObjects();
        }

        ResourceBehaviour resourceBehaviour = resource.GetComponent <ResourceBehaviour>();

        //resourceBehaviour.SetSelect(true);

        // Show info component panel
        Self.infoComponentPanel.SetActive(true);

        Self.infoComponentPanel
        .transform
        .Find("Single")
        .gameObject
        .SetActive(true);

        ResourceUI resourceUI = resourceBehaviour.GetUI()
                                .GetComponent <ResourceUI>();

        InfoComponentPanelUI infoComponentPanelUI = Self.infoComponentPanel.GetComponent <InfoComponentPanelUI>();

        infoComponentPanelUI.SetName(resource.name);
        infoComponentPanelUI.SetHealth(resourceBehaviour.GetHealth(), resourceBehaviour.GetMaxHealth());
        infoComponentPanelUI.SetIcon(resourceUI.GetIcon());

        // Set selected resource
        ResourcesManagerBehaviour.SelectedResource = resource;
    }
    /**
     * <summary>
     * Select a unit
     * </summary>
     *
     * <returns>
     * void
     * </returns>
     */
    public static void Select(GameObject unit)
    {
        if (unit.GetComponent <UnitBehaviour>() == null)
        {
            throw new Exception(unit.name + " " + "is not a unit type");
        }

        // Clear previous units
        UnitsManagerBehaviour.UnselectGameObjects();

        // Unselect resource
        if (ResourcesManagerBehaviour.SelectedResource != null)
        {
            ResourcesManagerBehaviour.UnselectResource();
        }

        UnitBehaviour unitBehaviour = unit.GetComponent <UnitBehaviour>();

        if (unitBehaviour is ISelectableUnit)
        {
            UnitsManagerBehaviour.SelectedUnits.Add(unit);
            unitBehaviour.SetSelect(true);

            Self.infoComponentPanel.SetActive(true);

            // Show info component panel
            Self.infoComponentPanel
            .transform
            .Find("Single")
            .gameObject
            .SetActive(true);

            // Hide multiple
            Self.infoComponentPanel
            .transform
            .Find("Multiple")
            .gameObject
            .SetActive(false);

            UnitUI unitUI = unitBehaviour.GetUI().GetComponent <UnitUI>();

            InfoComponentPanelUI infoComponentPanelUI = Self.infoComponentPanel.GetComponent <InfoComponentPanelUI>();
            infoComponentPanelUI.SetName(unit.name);
            infoComponentPanelUI.SetHealth(unitBehaviour.GetHealth(), unitBehaviour.GetMaxHealth());
            infoComponentPanelUI.SetIcon(unitUI.GetIcon());

            // Show action component panel
            Self.actionComponentPanel.SetActive(true);
        }
    }
    /**
     * <summary>
     * Move the selected to the given position
     * </summary>
     *
     * <param name="target"/>
     *
     * <returns>
     * void
     * </returns>
     */
    public static void MoveTo(Vector3 target)
    {
        // One unit movement
        if (UnitsManagerBehaviour.SelectedUnits.Count == 1)
        {
            UnitBehaviour unitBehaviour = UnitsManagerBehaviour.SelectedUnits[0].GetComponent <UnitBehaviour>();
            unitBehaviour.MoveTo(target);

            switch (unitBehaviour)
            {
            case BuilderBehaviour builder:
                if (builder.IsConstructing() || builder.IsGatheringResource())
                {
                    builder.Cancel();
                }
                break;
            }
            return;
        }

        List <Vector3> positions = UnitsManagerBehaviour.CalculatePositions(target);

        UnitsManagerBehaviour.SelectedUnits = UnitsManagerBehaviour.SelectedUnits
                                              .OrderBy(gameObject => gameObject.transform.position.x)
                                              .ThenByDescending(gameObject => gameObject.transform.position.z)
                                              .ToList();

        // Move the units to the positions
        for (int i = 0; i < UnitsManagerBehaviour.SelectedUnits.Count; i++)
        {
            Vector3    position     = positions[i];
            GameObject selectedUnit = UnitsManagerBehaviour.SelectedUnits[i];

            UnitBehaviour unitBehaviour = selectedUnit.GetComponent <UnitBehaviour>();
            unitBehaviour.MoveTo(position);

            switch (unitBehaviour)
            {
            case BuilderBehaviour builder:
                if (builder.IsConstructing() || builder.IsGatheringResource())
                {
                    builder.Cancel();
                }
                break;
            }
        }
    }
    /**
     * <summary>
     * Select a given building
     * </summary>
     *
     * <param name="building"></param>
     *
     * <returns>
     * void
     * </returns>
     */
    public static void SelectBuilding(GameObject building)
    {
        // Unselect previous building
        BuildingsManagerBehaviour.UnselectBuilding();

        // Unselect units
        if (UnitsManagerBehaviour.SelectedUnits != null)
        {
            UnitsManagerBehaviour.UnselectGameObjects();
        }

        // Unselect previous resource
        if (ResourcesManagerBehaviour.SelectedResource != null)
        {
            ResourcesManagerBehaviour.UnselectResource();
        }

        BuildingBehaviour buildingBehaviour = building.GetComponent <BuildingBehaviour>();

        buildingBehaviour.SetSelect(true);

        // Show info component panel
        Self.infoComponentPanel.SetActive(true);

        Self.infoComponentPanel
        .transform
        .Find("Single")
        .gameObject
        .SetActive(true);

        BuildingUI buildingUI = buildingBehaviour.GetUI()
                                .GetComponent <BuildingUI>();

        InfoComponentPanelUI infoComponentPanelUI = Self.infoComponentPanel.GetComponent <InfoComponentPanelUI>();

        infoComponentPanelUI.SetName(building.name);
        infoComponentPanelUI.SetHealth(buildingBehaviour.GetHealth(), buildingBehaviour.GetMaxHealth());
        infoComponentPanelUI.SetIcon(buildingUI.GetIcon());

        // Show action component panel
        Self.actionComponentPanel.SetActive(true);

        // Set selected building
        BuildingsManagerBehaviour.SelectedBuilding = building;
    }
    /**
     * <summary>
     * On right mouse clicked
     * </summary>
     *
     * <returns>
     * void
     * </returns>
     */
    private void OnRightMouseClicked()
    {
        // TODO: In the future, we may need to refactor for maintainability purposes.
        // Check if the units are selected.
        if (UnitsManagerBehaviour.SelectedUnits.Count > 0)
        {
            Ray        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;


            if (Physics.Raycast(ray, out hit) && hit.collider != null)
            {
                if (hit.collider.tag == Tags.Ground)
                {
                    UnitsManagerBehaviour.MoveTo(hit.point);
                }
            }
        }
    }
    /**
     * <summary>
     * On mouse over
     * </summary>
     *
     * <returns>
     * void
     * </returns>
     */
    private void OnMouseOver()
    {
        if (MouseBehaviour.Mode == MouseMode.Build)
        {
            return;
        }

        // Prevent selectino through the bottom UI
        if (ComponentPanelsUI.OnUI)
        {
            return;
        }

        MouseBehaviour.Mode = MouseMode.OverUnit;

        // Left clicked
        if (Input.GetMouseButtonDown(0))
        {
            UnitsManagerBehaviour.Select(this.gameObject);
        }
    }
    /**
     * <summary>
     * On left mouse up
     * </summary>
     *
     * <returns>
     * void
     * </returns>
     */
    private void OnLeftMouseUp()
    {
        // Stop scrolling, if the mouse is over UI or in build mode.
        if (!this.drag)
        {
            return;
        }

        // Is pointer up on the component panels UI, stop the selection
        if (ComponentPanelsUI.OnUI)
        {
            this.drag = false;
            this.HideSelectionArea();

            return;
        }

        RectTransform areaRect = this.area.GetComponent <RectTransform>();

        // Set final mouse position
        this.mousePosition.SetFinalPosition(this.transform.position);

        Bounds cameraViewportBounds = MouseScreenBehaviour.GetViewportBounds(
            this.mousePosition.GetInitialPosition(),
            this.mousePosition.GetFinalPosition()
            );

        ResourcesManagerBehaviour.UnselectResource();
        BuildingsManagerBehaviour.UnselectBuilding();

        UnitsManagerBehaviour.UnselectGameObjects();

        this.mouseSelectionBehaviour.SelectGameObjects(UnitsManagerBehaviour.Visibles, cameraViewportBounds);

        this.drag = false;
        this.HideSelectionArea();
    }
    /**
     * <summary>
     * Get selectable game objects based on screen to viewport
     * from a list
     *
     * @param List<GameObject> selectableGameObjects
     * @param Bounds cameraViewportBounds
     *
     * @return void
     * </summary>
     */
    public void SelectGameObjects(List <GameObject> selectableGameObjects, Bounds cameraViewportBounds)
    {
        List <GameObject> unitsWithinArea = new List <GameObject>();

        // Add selectable units to the list
        foreach (GameObject selectableGameObject in selectableGameObjects)
        {
            Vector3 gameObjectViewport = Camera.main.WorldToViewportPoint(selectableGameObject.transform.position);

            if (cameraViewportBounds.Contains(gameObjectViewport))
            {
                UnitBehaviour unitBehaviour = selectableGameObject.GetComponent <UnitBehaviour>();

                // If unit is selectable
                if (unitBehaviour is ISelectableUnit)
                {
                    unitsWithinArea.Add(selectableGameObject);
                }
            }
        }

        // Select units
        UnitsManagerBehaviour.Select(unitsWithinArea);
    }
 /**
  * <summary>
  * Unselect all selected game objects
  * </summary>
  *
  * <returns>
  * void
  * </returns>
  */
 public void UnselectGameObjects()
 {
     UnitsManagerBehaviour.UnselectGameObjects();
 }
    /**
     * <summary>
     * @overload
     *
     * Select a group of units
     * </summary>
     *
     * <returns>
     * void
     * </returns>
     */
    public static void Select(List <GameObject> units)
    {
        // Select a single unit, if the list has only one unit
        if (units.Count == 1)
        {
            UnitsManagerBehaviour.Select(units[0]);
            return;
        }

        // Validate unit game objects
        foreach (GameObject unit in units)
        {
            if (unit.GetComponent <UnitBehaviour>() == null)
            {
                throw new Exception(unit.name + " " + "is not a unit type");
            }
        }

        // Clear previous units
        UnitsManagerBehaviour.UnselectGameObjects();

        // Unselect resource
        if (ResourcesManagerBehaviour.SelectedResource != null)
        {
            ResourcesManagerBehaviour.UnselectResource();
        }

        // Add unit to the global list
        foreach (GameObject unit in units)
        {
            UnitBehaviour unitBehaviour = unit.GetComponent <UnitBehaviour>();

            if (unitBehaviour is ISelectableUnit)
            {
                UnitsManagerBehaviour.SelectedUnits.Add(unit);
            }
        }

        // Set selection
        foreach (GameObject unit in UnitsManagerBehaviour.SelectedUnits)
        {
            UnitBehaviour unitBehaviour = unit.GetComponent <UnitBehaviour>();
            unitBehaviour.SetSelect(true);
        }

        // Show multiple unit icons
        if (UnitsManagerBehaviour.SelectedUnits.Count > 1)
        {
            Self.infoComponentPanel.SetActive(true);

            // Show info component panel
            Self.infoComponentPanel
            .transform
            .Find("Single")
            .gameObject
            .SetActive(false);

            // Hide multiple
            Self.infoComponentPanel
            .transform
            .Find("Multiple")
            .gameObject
            .SetActive(true);

            // Remove previous multiple selection slots
            Self.RemoveMultipleSelectionSlots();

            // Generate new multiple selection slots
            Self.GenerateMultipleSelectionUnitSlots();

            // Hide action component panel
            Self.actionComponentPanel.SetActive(false);
        }
    }