<?php

/**
 * JCOGS Image Pro - Reflection Parameter Package
 * ==============================================
 * Sophisticated form interface for reflection parameter
 * 
 * @category   ExpressionEngine Add-on
 * @package    JCOGS Image Pro
 * @author     JCOGS Design <contact@jcogs.net>
 * @copyright  Copyright (c) 2021 - 2025 JCOGS Design
 * @license    https://jcogs.net/add-ons/license/jcogs_img_pro
 * @version    2.0.6
 * @link       https://JCOGS.net/
 * @since      Parameter Package Implementation
 */

namespace JCOGSDesign\JCOGSImagePro\Service\ParameterPackages;

/**
 * Reflection Parameter Package
 * 
 * Provides sophisticated 4-field interface for reflection parameter instead of requiring
 * users to construct complex comma-separated parameter strings manually.
 * 
 * Reflection Parameter Format (from JCOGS documentation):
 * reflection="<gap>[,<start opacity>[,<end opacity>[,<reflection height>]]]"
 * 
 * Components:
 * - gap: Vertical separation between image and reflection (default: 0)
 * - start_opacity: Opacity at top of reflection, closest to image (default: 80, range: 0-100)
 * - end_opacity: Opacity at bottom of reflection, furthest from image (default: 0, range: 0-100) 
 * - reflection_height: Height of reflection as dimension or % (default: 50%)
 * 
 * Examples:
 * - reflection="0" - Default parameters
 * - reflection="10,70,30,40%" - All parameters specified
 * - reflection="20px,70,10,60%" - With gap and custom opacity fade
 * 
 * Documentation: https://jcogs.net/documentation/jcogs_img/jcogs_img-parameters#jcogs-image-parameter-reflection
 */
class ReflectionParameterPackage extends AbstractParameterPackage
{
    /**
     * Package identifier
     * 
     * @return string Package name
     */
    public function getName(): string 
    {
        return 'reflection_parameter_package';
    }

    /**
     * Get the display label for this parameter package
     * 
     * @return string Package label
     */
    public function getLabel(): string
    {
        return 'Reflection Parameters';
    }

    /**
     * Get the description for this parameter package
     * 
     * @return string Package description
     */
    public function getDescription(): string
    {
        return 'Controls for simulated reflection effects below images';
    }

    /**
     * Get the category for this parameter package
     * 
     * @return string Package category
     */
    public function getCategory(): string
    {
        return 'transformational';
    }

    /**
     * Parameters handled by this package
     * 
     * @return array List of parameter names
     */
    public function getParameters(): array 
    {
        return ['reflection'];
    }

    /**
     * Parse composite reflection string into associative array
     * 
     * Supports three formats with automatic detection:
     * 1. Named attributes (preferred): "reflection_gap:10|reflection_start_opacity:70|..."
     * 2. Legacy positional: "10,70,30,40%"
     * 3. Minimal positional: "10"
     * 
     * Note: Reflection uses COMMA separator in positional format (unlike other parameters)
     * but PIPE separator in named attributes format for consistency.
     * 
     * @param string $param_name Parameter name (e.g., 'reflection')
     * @param string $composite_string Raw parameter value to parse
     * @return array Associative array with reflection_gap, reflection_start_opacity, etc. keys
     */
    public function parseCompositeString(string $param_name, string $composite_string): array
    {
        if (empty(trim($composite_string))) {
            return $this->getDefaultValues();
        }

        // Priority 1: Try named attribute format (has colons and pipes)
        if (strpos($composite_string, ':') !== false && strpos($composite_string, '|') !== false) {
            $parsed = $this->parseNamedAttributes($composite_string);
            if (!empty($parsed)) {
                return array_merge($this->getDefaultValues(), $parsed);
            }
        }

        // Priority 2: Try legacy positional format (comma-delimited)
        return $this->parsePositionalFormat($composite_string);
    }

    /**
     * Parse named attribute format
     * Format: "reflection_gap:10|reflection_start_opacity:70|reflection_end_opacity:30|reflection_height:40%"
     * 
     * @param string $composite_string Named attribute string
     * @return array Parsed values or empty array if invalid format
     */
    private function parseNamedAttributes(string $composite_string): array
    {
        $parsed = [];
        $parts = explode('|', trim($composite_string));

        foreach ($parts as $part) {
            $part = trim($part);
            if (empty($part)) {
                continue;
            }

            if (strpos($part, ':') === false) {
                continue;
            }

            list($name, $value) = explode(':', $part, 2);
            $name = trim($name);
            $value = trim($value);

            // Only accept reflection_* attributes
            if (strpos($name, 'reflection_') === 0) {
                $parsed[$name] = $value;
            }
        }

        return $parsed;
    }

    /**
     * Parse legacy positional format (comma-delimited)
     * Format: "10,70,30,40%" or "10" or "10,70"
     * 
     * @param string $composite_string Positional format string
     * @return array Associative array with reflection_gap, reflection_start_opacity, etc. keys
     */
    private function parsePositionalFormat(string $composite_string): array
    {
        $defaults = $this->getDefaultValues();
        $parts = array_map('trim', explode(',', trim($composite_string)));

        // Treat empty strings as null to use defaults (fixes issue #100 with ,,,)
        return [
            'reflection_gap' => (!empty($parts[0]) ? $parts[0] : $defaults['reflection_gap']),
            'reflection_start_opacity' => (!empty($parts[1] ?? '') ? $parts[1] : $defaults['reflection_start_opacity']),
            'reflection_end_opacity' => (!empty($parts[2] ?? '') ? $parts[2] : $defaults['reflection_end_opacity']),
            'reflection_height' => (!empty($parts[3] ?? '') ? $parts[3] : $defaults['reflection_height'])
        ];
    }

    /**
     * Get default values for all reflection sub-parameters
     * 
     * @return array Default values
     */
    private function getDefaultValues(): array
    {
        return [
            'reflection_gap' => '0',
            'reflection_start_opacity' => '80',
            'reflection_end_opacity' => '0',
            'reflection_height' => '50%'
        ];
    }

    /**
     * Normalize reflection parameter to positional format
     * 
     * Accepts both formats and returns consistent positional comma-delimited format:
     * - Input (positional): "10,70,30,40%" → Output: "10,70,30,40%"
     * - Input (named): "reflection_gap:10|reflection_start_opacity:70|..." → Output: "10,70,30,40%"
     * 
     * Note: Reflection uses comma separator, not pipe!
     * 
     * @param string $reflection_parameter Reflection parameter string in either format
     * @return string Normalized positional comma-delimited format
     */
    public function normalizeReflectionParameter(string $reflection_parameter): string
    {
        // Check for truly empty string (not just "0" which is valid)
        if ($reflection_parameter === '' || $reflection_parameter === null) {
            return '';
        }

        // Convert named format to positional if needed
        if ($this->isNamedAttributeFormat($reflection_parameter)) {
            return $this->convertNamedToPositionalFormat($reflection_parameter);
        }

        // Already in positional format
        return $reflection_parameter;
    }

    /**
     * Get validation definitions for reflection sub-parameters
     * 
     * This is the SINGLE SOURCE OF TRUTH for reflection parameter validation rules.
     * All validation (preset forms, CP forms, filter processing) should use these definitions.
     * 
     * Reflection parameter format: "gap,start_opacity,end_opacity,height"
     * Example: "10,70,30,40%"
     * 
     * @return array Map of sub-parameter names to validation configurations
     */
    public function getSubParameterDefinitions(): array
    {
        return [
            // Sub-parameter 1: Gap
            'reflection_gap' => [
                'label' => 'Gap',
                'description' => 'Vertical separation between image and reflection',
                'type' => 'text',
                'default_value' => '0',
                'validator' => 'numeric',
                'validation_config' => [
                    'allow_negative' => false,
                    'allow_percentage' => true,
                    'allow_units' => ['px', '%'],
                    'min_value' => 0,
                    'default_unit' => 'px'
                ],
                'placeholder' => '0, 10px, or 5%'
            ],

            // Sub-parameter 2: Start opacity
            'reflection_start_opacity' => [
                'label' => 'Start Opacity',
                'description' => 'Opacity at top of reflection (closest to image)',
                'type' => 'text',
                'default_value' => '80',
                'validator' => 'percentage',
                'validation_config' => [
                    'min_value' => 0,
                    'max_value' => 100,
                    'format' => '0-100'
                ],
                'placeholder' => '80 (default), 0-100'
            ],

            // Sub-parameter 3: End opacity
            'reflection_end_opacity' => [
                'label' => 'End Opacity',
                'description' => 'Opacity at bottom of reflection (furthest from image)',
                'type' => 'text',
                'default_value' => '0',
                'validator' => 'percentage',
                'validation_config' => [
                    'min_value' => 0,
                    'max_value' => 100,
                    'format' => '0-100'
                ],
                'placeholder' => '0 (default), 0-100'
            ],

            // Sub-parameter 4: Reflection height
            'reflection_height' => [
                'label' => 'Reflection Height',
                'description' => 'Height of reflection as dimension or percentage',
                'type' => 'text',
                'default_value' => '50%',
                'validator' => 'numeric',
                'validation_config' => [
                    'allow_negative' => false,
                    'allow_percentage' => true,
                    'allow_units' => ['px', '%'],
                    'min_value' => 0,
                    'max_value' => 100, // When percentage
                    'default_unit' => '%'
                ],
                'placeholder' => '50% (default), or 100px'
            ]
        ];
    }

    /**
     * Priority for reflection parameter package
     * Higher priority than general transformational package
     * 
     * @return int Priority order (lower numbers = higher priority)
     */
    public function getPriority(): int
    {
        return 21; // Higher than TransformationalParameterPackage (30)
    }

    /**
     * Get form fields for reflection parameter
     * 
     * @param array $current_values Current parameter values  
     * @return array Flat associative array of form fields
     */
    protected function getPackageFormFields(array $current_values = []): array
    {
        // Get current reflection value
        $current_value = $current_values['reflection'] ?? '';
        
        // Parse existing reflection value using new composite parser (supports named and positional formats)
        $parsed = $this->parseCompositeString('reflection', $current_value);
        
        return [
            'reflection_gap' => [
                'type' => 'text',
                'label' => 'Gap Height',
                'desc' => 'Vertical separation between image and reflection (e.g., 0, 10px, 5%)',
                'value' => $parsed['reflection_gap'] ?? '',
                'placeholder' => 'For example: 10px'
            ],
            
            'reflection_start_opacity' => [
                'type' => 'text',
                'label' => 'Start Opacity',
                'desc' => 'Opacity at top of reflection, closest to image (0-100, default: 80)',
                'value' => $parsed['reflection_start_opacity'] ?? '',
                'placeholder' => 'For example: 80'
            ],
            
            'reflection_end_opacity' => [
                'type' => 'text',
                'label' => 'End Opacity', 
                'desc' => 'Opacity at bottom of reflection, furthest from image (0-100, default: 0)',
                'value' => $parsed['reflection_end_opacity'] ?? '',
                'placeholder' => 'For example: 0'
            ],
            
            'reflection_height' => [
                'type' => 'text',
                'label' => 'Reflection Height',
                'desc' => 'Height of reflection as dimension or percentage (e.g., 50%, 100px)',
                'value' => $parsed['reflection_height'] ?? '',
                'placeholder' => 'For example: 50%'
            ]
        ];
    }

    /**
     * Process form data to generate reflection parameter value in named attributes format
     * 
     * Generates self-documenting format: "reflection_gap:10|reflection_start_opacity:70|..."
     * 
     * @param string $parameter_name Parameter name (should be 'reflection')
     * @param array $form_data Form submission data
     * @return string Formatted reflection parameter value in named attributes format
     */
    public function processParameterFromForm(string $parameter_name, array $form_data): string 
    {
        $parts = [];

        // Get reflection components with defaults
        $gap = trim($form_data['reflection_gap'] ?? '');
        $start_opacity = trim($form_data['reflection_start_opacity'] ?? '');
        $end_opacity = trim($form_data['reflection_end_opacity'] ?? '');
        $height = trim($form_data['reflection_height'] ?? '');

        // Build named attributes format - include all non-empty values
        if (!empty($gap)) {
            $parts[] = "reflection_gap:{$gap}";
        }
        if (!empty($start_opacity)) {
            $parts[] = "reflection_start_opacity:{$start_opacity}";
        }
        if (!empty($end_opacity)) {
            $parts[] = "reflection_end_opacity:{$end_opacity}";
        }
        if (!empty($height)) {
            $parts[] = "reflection_height:{$height}";
        }

        // If no components specified, return empty
        if (empty($parts)) {
            return '';
        }

        // Return named attributes format with pipe separator
        return implode('|', $parts);
    }

    // NOTE: validateParameter() override removed
    // Now uses AbstractParameterPackage::validateParameter() which automatically
    // validates composite parameters using getSubParameterDefinitions()

    /**
     * Detect if parameter string uses named attribute format
     * 
     * Named format: "reflection_gap:10|reflection_start_opacity:70|..."
     * Positional format: "10,70,30,40%"
     * 
     * Note: Named format uses pipe separator, positional uses comma separator!
     * Single value like "0" or "10px" is positional.
     * 
     * @param string $reflection_parameter Reflection parameter string
     * @return bool True if named attribute format detected
     */
    private function isNamedAttributeFormat(string $reflection_parameter): bool
    {
        // Named format has "reflection_*:" pattern
        // Check for this pattern anywhere in string
        if (preg_match('/reflection_[a-z_]+:/', $reflection_parameter)) {
            return true;
        }
        
        // Otherwise positional (uses commas or single value)
        return false;
    }

    /**
     * Convert named attribute format to positional comma-delimited format
     * 
     * @param string $reflection_parameter Named attribute format string (pipe-separated)
     * @return string Positional comma-delimited format string
     */
    private function convertNamedToPositionalFormat(string $reflection_parameter): string
    {
        // Parse named attributes into associative array (uses pipes)
        $named_values = [];
        $parts = explode('|', trim($reflection_parameter));
        
        foreach ($parts as $part) {
            $part = trim($part);
            if (empty($part)) {
                continue;
            }
            
            // Split on first colon
            if (strpos($part, ':') !== false) {
                list($name, $value) = explode(':', $part, 2);
                $named_values[trim($name)] = trim($value);
            }
        }
        
        // Build positional format using commas
        $positional = [];
        
        // 1. Gap
        $positional[] = $named_values['reflection_gap'] ?? '0';
        
        // 2. Start opacity
        $positional[] = $named_values['reflection_start_opacity'] ?? '80';
        
        // 3. End opacity
        $positional[] = $named_values['reflection_end_opacity'] ?? '0';
        
        // 4. Reflection height
        $positional[] = $named_values['reflection_height'] ?? '50%';
        
        // Join with commas (reflection uses comma separator!)
        return implode(',', $positional);
    }
}
