"""
Gorilla Troops Optimizer (GTO)
===============================

Gorilla Troops Optimizer inspired by the social intelligence and
life strategy of gorilla troops.

Reference:
Abdollahzadeh, B., Gharehchopogh, F. S., & Mirjalili, S. (2021).
African vultures optimization algorithm: A new nature-inspired metaheuristic algorithm for global optimization problems.
Computers & Industrial Engineering, 158, 107408.

Author: MHA Flow Development Team
License: MIT
"""

import math
import numpy as np
from ..base import BaseOptimizer
from .levy_flight_universal import add_levy_flight_to_position


class GorillaTroopsOptimizer(BaseOptimizer):
    """
    Gorilla Troops Optimizer (GTO)
    
    Inspired by gorilla social behavior including:
    - Silverback (leader) following
    - Migration to unknown locations
    - Movement toward other gorillas
    - Adult male competition
    
    Parameters
    ----------
    population_size : int, default=30
        Number of gorillas in the troop
    max_iterations : int, default=100
        Maximum number of iterations
    beta : float, default=3
        Gorilla energy parameter
    p : float, default=0.03
        Probability parameter
    """
    
    def __init__(self, population_size=30, max_iterations=100, beta=3, p=0.03, **kwargs):
        super().__init__(population_size, max_iterations, **kwargs)
        self.algorithm_name = "Gorilla Troops Optimizer"
        self.aliases = ["gto", "gorilla", "gorilla_troops"]
        self.beta = beta
        self.p = p
    
    def _optimize(self, objective_function, X=None, y=None, **kwargs):
        # Levy flight available via: add_levy_flight_to_position()
        """
        Execute the Gorilla Troops Optimizer
        
        Args:
            objective_function: Function to optimize
            X: Feature matrix (optional)
            y: Target values (optional)
            **kwargs: Additional parameters
            
        Returns:
            Tuple of (best_solution, best_fitness, global_fitness, local_fitness, local_positions)
        """
        # Get dimensions and bounds
        if X is not None:
            dimension = X.shape[1]
            lb, ub = 0.0, 1.0
        else:
            dimension = kwargs.get('dimensions', getattr(self, 'dimensions_', 10))
            lb = kwargs.get('lower_bound', getattr(self, 'lower_bound_', -100.0))
            ub = kwargs.get('upper_bound', getattr(self, 'upper_bound_', 100.0))
            if hasattr(lb, '__getitem__'):
                lb = lb[0]
            if hasattr(ub, '__getitem__'):
                ub = ub[0]
        
        # Initialize gorilla troop
        gorillas = np.random.uniform(lb, ub, (self.population_size_, dimension))
        fitness = np.array([objective_function(g) for g in gorillas])
        
        best_idx = np.argmin(fitness)
        silverback = gorillas[best_idx].copy()  # Leader (alpha male)
        best_fitness = fitness[best_idx]
        global_fitness = []
        local_fitness = []
        local_positions = []
        
        for iteration in range(self.max_iterations_):
            # Decreasing parameter from 2 to 0
            C = 1 - iteration / self.max_iterations_
            
            for i in range(self.population_size_):
                r = np.random.rand()
                
                # Exploration: Migration to unknown location
                if r < self.p:
                    gorillas[i] = (ub - lb) * np.random.rand(dimension) + lb
                else:
                    # Exploitation phase
                    if r < 0.5:
                        # Movement toward other gorillas
                        j = np.random.randint(0, self.population_size_)
                        H = np.random.rand()  # Gorilla strength
                        
                        # Coefficient A
                        A = (2 * np.random.rand() - 1) * C
                        
                        # Update position
                        gorillas[i] = (np.random.rand() - A) * gorillas[j] + \
                                     H * (gorillas[i] - gorillas[j])
                    else:
                        # Movement toward silverback (leader)
                        H = np.random.rand()
                        
                        # Coefficient A
                        A = (2 * np.random.rand() - 1) * C
                        
                        # Levy flight component
                        L = C * H * np.random.rand(dimension)
                        
                        # Update toward silverback
                        gorillas[i] = (np.random.rand() - A) * silverback + \
                                     L * (gorillas[i] - silverback)
                
                # Apply Levy flight periodically for diversity
                if iteration % 7 == 0:
                    levy_step = self._levy_flight(dimension)
                    gorillas[i] = gorillas[i] + levy_step * (silverback - gorillas[i])
                
                # Boundary control
                gorillas[i] = np.clip(gorillas[i], lb, ub)
                
                # Evaluate
                new_fitness = objective_function(gorillas[i])
                
                if new_fitness < fitness[i]:
                    fitness[i] = new_fitness
                    if new_fitness < best_fitness:
                        silverback = gorillas[i].copy()
                        best_fitness = new_fitness
            
            global_fitness.append(best_fitness)
            # Note: Add local_fitness and local_positions tracking as needed
            
            if self.verbose_ and iteration % 10 == 0:
                print(f"GTO Iteration {iteration}: Best Fitness = {best_fitness:.6e}")
        
        return silverback, best_fitness, global_fitness, local_fitness, local_positions
    
    def _levy_flight(self, dimension):
        """Generate Levy flight step"""
        beta = 1.5
        sigma = (math.gamma(1 + beta) * np.sin(np.pi * beta / 2) / 
                (math.gamma((1 + beta) / 2) * beta * 2 ** ((beta - 1) / 2))) ** (1 / beta)
        u = np.random.randn(dimension) * sigma
        v = np.random.randn(dimension)
        step = u / (np.abs(v) ** (1 / beta))
        return 0.01 * step
