//=============================================================================================================================
//
// Copyright (c) 2015-2022 VisionStar Information Technology (Shanghai) Co., Ltd. All Rights Reserved.
// EasyAR is the registered trademark or trademark of VisionStar Information Technology (Shanghai) Co., Ltd in China
// and other countries for the augmented reality technology developed by VisionStar Information Technology (Shanghai) Co., Ltd.
//
//=============================================================================================================================

#include "ar.hpp"
#ifdef _MSC_VER
#pragma warning( push, 1 )
#endif
#include <QtGui>
#ifdef _MSC_VER
#pragma warning( pop )
#endif

namespace easyar {

void HelloAR::loadFromImage(std::shared_ptr<easyar::ImageTracker> tracker, const std::string & path, const std::string & name)
{
    std::optional<std::shared_ptr<easyar::ImageTarget>> imageTarget = easyar::ImageTarget::createFromImageFile(path, easyar::StorageType::Assets, name, "", "", 1.0f);
    if (imageTarget.has_value())
    {
        tracker->loadTarget(imageTarget.value(), scheduler_, [](std::shared_ptr<easyar::Target> target, bool status)
        {
            std::printf("load target (%d): %s (%d)\n", status, target->name().c_str(), target->runtimeID());
        });
    }
}

void HelloAR::recreate_context()
{
    bgRenderer_ = nullptr;
    boxRenderer_ = nullptr;
    bgRenderer_ = std::make_shared<Renderer>();
    boxRenderer_ = std::make_shared<BoxRenderer>();
}

void HelloAR::initialize()
{
    scheduler_ = std::make_shared<easyar::DelayedCallbackScheduler>();

    recreate_context();

    throttler_ = easyar::InputFrameThrottler::create();
    i2FAdapter_ = easyar::InputFrameToFeedbackFrameAdapter::create();
    outputFrameFork_ = easyar::OutputFrameFork::create(2);
    outputFrameBuffer_ = easyar::OutputFrameBuffer::create();

    camera_ = easyar::CameraDeviceSelector::createCameraDevice(easyar::CameraDevicePreference::PreferObjectSensing);
    if (!camera_->openWithPreferredType(easyar::CameraDeviceType::Back)) { return; }
    camera_->setFocusMode(easyar::CameraDeviceFocusMode::Continousauto);
    camera_->setSize(easyar::Vec2I{{1280, 960}});
    camera_->setBufferCapacity(10);
    camera_->setStateChangedCallback(scheduler_, [](easyar::CameraState s)
    {
        if (s == easyar::CameraState::Disconnected)
        {
            std::printf("camera disconnected");
        }
        else if (s == easyar::CameraState::Preempted)
        {
            std::printf("camera preempted");
        }
    });

    tracker_ = easyar::ImageTracker::create();
    loadFromImage(tracker_, "sightplus/argame00.jpg", "argame00");
    loadFromImage(tracker_, "sightplus/argame01.jpg", "argame01");
    loadFromImage(tracker_, "sightplus/argame02.jpg", "argame02");
    loadFromImage(tracker_, "sightplus/argame03.jpg", "argame03");
    loadFromImage(tracker_, "idback.jpg", "idback");
    loadFromImage(tracker_, "namecard.jpg", "namecard");

    camera_->inputFrameSource()->connect(throttler_->input());
    throttler_->output()->connect(i2FAdapter_->input());
    i2FAdapter_->output()->connect(tracker_->feedbackFrameSink());
    tracker_->outputFrameSource()->connect(outputFrameFork_->input());
    outputFrameFork_->output(0)->connect(outputFrameBuffer_->input());

    outputFrameBuffer_->signalOutput()->connect(throttler_->signalInput());
    outputFrameFork_->output(1)->connect(i2FAdapter_->sideInput());
}

void HelloAR::finalize()
{
    tracker_ = nullptr;
    bgRenderer_ = nullptr;
    boxRenderer_ = nullptr;
    camera_ = nullptr;
    throttler_ = nullptr;
    outputFrameFork_ = nullptr;
    outputFrameBuffer_ = nullptr;
    i2FAdapter_ = nullptr;
    scheduler_ = nullptr;
}

bool HelloAR::start()
{
    bool status = true;
    if (camera_ != nullptr)
    {
        status &= camera_->start();
    }
    else
    {
        status = false;
    }
    if (tracker_ != nullptr)
    {
        status &= tracker_->start();
    }
    else
    {
        status = false;
    }
    return status;
}

void HelloAR::stop()
{
    if (camera_ != nullptr)
    {
        camera_->stop();
    }
    if (tracker_ != nullptr)
    {
        tracker_->stop();
    }
}

void HelloAR::render(int width, int height, int screenRotation)
{
    while (scheduler_->runOne())
    {
    }

    glViewport(0, 0, width, height);
    glClearColor(0.f, 0.f, 0.f, 1.f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    auto oFrame = outputFrameBuffer_->peek();
    if (!oFrame.has_value()) { return; }
    auto frame = oFrame.value();
    if (!frame->inputFrame()->hasCameraParameters()) { return; }
    auto cameraParameters = frame->inputFrame()->cameraParameters();
    float viewport_aspect_ratio = (float)width / (float)height;
    auto projection = cameraParameters->projection(0.01f, 1000.f, viewport_aspect_ratio, screenRotation, true, cameraHorizontalFlip_);
    auto imageProjection = cameraParameters->imageProjection(viewport_aspect_ratio, screenRotation, true, cameraHorizontalFlip_);
    auto image = frame->inputFrame()->image();
    if (frame->inputFrame()->index() != previousInputFrameIndex_)
    {
        auto buffer = image->buffer();
        bgRenderer_->upload(image->format(), image->width(), image->height(), image->pixelWidth(), image->pixelHeight(), buffer->data());
        previousInputFrameIndex_ = frame->inputFrame()->index();
    }
    bgRenderer_->render(imageProjection);
    for (auto && result : frame->results())
    {
        if (!result.has_value()) { continue; }
        auto imageTrackerResult = std::dynamic_pointer_cast<easyar::ImageTrackerResult>(result.value());
        if (imageTrackerResult != nullptr)
        {
            for (auto && targetInstance : imageTrackerResult->targetInstances())
            {
                auto status = targetInstance->status();
                auto target = targetInstance->target();
                auto imagetarget = std::dynamic_pointer_cast<easyar::ImageTarget>(target);
                if (imagetarget == nullptr)
                {
                    continue;
                }
                Vec2F targetSize{{imagetarget->scale(), imagetarget->scale() / imagetarget->aspectRatio()}};
                boxRenderer_->render(projection, targetInstance->pose(), targetSize);
            }
        }
    }

}

void HelloAR::setTrack(bool enable)
{
    track_enabled_ = enable;
    if (!tracker_)
        return;
    if (track_enabled_)
        tracker_->start();
    else
        tracker_->stop();
}

void HelloAR::setHorizontalFlip(bool flip)
{
    cameraHorizontalFlip_ = flip;
}

}
