<?php

/**
 * JCOGS Image Pro - Crop Parameter Package
 * ========================================
 * Sophisticated form interface for crop 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;

use JCOGSDesign\JCOGSImagePro\Service\ParameterRegistry;

/**
 * Crop Parameter Package
 * 
 * Provides sophisticated 4-field interface for crop parameter instead of requiring
 * users to construct complex pipe-separated parameter strings manually.
 * 
 * Crop Parameter Format (from JCOGS documentation):
 * crop="[yes|no|face_detect[|left|center|right|face_detect],[bottom|center|top|face_detect][|<integer>,<integer>[|[yes|no[|<integer>]]]]"
 * 
 * Components:
 * 1. Crop mode: yes, no, or face_detect
 * 2. Position: Horizontal (left, center, right, face_detect) and vertical (bottom, center, top, face_detect)
 * 3. Offset: Horizontal and vertical adjustments (can be pixels, %, negative values)
 * 4. Smart scaling: yes or no (default: yes)
 * 
 * Examples:
 * - crop="yes" - Default center crop with smart scaling
 * - crop="yes|center,top" - Crop from center-top position
 * - crop="yes|right,bottom|20,-20|no" - Crop from right-bottom with offset, no smart scaling
 * - crop="face_detect" - Auto-crop focused on detected faces
 * 
 * Documentation: https://jcogs.net/documentation/jcogs_img/jcogs_img-parameters#jcogs-image-parameter-crop
 */
class CropParameterPackage extends AbstractParameterPackage
{
    /**
     * Package identifier
     * 
     * @return string Package name
     */
    public function getName(): string 
    {
        return 'crop_parameter_package';
    }

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

    /**
     * Get the description for this parameter package
     * 
     * @return string Package description
     */
    public function getDescription(): string
    {
        return 'Controls for intelligent image cropping and positioning';
    }

    /**
     * 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 ['crop'];
    }

    /**
     * Normalize crop parameter to positional format
     * 
     * Accepts both formats and returns consistent positional pipe-delimited format:
     * - Input (positional): "yes|center,center|0,0|yes" → Output: "yes|center,center|0,0|yes"
     * - Input (named): "crop_mode:yes|crop_focus_h:center|crop_focus_v:center|..." → Output: "yes|center,center|0,0|yes"
     * 
     * This method is the bridge between template tags and crop filters,
     * ensuring backward compatibility while supporting the new named attribute system.
     * 
     * @param string $crop_parameter Crop parameter string in either format
     * @return string Normalized positional pipe-delimited format
     */
    public function normalizeCropParameter(string $crop_parameter): string
    {
        if (empty($crop_parameter)) {
            return '';
        }

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

        // Already in positional format
        return $crop_parameter;
    }

    /**
     * Get validation definitions for crop sub-parameters
     * 
     * This is the SINGLE SOURCE OF TRUTH for crop parameter validation rules.
     * All validation (preset forms, CP forms, filter processing) should use these definitions.
     * 
     * Crop parameter format: "mode|position|offset|smart_scaling"
     * Example: "yes|center,center|0,0|yes"
     * 
     * @return array Map of sub-parameter names to validation configurations
     */
    public function getSubParameterDefinitions(): array
    {
        return [
            // Sub-parameter 1: Crop mode
            'crop_mode' => [
                'label' => 'Crop Mode',
                'description' => 'Whether to crop the image and how to handle face detection',
                'type' => 'select',
                'default_value' => 'no',
                'validator' => 'enum',
                'validation_config' => [
                    'allowed_values' => ['yes', 'no', 'face_detect'],
                    'case_sensitive' => false
                ],
                'choices' => [
                    'no' => 'Resize To Fit (No Crop)',
                    'yes' => 'Crop To Exact Dimensions',
                    'face_detect' => 'Face Detection - Focus on detected faces'
                ]
            ],

            // Sub-parameter 2a: Crop horizontal position
            'crop_focus_h' => [
                'label' => 'Horizontal Position',
                'description' => 'Horizontal focus point for crop positioning',
                'type' => 'select',
                'default_value' => 'center',
                'validator' => 'enum',
                'validation_config' => [
                    'allowed_values' => ['left', 'center', 'right', 'face_detect'],
                    'case_sensitive' => false
                ],
                'choices' => [
                    'left' => 'Left',
                    'center' => 'Center (default)',
                    'right' => 'Right',
                    'face_detect' => 'Face Detection'
                ]
            ],

            // Sub-parameter 2b: Crop vertical position
            'crop_focus_v' => [
                'label' => 'Vertical Position',
                'description' => 'Vertical focus point for crop positioning',
                'type' => 'select',
                'default_value' => 'center',
                'validator' => 'enum',
                'validation_config' => [
                    'allowed_values' => ['top', 'center', 'bottom', 'face_detect'],
                    'case_sensitive' => false
                ],
                'choices' => [
                    'top' => 'Top',
                    'center' => 'Center (default)',
                    'bottom' => 'Bottom',
                    'face_detect' => 'Face Detection'
                ]
            ],

            // Sub-parameter 3a: Crop horizontal offset
            'crop_offset_x' => [
                'label' => 'Horizontal Offset',
                'description' => 'Fine-tune horizontal crop position (pixels or percentage)',
                'type' => 'text',
                'default_value' => '0',
                'validator' => 'numeric',
                'validation_config' => [
                    'allow_negative' => true,
                    'allow_percentage' => true,
                    'allow_units' => ['px', '%'],
                    'default_unit' => 'px'
                ],
                'placeholder' => '0 or 10px or 5%'
            ],

            // Sub-parameter 3b: Crop vertical offset
            'crop_offset_y' => [
                'label' => 'Vertical Offset',
                'description' => 'Fine-tune vertical crop position (pixels or percentage)',
                'type' => 'text',
                'default_value' => '0',
                'validator' => 'numeric',
                'validation_config' => [
                    'allow_negative' => true,
                    'allow_percentage' => true,
                    'allow_units' => ['px', '%'],
                    'default_unit' => 'px'
                ],
                'placeholder' => '0 or -10px or -2%'
            ],

            // Sub-parameter 4: Smart scaling
            'crop_smart_scaling' => [
                'label' => 'Smart Scaling',
                'description' => 'Optimize image before cropping for better quality',
                'type' => 'select',
                'default_value' => 'yes',
                'validator' => 'enum',
                'validation_config' => [
                    'allowed_values' => ['yes', 'no', 'y', 'n'],
                    'case_sensitive' => false
                ],
                'choices' => [
                    'yes' => 'Yes - Enable smart scaling (recommended)',
                    'no' => 'No - Use exact crop dimensions'
                ]
            ]
        ];
    }

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

    /**
     * Get form fields for crop 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 crop value and parse into components using the parser
        $current_crop = $current_values['crop'] ?? 'no';
        $parsed = $this->parseCompositeString('crop', $current_crop);
        
        return [
            'crop_enable' => [
                'type' => 'select',
                'label' => 'Crop Mode',
                'desc' => 'Choose how the image should be handled when it doesn\'t match the target dimensions',
                'value' => $parsed['crop_mode'] ?? 'no',
                'choices' => [
                    'no' => 'Resize To Fit (No Crop)',
                    'yes' => 'Crop To Exact Dimensions',
                    'face_detect' => 'Face Detection - Focus on detected faces'
                ],
                'placeholder' => 'For example: yes'
            ],
            
            'crop_focus_h' => [
                'type' => 'select',
                'label' => 'Horizontal Focus',
                'desc' => 'Choose horizontal position when cropping',
                'value' => $parsed['crop_focus_h'] ?? 'center',
                'choices' => [
                    'left' => 'Left',
                    'center' => 'Center (default)',
                    'right' => 'Right'
                ],
                'placeholder' => 'For example: center'
            ],
            
            'crop_focus_v' => [
                'type' => 'select',
                'label' => 'Vertical Focus',
                'desc' => 'Choose vertical position when cropping',
                'value' => $parsed['crop_focus_v'] ?? 'center',
                'choices' => [
                    'top' => 'Top',
                    'center' => 'Center (default)',
                    'bottom' => 'Bottom'
                ],
                'placeholder' => 'For example: center'
            ],
            
            'crop_offset_x' => [
                'type' => 'text',
                'label' => 'Horizontal Offset',
                'desc' => 'Fine-tune horizontal crop position (pixels or percentage)',
                'value' => $parsed['crop_offset_x'] ?? '0',
                'placeholder' => 'For example: 0 or 10px or 5%'
            ],
            
            'crop_offset_y' => [
                'type' => 'text',
                'label' => 'Vertical Offset',
                'desc' => 'Fine-tune vertical crop position (pixels or percentage)',
                'value' => $parsed['crop_offset_y'] ?? '0',
                'placeholder' => 'For example: 0 or -10px or -2%'
            ],
            
            'crop_smart_scaling' => [
                'type' => 'select',
                'label' => 'Smart Scaling',
                'desc' => 'Smart scaling optimizes the image before cropping to ensure better quality results',
                'value' => $parsed['crop_smart_scaling'] ?? 'yes',
                'choices' => [
                    'yes' => 'Yes - Enable smart scaling (recommended)',
                    'no' => 'No - Use exact crop dimensions'
                ],
                'placeholder' => 'For example: yes'
            ]
        ];
    }

    /**
     * Process form data to generate crop parameter value in named attributes format
     * 
     * Generates self-documenting named format: crop_mode:yes|crop_focus_h:center|crop_focus_v:center|...
     * 
     * @param string $parameter_name Parameter name (should be 'crop')
     * @param array $form_data Form submission data
     * @return string Formatted crop parameter value in named attributes format
     */
    public function processParameterFromForm(string $parameter_name, array $form_data): string 
    {
        $named_parts = [];

        // Part 1: Crop mode (Required)
        $crop_enable = trim($form_data['crop_enable'] ?? 'no');
        $named_parts[] = 'crop_mode:' . $crop_enable;

        // If crop is disabled, return just crop_mode:no
        if ($crop_enable === 'no') {
            return 'crop_mode:no';
        }

        // Part 2: Horizontal and vertical focus (separate fields now)
        $focus_h = trim($form_data['crop_focus_h'] ?? 'center');
        $focus_v = trim($form_data['crop_focus_v'] ?? 'center');
        $named_parts[] = 'crop_focus_h:' . $focus_h;
        $named_parts[] = 'crop_focus_v:' . $focus_v;

        // Part 3: Horizontal and vertical offsets (separate fields now)
        $offset_x = trim($form_data['crop_offset_x'] ?? '0');
        $offset_y = trim($form_data['crop_offset_y'] ?? '0');
        $named_parts[] = 'crop_offset_x:' . $offset_x;
        $named_parts[] = 'crop_offset_y:' . $offset_y;

        // Part 4: Smart Scaling
        $crop_smart_scaling = trim($form_data['crop_smart_scaling'] ?? 'yes');
        $named_parts[] = 'crop_smart_scaling:' . $crop_smart_scaling;

        return implode('|', $named_parts);
    }

    /**
     * Parse composite string into associative array
     * 
     * Supports three formats with priority:
     * 1. Named attributes: crop_mode:yes|crop_focus_h:center|... (preferred)
     * 2. Legacy positional: yes|center,center|0,0|yes (4-value format)
     * 3. Minimal positional: yes|center,center (2-3 value format)
     * 
     * @param string $param_name Parameter name (should be 'crop')
     * @param string $composite_string Composite parameter string
     * @return array Associative array of sub-parameters
     */
    public function parseCompositeString(string $param_name, string $composite_string): array
    {
        if (empty($composite_string)) {
            return $this->getDefaultValues();
        }

        // Priority 1: Named attributes format (has colons)
        if (strpos($composite_string, ':') !== false) {
            return $this->parseNamedAttributes($composite_string);
        }

        // Priority 2 & 3: Positional formats (pipe-delimited)
        return $this->parsePositionalFormat($composite_string);
    }

    /**
     * Parse named attributes format
     * 
     * @param string $named_string Named attributes string
     * @return array Associative array of sub-parameters
     */
    private function parseNamedAttributes(string $named_string): array
    {
        $values = $this->getDefaultValues();
        $parts = explode('|', $named_string);

        foreach ($parts as $part) {
            $part = trim($part);
            if (empty($part) || strpos($part, ':') === false) {
                continue;
            }

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

            // Map named attributes to array keys
            if (array_key_exists($key, $values)) {
                $values[$key] = $value;
            }
        }

        return $values;
    }

    /**
     * Parse positional format (legacy support)
     * 
     * Format: mode|position|offset|smart_scaling
     * Example: yes|center,center|0,0|yes
     * 
     * @param string $positional_string Positional format string
     * @return array Associative array of sub-parameters
     */
    private function parsePositionalFormat(string $positional_string): array
    {
        $values = $this->getDefaultValues();
        $parts = explode('|', $positional_string);

        // Part 1: Crop mode - treat empty string as null (fixes issue #100 with |||)
        if (isset($parts[0]) && trim($parts[0]) !== '') {
            $values['crop_mode'] = trim($parts[0]);
        }

        // If crop is disabled, return early with defaults
        if ($values['crop_mode'] === 'no') {
            return $values;
        }

        // Part 2: Position (horizontal,vertical) - treat empty string as null
        if (isset($parts[1]) && trim($parts[1]) !== '') {
            $position = explode(',', trim($parts[1]));
            if (isset($position[0]) && trim($position[0]) !== '') {
                $values['crop_focus_h'] = trim($position[0]);
            }
            if (isset($position[1]) && trim($position[1]) !== '') {
                $values['crop_focus_v'] = trim($position[1]);
            }
        }

        // Part 3: Offset (horizontal,vertical) - treat empty string as null
        if (isset($parts[2]) && trim($parts[2]) !== '') {
            $offset = explode(',', trim($parts[2]));
            if (isset($offset[0]) && trim($offset[0]) !== '') {
                $values['crop_offset_x'] = trim($offset[0]);
            }
            if (isset($offset[1]) && trim($offset[1]) !== '') {
                $values['crop_offset_y'] = trim($offset[1]);
            }
        }

        // Part 4: Smart scaling - treat empty string as null
        if (isset($parts[3]) && trim($parts[3]) !== '') {
            $values['crop_smart_scaling'] = trim($parts[3]);
        }

        return $values;
    }

    /**
     * Get default values for crop sub-parameters
     * 
     * @return array Default values
     */
    private function getDefaultValues(): array
    {
        return [
            'crop_mode' => 'no',
            'crop_focus_h' => 'center',
            'crop_focus_v' => 'center',
            'crop_offset_x' => '0',
            'crop_offset_y' => '0',
            'crop_smart_scaling' => 'yes'
        ];
    }

    /**
     * Validate crop parameter value
     * 
     * @param string $param_name Parameter name (should be 'crop')
     * @param mixed $value Parameter value to validate
     * @return bool|string True if valid, error message if invalid
     */
    protected function validateParameter(string $param_name, $value)
    {
        if ($param_name !== 'crop') {
            return parent::validateParameter($param_name, $value);
        }

        // Allow empty values (will be handled as 'no')
        if (empty($value)) {
            return true;
        }

        $crop_value = trim((string)$value);
        
        // 'no' or 'none' are always valid
        if (in_array(strtolower($crop_value), ['no', 'none'])) {
            return true;
        }

        // Parse the composite string into sub-parameters
        // This handles both named attributes and positional formats
        $parsed = $this->parseCompositeString($param_name, $crop_value);
        
        // Validate crop mode
        $crop_mode = $parsed['crop_mode'] ?? 'no';
        $result = ParameterRegistry::validateValue('enum', $crop_mode, [
            'allowed_values' => ['yes', 'no', 'face_detect'],
            'case_sensitive' => false
        ]);
        if (!$result->success) {
            return $result->error;
        }
        $crop_mode = $result->sanitizedValue;

        // If crop_mode is 'no', stop validation here
        if ($crop_mode === 'no') {
            return true;
        }

        // Validate horizontal focus position
        $result = ParameterRegistry::validateValue('enum', $parsed['crop_focus_h'] ?? 'center', [
            'allowed_values' => ['left', 'center', 'right'],
            'case_sensitive' => false
        ]);
        if (!$result->success) {
            return "Invalid horizontal focus: {$result->error}";
        }

        // Validate vertical focus position
        $result = ParameterRegistry::validateValue('enum', $parsed['crop_focus_v'] ?? 'center', [
            'allowed_values' => ['top', 'center', 'bottom'],
            'case_sensitive' => false
        ]);
        if (!$result->success) {
            return "Invalid vertical focus: {$result->error}";
        }

        // Validate offset X (supports pixels, percentages, or plain numbers)
        $result = ParameterRegistry::validateValue('numeric', $parsed['crop_offset_x'] ?? '0', [
            'allow_negative' => true,
            'allow_percentage' => true,
            'allow_units' => ['px', '%'],
            'default_unit' => 'px'
        ]);
        if (!$result->success) {
            return "Invalid offset X: {$result->error}";
        }

        // Validate offset Y (supports pixels, percentages, or plain numbers)
        $result = ParameterRegistry::validateValue('numeric', $parsed['crop_offset_y'] ?? '0', [
            'allow_negative' => true,
            'allow_percentage' => true,
            'allow_units' => ['px', '%'],
            'default_unit' => 'px'
        ]);
        if (!$result->success) {
            return "Invalid offset Y: {$result->error}";
        }

        // Validate smart scaling
        $result = ParameterRegistry::validateValue('enum', $parsed['crop_smart_scaling'] ?? 'yes', [
            'allowed_values' => ['yes', 'no', 'y', 'n'],
            'case_sensitive' => false
        ]);
        if (!$result->success) {
            return "Invalid smart scaling: {$result->error}";
        }

        return true;
    }

    /**
     * Detect if parameter string uses named attribute format
     * 
     * Named format: "crop_mode:yes|crop_focus_h:center|..."
     * Positional format: "yes|center,center|0,0|yes"
     * 
     * @param string $crop_parameter Crop parameter string
     * @return bool True if named attribute format detected
     */
    private function isNamedAttributeFormat(string $crop_parameter): bool
    {
        // Named format has colon separators in pipe-delimited parts
        // Check first few parts for "name:value" pattern
        $parts = explode('|', trim($crop_parameter));
        
        if (empty($parts)) {
            return false;
        }
        
        // Check first part - if it's "crop_*:" it's named format
        $first_part = trim($parts[0]);
        if (preg_match('/^crop_[a-z_]+:/', $first_part)) {
            return true;
        }
        
        // Otherwise positional
        return false;
    }

    /**
     * Convert named attribute format to positional pipe-delimited format
     * 
     * @param string $crop_parameter Named attribute format string
     * @return string Positional pipe-delimited format string
     */
    private function convertNamedToPositionalFormat(string $crop_parameter): string
    {
        // Parse named attributes into associative array
        $named_values = [];
        $parts = explode('|', trim($crop_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 defined order
        $positional = [];
        
        // 1. Crop mode (required)
        $crop_mode = $named_values['crop_mode'] ?? 'no';
        $positional[] = $crop_mode;
        
        // If crop is disabled, return just 'no'
        if ($crop_mode === 'no') {
            return 'no';
        }
        
        // 2. Position (horizontal,vertical)
        $focus_h = $named_values['crop_focus_h'] ?? 'center';
        $focus_v = $named_values['crop_focus_v'] ?? 'center';
        $positional[] = $focus_h . ',' . $focus_v;
        
        // 3. Offset (horizontal,vertical)
        $offset_x = $named_values['crop_offset_x'] ?? '0';
        $offset_y = $named_values['crop_offset_y'] ?? '0';
        $positional[] = $offset_x . ',' . $offset_y;
        
        // 4. Smart scaling
        $positional[] = $named_values['crop_smart_scaling'] ?? 'yes';
        
        // Join with pipe
        return implode('|', $positional);
    }
}
