//  SPDX-License-Identifier: MIT
//
//  EmulationStation Desktop Edition
//  MediaViewer.cpp
//
//  Fullscreen game media viewer.
//

#include "MediaViewer.h"

#include "Sound.h"
#include "components/VideoFFmpegComponent.h"
#include "views/ViewController.h"

MediaViewer::MediaViewer()
    : mVideo {nullptr}
    , mImage {nullptr}
{
    Window::getInstance()->setMediaViewer(this);
}

MediaViewer::~MediaViewer()
{
    if (mVideo) {
        delete mVideo;
        mVideo = nullptr;
    }
    if (mImage) {
        delete mImage;
        mImage = nullptr;
    }
}

bool MediaViewer::startMediaViewer(FileData* game)
{
    mHasVideo = false;
    mHasImages = false;
    mCurrentImageIndex = 0;
    mScreenshotIndex = -1;
    mTitleScreenIndex = -1;

    mGame = game;

    initiateViewer();

    if (mHasVideo || mHasImages)
        return true;
    else
        return false;
}

void MediaViewer::stopMediaViewer()
{
    NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
    ViewController::getInstance()->stopViewVideos();

    if (mVideo) {
        delete mVideo;
        mVideo = nullptr;
    }

    if (mImage) {
        delete mImage;
        mImage = nullptr;
    }

    mVideoFile = "";
    mImageFiles.clear();
}

void MediaViewer::update(int deltaTime)
{
    if (mVideo)
        mVideo->update(deltaTime);
}

void MediaViewer::render(const glm::mat4& /*parentTrans*/)
{
    glm::mat4 trans {Renderer::getIdentity()};
    Renderer::setMatrix(trans);

    // Render a black background below the game media.
    Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
                       0x000000FF, 0x000000FF);

    if (mVideo && !mDisplayingImage) {
        mVideo->render(trans);

        Renderer::postProcessingParams videoParameters;
        unsigned int shaders {0};
        if (Settings::getInstance()->getBool("MediaViewerVideoScanlines"))
            shaders = Renderer::SHADER_SCANLINES;
        if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
            shaders |= Renderer::SHADER_BLUR_HORIZONTAL;
            float heightModifier {Renderer::getScreenHeightModifier()};
            // clang-format off
            if (heightModifier < 1)
                videoParameters.blurPasses = 2;        // Below 1080
            else if (heightModifier >= 4)
                videoParameters.blurPasses = 12;       // 8K
            else if (heightModifier >= 2.9)
                videoParameters.blurPasses = 10;       // 6K
            else if (heightModifier >= 2.6)
                videoParameters.blurPasses = 8;        // 5K
            else if (heightModifier >= 2)
                videoParameters.blurPasses = 5;        // 4K
            else if (heightModifier >= 1.3)
                videoParameters.blurPasses = 3;        // 1440
            else if (heightModifier >= 1)
                videoParameters.blurPasses = 2;        // 1080
            // clang-format on
        }

        if (shaders != 0)
            Renderer::shaderPostprocessing(shaders, videoParameters);
    }
    else if (mImage && mImage->hasImage() && mImage->getSize() != glm::vec2 {}) {
        mImage->render(trans);

        if (mCurrentImageIndex == mScreenshotIndex &&
            Settings::getInstance()->getBool("MediaViewerScreenshotScanlines"))
            Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);
        else if (mCurrentImageIndex == mTitleScreenIndex &&
                 Settings::getInstance()->getBool("MediaViewerScreenshotScanlines"))
            Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);

        // This is necessary so that the video loops if viewing an image when
        // the video ends.
        if (mVideo)
            mVideo->handleLooping();
    }
}

void MediaViewer::initiateViewer()
{
    if (mGame->getType() == PLACEHOLDER)
        return;

    findMedia();

    if (!mHasVideo && !mHasImages)
        return;

    if (mHasVideo)
        playVideo();
    else
        showImage(0);
}

void MediaViewer::findMedia()
{
    std::string mediaFile;

    if ((mediaFile = mGame->getVideoPath()) != "") {
        mVideoFile = mediaFile;
        mHasVideo = true;
    }

    if (!mHasVideo && (mediaFile = mGame->getScreenshotPath()) != "") {
        mImageFiles.push_back(mediaFile);
        mScreenshotIndex = 0;
    }

    if ((mediaFile = mGame->getCoverPath()) != "")
        mImageFiles.push_back(mediaFile);

    if ((mediaFile = mGame->getBackCoverPath()) != "")
        mImageFiles.push_back(mediaFile);

    if ((mediaFile = mGame->getTitleScreenPath()) != "") {
        mImageFiles.push_back(mediaFile);
        mTitleScreenIndex = static_cast<int>(mImageFiles.size() - 1);
    }

    if (mHasVideo && (mediaFile = mGame->getScreenshotPath()) != "") {
        mImageFiles.push_back(mediaFile);
        mScreenshotIndex = static_cast<int>(mImageFiles.size() - 1);
    }

    if ((mediaFile = mGame->getFanArtPath()) != "")
        mImageFiles.push_back(mediaFile);

    if ((mediaFile = mGame->getMiximagePath()) != "")
        mImageFiles.push_back(mediaFile);

    if (!mImageFiles.empty())
        mHasImages = true;
}

void MediaViewer::showNext()
{
    if (mHasImages && mCurrentImageIndex != static_cast<int>(mImageFiles.size()) - 1)
        NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);

    bool showedVideo = false;

    if (mVideo && !mHasImages) {
        return;
    }
    else if (mVideo && !Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) {
        delete mVideo;
        mVideo = nullptr;
        showedVideo = true;
    }

    if (mImage) {
        delete mImage;
        mImage = nullptr;
    }

    if ((mVideo || showedVideo) && !mDisplayingImage)
        mCurrentImageIndex = 0;
    else if (static_cast<int>(mImageFiles.size()) > mCurrentImageIndex + 1)
        ++mCurrentImageIndex;

    if (mVideo)
        mDisplayingImage = true;

    showImage(mCurrentImageIndex);
}

void MediaViewer::showPrevious()
{
    if ((mHasVideo && mDisplayingImage) || (!mHasVideo && mCurrentImageIndex != 0))
        NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);

    if (mCurrentImageIndex == 0 && !mHasVideo) {
        return;
    }
    else if (mCurrentImageIndex == 0 && mHasVideo) {
        if (mImage) {
            delete mImage;
            mImage = nullptr;
        }
        mDisplayingImage = false;
        playVideo();
        return;
    }

    if (mImage) {
        delete mImage;
        mImage = nullptr;
    }

    --mCurrentImageIndex;
    showImage(mCurrentImageIndex);
}

void MediaViewer::playVideo()
{
    if (mVideo || mVideoFile == "")
        return;

    mDisplayingImage = false;
    ViewController::getInstance()->pauseViewVideos();

    mVideo = new VideoFFmpegComponent;
    mVideo->setOrigin(0.5f, 0.5f);
    mVideo->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f);

    if (Settings::getInstance()->getBool("MediaViewerStretchVideos"))
        mVideo->setResize(Renderer::getScreenWidth(), Renderer::getScreenHeight());
    else
        mVideo->setMaxSize(Renderer::getScreenWidth(), Renderer::getScreenHeight());

    mVideo->setVideo(mVideoFile);
    mVideo->setMediaViewerMode(true);
    mVideo->startVideoPlayer();
}

void MediaViewer::showImage(int index)
{
    if (mImage)
        return;

    mDisplayingImage = true;

    if (!mImageFiles.empty() && static_cast<int>(mImageFiles.size()) >= index) {
        mImage = new ImageComponent(false, false);
        mImage->setImage(mImageFiles[index]);
        mImage->setOrigin(0.5f, 0.5f);
        mImage->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f);
        mImage->setMaxSize(Renderer::getScreenWidth(), Renderer::getScreenHeight());
    }
}