<?php

declare(strict_types=1);

namespace Intervention\Image\Drivers\Gd\Decoders;

use Exception;
use GdImage;
use Intervention\Gif\Decoder as GifDecoder;
use Intervention\Gif\Splitter as GifSplitter;
use Intervention\Image\Drivers\Gd\Core;
use Intervention\Image\Drivers\Gd\Frame;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Image;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\ImageInterface;

class NativeObjectDecoder extends AbstractDecoder
{
    /**
     * {@inheritdoc}
     *
     * @see DecoderInterface::decode()
     */
    public function decode(mixed $input): ImageInterface|ColorInterface
    {
        if (!is_object($input)) {
            throw new DecoderException('Unable to decode input');
        }

        if (!($input instanceof GdImage)) {
            throw new DecoderException('Unable to decode input');
        }

        if (!imageistruecolor($input)) {
            imagepalettetotruecolor($input);
        }

        imagesavealpha($input, true);

        // build image instance
        return new Image(
            $this->driver(),
            new Core([
                new Frame($input)
            ])
        );
    }

    /**
     * Decode image from given GIF source which can be either a file path or binary data
     *
     * Depending on the configuration, this is taken over by the native GD function
     * or, if animations are required, by our own extended decoder.
     *
     * @param mixed $input
     * @throws RuntimeException
     * @return ImageInterface
     */
    protected function decodeGif(mixed $input): ImageInterface
    {
        // create non-animated image depending on config
        if (!$this->driver()->config()->decodeAnimation) {
            $native = match (true) {
                $this->isGifFormat($input) => @imagecreatefromstring($input),
                default => @imagecreatefromgif($input),
            };

            if ($native === false) {
                throw new DecoderException('Unable to decode input.');
            }

            $image = self::decode($native);
            $image->origin()->setMediaType('image/gif');

            return $image;
        }

        try {
            // create empty core
            $core = new Core();

            $gif = GifDecoder::decode($input);
            $splitter = GifSplitter::create($gif)->split();
            $delays = $splitter->getDelays();

            // set loops on core
            if ($loops = $gif->getMainApplicationExtension()?->getLoops()) {
                $core->setLoops($loops);
            }

            // add GDImage instances to core
            foreach ($splitter->coalesceToResources() as $key => $native) {
                $core->push(
                    new Frame($native, $delays[$key] / 100)
                );
            }
        } catch (Exception $e) {
            throw new DecoderException($e->getMessage(), $e->getCode(), $e);
        }

        // create (possibly) animated image
        $image = new Image($this->driver(), $core);

        // set media type
        $image->origin()->setMediaType('image/gif');

        return $image;
    }
}
