<?php

namespace dynamicimages\images;

use dynamicimages\classes\dynamicimages_asset;
use dynamicimages\classes\dynamicimages_image;

class machine_1 extends dynamicimages_image
{

    const IMAGE_POWER_BUTTON = 'machine_1_button_start_stop.png';
    const IMAGE_PANEL_BACKGROUND = 'machine_1_panel_background.png';
    const IMAGE_WASH_PROGRAMS_THUMB = 'machine_1_wash_programs_thumb_standalone_transparent.png';
    const IMAGE_WASH_PROGRAMS = 'machine_1_programs_wheel_transparent.png';
    const IMAGE_BUTTON_HIGHLIGHTED = 'machine_1_button_highlighted_blue.png'; // 'machine_1_button_highlighted_transparent.png';
    const IMAGE_BUTTON_HIGHLIGHTED_BLUE = 'machine_1_button_highlighted_blue.png';
    const IMAGE_BUTTON_HIGHLIGHTED_GREY = 'machine_1_button_highlighted_grey.png';
    const IMAGE_BUTTON_HIGHLIGHTED_COMPLETED = 'machine_1_button_highlighted_completed_grey.png';
    const IMAGE_BUTTON_HIGHLIGHTED_GREEN = 'machine_1_button_highlighted_green.png';
    // Thumb
    public int $thumb_position = 1; // 0-11 (default: 0 = up = 270 degrees)
    public int $thumb_size = 1550; // height and width of the thumb
    // Buttons
    public int $button_highlight_size = 295; // size of the highlighted button
    public int $button_rows = 4; // Number of rows (on top of each other)
    public int $button_rows_spacing = 179; // Spacing between rows of buttons
    public int $button_last_row_spacing_buffer = -23; // Extra spacing buffer for the last row (The last row has a smaller distance)
    public int $button_columns = 3; // Number of columns (side by side)
    public int $button_columns_spacing = 442; // Spacing between columns of buttons
    public array $highlighted_buttons = [
        // Array of button positions to highlight (0 indexed, left to right, top to bottom)
        // e.g., 0, 2, 4, 7
    ];
    // Reset button
    public int $reset_button_highlight_size = 380; // size of the highlighted reset button
    // Start button
    public int $start_button_highlight_size = 300; // size of the highlighted start button
    // Button counts
    public int $button_counter = 0; // Is incremented every time a button is drawn
    // Current step
    public int $current_step = 0; // No "click counter" completed yet = 0
    // Config
    public bool $only_generate_current_step = false; // If true, only generate the current step button highlights


    /**
     * @throws \Exception
     */
    public function __construct()
    {
        $this->setAssetPath(self::asset_path . str_replace(__NAMESPACE__ . '\\', '', __CLASS__) . '/');
        parent::__construct([
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_POWER_BUTTON)),
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_PANEL_BACKGROUND)),
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_WASH_PROGRAMS_THUMB)),
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_WASH_PROGRAMS)),
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_BUTTON_HIGHLIGHTED)),
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_BUTTON_HIGHLIGHTED_BLUE)),
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_BUTTON_HIGHLIGHTED_COMPLETED)),
            new dynamicimages_asset(self::getAssetPath(self::IMAGE_BUTTON_HIGHLIGHTED_GREEN)),
        ]);
    }

    /**
     * @throws \Exception
     */
    public function setup(): void
    {
        $this->initImageCanvas(6000, 4500);
        // By default, the background should be shown with the programs on top and then on top of that the thumb
        $this->drawAsset($this->getAsset(self::IMAGE_PANEL_BACKGROUND), 0, 0);
        $this->drawProgramWheel();
        $this->drawThumb();
        $this->drawStepThumb();
        $this->drawHighlightedResetButton();
        $this->drawHighlightedButtons();
        $this->drawAsset($this->getAsset(self::IMAGE_POWER_BUTTON), 0, 0);
        $this->drawHighlightedStartButton();
        $this->drawCropOutLine(true);
    }

    public function drawCropOutLine(bool $crop = false): void
    {
        $line_template = [
            'x' => [
                'from' => 0,
                'to' => 6000
            ],
            'y' => [
                'from' => 0,
                'to' => 0
            ],
        ];
        // Define top crop line
        $top_line = [
            'x' => [
                'from' => 0,
                'to' => 6000
            ],
            'y' => [
                'from' => 262,
                'to' => 262
            ],
        ];
        // Define the left crop line
        $left_line = [
            'x' => [
                'from' => 194,
                'to' => 194
            ],
            'y' => [
                'from' => 0,
                'to' => 4500
            ],
        ];
        // Define the right crop line
        $right_line = [
            'x' => [
                'from' => 5765,
                'to' => 5765
            ],
            'y' => [
                'from' => 0,
                'to' => 4500
            ],
        ];
        // Define the bottom crop line
        $bottom_line = [
            'x' => [
                'from' => 0,
                'to' => 6000
            ],
            'y' => [
                'from' => 4160,
                'to' => 4160
            ],
        ];
        $lines = [
            $top_line,
            $left_line,
            $right_line,
            $bottom_line
        ];

        // If the crop parameter is false, just draw the outline
        if (!$crop) {
            // Draw the crop outline
            foreach ( $lines as $line ) {
                $x1 = (int)$line['x']['from'];;
                $y1 = (int)$line['y']['from'];;
                $x2 = (int)$line['x']['to'];;
                $y2 = (int)$line['y']['to'];;
                $this->drawLine($x1, $y1, $x2, $y2, 'rgba(255,0,0,0.75)', 1);
            }
        }
        // If the crop parameter is true, apply the crop
        else {
            $cropX = (int)$left_line['x']['from'];
            $cropY = (int)$top_line['y']['from'];
            $cropWidth = (int)6000 - $cropX - (6000 - (int)$right_line['x']['from']);
            $cropHeight = (int)4500 - $cropY - (4500 - (int)$bottom_line['y']['from']);
            $this->crop($cropWidth, $cropHeight, $cropX, $cropY);
        }
    }

    public function getHighlightedAssetName(): string
    {
        if ($this->button_counter == $this->current_step) {
            return self::IMAGE_BUTTON_HIGHLIGHTED_BLUE;
        }
        if ($this->button_counter < $this->current_step) {
            return self::IMAGE_BUTTON_HIGHLIGHTED_COMPLETED;
        }
        return self::IMAGE_BUTTON_HIGHLIGHTED_GREY;
    }

    /**
     * @throws \Exception
     */
    public function drawHighlightedStartButton(): void
    {
        $x = 4965; // X position for the start button
        $y = 1980; // Y position for the start button
        $tmp = new dynamicimages_asset($this->getAssetPath(self::getHighlightedAssetName()));
        $tmp->resize($this->start_button_highlight_size, $this->start_button_highlight_size);
        $tmp = $this->drawStepCounterOnButton($tmp);
        // If only generating current step, skip if not matching
        if ($this->only_generate_current_step && $this->button_counter != $this->current_step +1) {
            return;
        }
        $this->drawAsset($tmp, $x, $y);
        // Free memory used by temporary asset image, if any
        if (method_exists($tmp, 'clearMemoryImage')) {
            $tmp->clearMemoryImage();
        }
    }

    /**
     * @throws \Exception
     */
    public function drawHighlightedResetButton(): void
    {
        $x = 1736; // X position for the reset button
        $y = 599; // Y position for the reset button
        $tmp = new dynamicimages_asset($this->getAssetPath(self::getHighlightedAssetName()));
        $tmp->resize($this->reset_button_highlight_size, $this->reset_button_highlight_size);
        $tmp = $this->drawStepCounterOnButton($tmp);
        if ($this->only_generate_current_step && $this->button_counter != $this->current_step +1) {
            return;
        }
        $this->drawAsset($tmp, $x, $y);
        // Free memory used by temporary asset image, if any
        if (method_exists($tmp, 'clearMemoryImage')) {
            $tmp->clearMemoryImage();
        }
    }
    
    public function drawStepCounterOnButton(dynamicimages_asset $buttonAsset): dynamicimages_asset
    {
        // Draw a centered step counter number on the button
        $font = $this->getFontPath('AlfaSlabOne-Regular.ttf');
        $imgW = $buttonAsset->getWidth();
        $imgH = $buttonAsset->getHeight();
        // Virtual badge used only to size text well (no circle is drawn)
        $badgeDiameter = 0.40 * min($imgW, $imgH);
        $badgeRadius = $badgeDiameter / 2.0;

        // Badge center equals image center
        $cx = $imgW / 2.0;
        $cy = $imgH / 2.0;
        // With center/center alignment, offsets are deltas from image center
        $xOffset = (int)round($cx - ($imgW / 2.0));
        $yOffset = (int)round($cy - ($imgH / 2.0));

        // Dynamic font sizing to fit up to 2 digits inside the virtual badge
        $targetFrac = 0.72; // 72% of diameter
        $maxBox = $badgeDiameter * $targetFrac;
        $fontSize = (int)max(1, round($badgeDiameter * 0.6));
        try {
            $im = $buttonAsset->getMemoryImage();
            if ($im instanceof \Imagick) {
                $draw = new \ImagickDraw();
                $draw->setFont($font);
                for ($i = 0; $i < 6; $i++) {
                    $draw->setFontSize($fontSize);
                    $metrics = $im->queryFontMetrics($draw, (string)$this->getNextButtonStepCount(), false);
                    $this->button_counter--; // undo increment from metrics call
                    $textW = (int)ceil($metrics['textWidth'] ?? 0);
                    $textH = (int)ceil(($metrics['ascender'] ?? 0) - ($metrics['descender'] ?? 0));
                    if ($textW > $maxBox || $textH > $maxBox) {
                        $fontSize = (int)max(1, floor($fontSize * 0.9));
                    } else {
                        $next = (int)ceil($fontSize * 1.05);
                        if ($next === $fontSize) {
                            break;
                        }
                        $fontSize = $next;
                    }
                }
                // Safety: ensure we don't exceed the box
                $draw->setFontSize($fontSize);
                $metrics = $im->queryFontMetrics($draw, (string)$this->getNextButtonStepCount(), false);
                $this->button_counter--; // undo increment from metrics call
                $textW = (int)ceil($metrics['textWidth'] ?? 0);
                $textH = (int)ceil(($metrics['ascender'] ?? 0) - ($metrics['descender'] ?? 0));
                if ($textW > $maxBox || $textH > $maxBox) {
                    $fontSize = (int)max(1, floor($fontSize * 0.95));
                }
            }
        } catch (\Throwable $e) {
            // Fallback to initial estimate
        }

        $angle = 0;
        $color = [255, 255, 255];
        $text = (string)$this->getNextButtonStepCount();
        // If the step has been completed, do not draw the number
        if ($this->button_counter <= $this->current_step) {
            $text = '';
        }

        $buttonAsset->addTextOverlay($text, [
            'font_size' => $fontSize,
            'font_color' => $color,
            'font_path' => $font,
            'x_offset' => $xOffset,
            'y_offset' => $yOffset,
            'angle' => $angle,
            'align' => 'center',
            'valign' => 'center',
        ]);

        return $buttonAsset;
    }

    public function drawStepCounterOnButtonTopRight(dynamicimages_asset $buttonAsset): dynamicimages_asset
    {
        $font = $this->getFontPath('AlfaSlabOne-Regular.ttf');
        // The blue badge circle occupies 25% of the button width/height (diameter)
        $imgW = $buttonAsset->getWidth();
        $imgH = $buttonAsset->getHeight();
        $badgeDiameter = 0.25 * min($imgW, $imgH);
        $badgeRadius = $badgeDiameter / 2.0;

        // Center of the badge is near the top-right corner; apply inner inset to match actual asset layout
        // Final calibrated insets: 50% of badge diameter inward from right and downward from top
        $centerXNudgeIn = 0.50 * $badgeDiameter; // inward from right edge (~50% of diameter)
        $centerYNudgeIn = 0.50 * $badgeDiameter; // downward from top edge (~50% of diameter)
        $cx = $imgW - $badgeRadius - $centerXNudgeIn; // adjusted circle center X
        $cy = $badgeRadius + $centerYNudgeIn;         // adjusted circle center Y

        // We use center/center alignment and offset from the image center to the badge center
        $baseCenterXOffset = (int)round($cx - ($imgW / 2.0));
        $baseCenterYOffset = (int)round($cy - ($imgH / 2.0));
        $xOffset = $baseCenterXOffset;
        $yOffset = $baseCenterYOffset;

        // Temporarily disable optical nudge while calibrating true badge center
        $xNudge = 0.00 * $badgeDiameter;
        $yNudge = 0.00 * $badgeDiameter;
        $xOffset -= (int)round($xNudge);
        $yOffset += (int)round($yNudge);

        // Optical nudge remains disabled; visual centering confirmed with calibrated center.

        // Compute a dynamic font size that fits up to 2 digits comfortably inside the badge
        // Target occupied size inside the circle (padding from edges)
        $targetFrac = 0.72; // 72% of diameter for both width and height
        $maxBox = $badgeDiameter * $targetFrac;

        // Start with an optimistic font size close to the target box
        $fontSize = (int)max(1, round($badgeDiameter * 0.6));
        try {
            // Try to refine using Imagick metrics if available
            $im = $buttonAsset->getMemoryImage();
            if ($im instanceof \Imagick) {
                $draw = new \ImagickDraw();
                $draw->setFont($font);
                // Iterate a few times to converge 
                for ($i = 0; $i < 6; $i++) {
                    $draw->setFontSize($fontSize);
                    $metrics = $im->queryFontMetrics($draw, (string)$this->getNextButtonStepCount(), false);
                    // We queried getNextButtonStepCount() for metrics; revert increment afterwards
                    $this->button_counter--; // undo increment from metrics call
                    $textW = (int)ceil($metrics['textWidth'] ?? 0);
                    $textH = (int)ceil(($metrics['ascender'] ?? 0) - ($metrics['descender'] ?? 0));

                    // If it exceeds maxBox in either dimension, reduce; otherwise increase slightly
                    if ($textW > $maxBox || $textH > $maxBox) {
                        $fontSize = (int)max(1, floor($fontSize * 0.9));
                    } else {
                        // Try to expand a bit to approach the limit
                        $next = (int)ceil($fontSize * 1.05);
                        if ($next === $fontSize) {
                            break;
                        }
                        $fontSize = $next;
                    }
                }
                // Final safety cap not to exceed the box 
                $draw->setFontSize($fontSize);
                $metrics = $im->queryFontMetrics($draw, (string)$this->getNextButtonStepCount(), false);
                $this->button_counter--; // undo increment from metrics call
                $textW = (int)ceil($metrics['textWidth'] ?? 0);
                $textH = (int)ceil(($metrics['ascender'] ?? 0) - ($metrics['descender'] ?? 0));
                if ($textW > $maxBox || $textH > $maxBox) {
                    $fontSize = (int)max(1, floor($fontSize * 0.95));
                }
            }
        } catch (\Throwable $e) {
            // Fallback: keep the initial estimate on any error
        }
        $angle = 0;
        $color = [255, 255, 255]; // White color for the text
        $text = (string)$this->getNextButtonStepCount();
        // If the step has been completed, do not draw the number
        if ($this->button_counter <= $this->current_step) {
            $text = '';
        }
        $buttonAsset->addTextOverlay($text, [
            'font_size' => $fontSize,
            'font_color' => $color,
            'font_path' => $font,
            'x_offset' => $xOffset,
            'y_offset' => $yOffset,
            'angle' => $angle,
            'align' => 'center',
            'valign' => 'center',
        ]);

        // Debug guides removed after calibration
        return $buttonAsset;
    }

    /**
     * @throws \Exception
     */
    public function drawHighlightedButtons(): void
    {
        $startX = 2815; // Starting X position for the first button
        $startY = 1265; // Starting Y position for the first button

        for ($row = 0; $row < $this->button_rows; $row++) {
            for ($col = 0; $col < $this->button_columns; $col++) {
                // If the current button position is not in the highlighted buttons array, skip it
                $buttonIndex = $row * $this->button_columns + $col;
                if (!in_array($buttonIndex, $this->highlighted_buttons, true)) {
                    continue;
                }
                // Calculate the position for the current button
                $x = $startX + ($col * ($this->button_highlight_size + $this->button_columns_spacing));
                $y = $startY + ($row * ($this->button_highlight_size + $this->button_rows_spacing));
                // Last row adjustment
                if ($row === $this->button_rows - 1) {
                    $y += $this->button_last_row_spacing_buffer;
                }
                $tmp = new dynamicimages_asset($this->getAssetPath(self::getHighlightedAssetName()));
                $tmp->resize($this->button_highlight_size, $this->button_highlight_size);
                $tmp = $this->drawStepCounterOnButton($tmp);
                if ($this->only_generate_current_step && $this->button_counter != $this->current_step +1) {
                    continue;
                }
                $this->drawAsset($tmp, $x, $y);
                // Free memory used by temporary asset image, if any
                if (method_exists($tmp, 'clearMemoryImage')) {
                    $tmp->clearMemoryImage();
                }
            }
        }
    }

    /**
     * @return int
     */
    public function getNextButtonStepCount(): int
    {
        $this->button_counter++;
        return $this->button_counter;
    }

    /**
     * @throws \Exception
     */
    public function drawProgramWheel(): void
    {
        // Match the wheel center to the thumb's visual center
        $thumbX = 650;
        $thumbY = 1290;
        $buffer = 600; // Pixels to increase the wheel size beyond the thumb edges
        $thumbX -= $buffer / 2;
        $thumbY -= $buffer / 2;
        $thumbWidth = $this->calculateThumbWidth($this->thumb_size) + $buffer;
        $thumbHeight = $this->thumb_size + $buffer;
        $tmp = new dynamicimages_asset($this->getAssetPath(self::IMAGE_WASH_PROGRAMS));
        $tmp->resize($thumbWidth, $thumbHeight);
        $this->drawAsset($tmp, $thumbX, $thumbY);
        // Free memory used by temporary asset image, if any
        if (method_exists($tmp, 'clearMemoryImage')) {
            $tmp->clearMemoryImage();
        }
    }

    public function getRotationThumbPosition(): float
    {
        // Each position represents a 30 degree increment (12 positions in total for 360 degrees)
        $degrees = -90;
        return ($this->thumb_position) * 30 + $degrees;
    }

    public function calculateThumbWidth(int $height): int
    {
        $originalWidth = $this->getAsset(self::IMAGE_WASH_PROGRAMS_THUMB)->getWidth();
        $originalHeight = $this->getAsset(self::IMAGE_WASH_PROGRAMS_THUMB)->getHeight();
        return (int)round(($originalWidth / $originalHeight) * $height);
    }

    /**
     * @return void
     * @throws \Exception
     */
    public function drawThumb(): void
    {
        // Thumb parameters
        $thumbX = 650;
        $thumbY = 1290;
        // Base resized thumb to compute the visual pivot (center coordinates)
        $baseThumb = $this->getAsset(self::IMAGE_WASH_PROGRAMS_THUMB)->resize($this->calculateThumbWidth($this->thumb_size), $this->thumb_size);
        $centerX = $thumbX + (int)floor($baseThumb->getWidth() / 2);
        $centerY = $thumbY + (int)floor($baseThumb->getHeight() / 2);

        $tmp = new dynamicimages_asset($this->getAssetPath(self::IMAGE_WASH_PROGRAMS_THUMB));
        $tmp->resize($this->calculateThumbWidth($this->thumb_size), $this->thumb_size)
            ->rotate($this->getRotationThumbPosition());
        $this->drawAssetCentered($tmp, $centerX, $centerY);
        // Free memory used by temporary asset image, if any
        if (method_exists($tmp, 'clearMemoryImage')) {
            $tmp->clearMemoryImage();
        }
    }

    /**
     * @throws \Exception
     */
    public function drawStepThumb(): void
    {
        // Draw the blue step indicator on the thumb
        $thumbX = 650;
        $thumbY = 1290;
        $stepSize = 350; // Size of the step indicator
        // Base resized thumb to compute the visual pivot (center coordinates)
        $baseThumb = $this->getAsset(self::IMAGE_WASH_PROGRAMS_THUMB)->resize($this->calculateThumbWidth($this->thumb_size), $this->thumb_size);
        $centerX = $thumbX + (int)floor($baseThumb->getWidth() / 2);
        $centerY = $thumbY + (int)floor($baseThumb->getHeight() / 2);
        $angle = $this->getRotationThumbPosition();
        // Calculate position to draw the step indicator
        $radius = ($this->thumb_size / 2) - ($stepSize / 2) - 150; // Radius from center to position the step indicator
        $rad = deg2rad($angle);
        $stepX = $centerX + (int)round($radius * cos($rad)) - (int)floor($stepSize / 2);
        $stepY = $centerY + (int)round($radius * sin($rad)) - (int)floor($stepSize / 2);
        $tmp = new dynamicimages_asset($this->getAssetPath(self::getHighlightedAssetName()));
        $tmp->resize($stepSize, $stepSize);
        $tmp = $this->drawStepCounterOnButton($tmp);
        if ($this->only_generate_current_step && $this->button_counter != $this->current_step +1) {
            return;
        }
        $this->drawAsset($tmp, $stepX, $stepY);
        // Free memory used by temporary asset image, if any
        if (method_exists($tmp, 'clearMemoryImage')) {
            $tmp->clearMemoryImage();
        }
    }
}