#include "pch.h"

#include "legacy_recording_services.h"

#include "app.h"
#include "canvas.h"
#include "app_core/app_status.h"
#include "legacy_app_dialog_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_progress_bar.h"

namespace pp::panopainter {
void update_legacy_recording_frame_label(App& app);

namespace {

pp::app::RecordingWorkerIterationPlan make_recording_worker_iteration_plan(App& app)
{
    auto* legacy_canvas = Canvas::I;
    auto* canvas_document = app.canvas ? app.canvas->m_canvas.get() : nullptr;
    auto* encoder = legacy_canvas ? legacy_canvas->m_encoder.get() : nullptr;
    return pp::app::plan_recording_worker_iteration(
        app.rec_running,
        encoder != nullptr,
        legacy_canvas != nullptr && canvas_document != nullptr);
}

void encode_recording_frame(
    App& app,
    const pp::app::RecordingWorkerIterationPlan& plan,
    Canvas* legacy_canvas,
    Canvas* canvas_document,
    MP4Encoder* encoder)
{
    if (plan.clear_dirty_stroke)
        canvas_document->m_dirty_stroke = false;

    PBO equirect = legacy_canvas->m_layers_merge.gen_equirect_pbo(encoder->frame_size());
    std::this_thread::yield();

    ImageRef img;
    img.create(equirect.width, equirect.height, equirect.map());
    encoder->encode(img);
    equirect.unmap();
    LOG("rec frame encoded");

    if (plan.update_frame_label)
        update_legacy_recording_frame_label(app);
}

} // namespace

void update_legacy_recording_frame_label(App& app)
{
    if (auto txt = app.layout[app.main_id]->find<NodeText>("txt-rec"))
    {
        const auto label = pp::app::make_recording_frame_label(
            app.rec_running,
            Canvas::I->m_encoder != nullptr,
            Canvas::I->m_encoder ? Canvas::I->m_encoder->frames_count() : 0);
        txt->set_text(label.text.c_str());
    }
}

namespace {

class LegacyRecordingServices final : public pp::app::RecordingServices {
public:
    explicit LegacyRecordingServices(App& app) noexcept
        : app_(app)
    {
    }

    void start_thread() override
    {
        update_legacy_recording_frame_label(app_);
        app_.rec_thread = std::jthread(&App::rec_loop, &app_);
    }

    void stop_thread() override
    {
        app_.rec_thread.request_stop();
        app_.rec_running = false;
        app_.rec_cv.notify_all();
        if (app_.rec_thread.joinable())
            app_.rec_thread.join();
        update_legacy_recording_frame_label(app_);
    }

    void delete_recorded_files() override
    {
        app_.clear_platform_recorded_files(app_.rec_path);
    }

    void set_frame_count(int frame_count) override
    {
        app_.rec_count = frame_count;
    }

    void update_frame_label() override
    {
        update_legacy_recording_frame_label(app_);
    }

    void begin_export(int progress_total) override
    {
        pp::app::RecordingExportPlan plan;
        plan.progress_total = progress_total;
        progress_ = pp::panopainter::create_legacy_app_progress_dialog(
            app_,
            pp::app::plan_recording_export_progress_dialog(plan)).get();
    }

    void write_mp4(std::string_view path) override
    {
        Canvas::I->m_encoder->write_mp4(std::string(path));
    }

    void end_export() override
    {
        if (progress_)
            pp::panopainter::close_legacy_dialog_node(*progress_);
        progress_ = nullptr;
    }

private:
    App& app_;
    NodeProgressBar* progress_ = nullptr;
};

} // namespace

bool process_legacy_recording_worker_iteration(App& app)
{
    std::unique_lock<std::mutex> lock(app.rec_mutex);
    app.rec_cv.wait(lock);

    const auto plan = make_recording_worker_iteration_plan(app);
    if (!plan.continue_running)
        return false;

    auto* legacy_canvas = Canvas::I;
    auto* canvas_document = app.canvas ? app.canvas->m_canvas.get() : nullptr;
    auto* encoder = legacy_canvas ? legacy_canvas->m_encoder.get() : nullptr;
    if (plan.encode_frame && legacy_canvas && canvas_document && encoder)
        encode_recording_frame(app, plan, legacy_canvas, canvas_document, encoder);
    return true;
}

pp::foundation::Status execute_legacy_recording_start_action(
    App& app,
    pp::app::RecordingStartAction action)
{
    LegacyRecordingServices services(app);
    return pp::app::execute_recording_start_action(action, services);
}

pp::foundation::Status execute_legacy_recording_stop_action(
    App& app,
    pp::app::RecordingStopAction action)
{
    LegacyRecordingServices services(app);
    return pp::app::execute_recording_stop_action(action, services);
}

pp::foundation::Status execute_legacy_recording_clear_plan(
    App& app,
    const pp::app::RecordingClearPlan& plan)
{
    LegacyRecordingServices services(app);
    return pp::app::execute_recording_clear_plan(plan, services);
}

pp::foundation::Status execute_legacy_recording_export_plan(
    App& app,
    const pp::app::RecordingExportPlan& plan,
    std::string_view path)
{
    LegacyRecordingServices services(app);
    return pp::app::execute_recording_export_plan(plan, services, path);
}

} // namespace pp::panopainter
