#include <Core/Log/LOG.h>
#include <Core/Meshes/TRIANGULATED_SURFACE.h>
#include <Core/Parsing/PARSE_ARGS.h>
#include <Core/Utilities/VIEWER_DIR.h>
#include "components/QT_COMPONENT.h"
#include "QT_CAMERA.h"
#include "QT_MAIN_WINDOW.h"
#include "QT_PICK.h"
#include "QT_RENDER_UTILS.h"
#include "QT_WINDOW.h"
#include "QT_WORLD.h"
#include <QGraphicsSceneResizeEvent>
#include <QKeyEvent>
#include <QOpenGLContext>
#include <QOpenGLDebugMessage>
#include <QOpenGLFramebufferObject>
#include <QOpenGLFunctions>
#include <QOpenGLPaintDevice>
#include <QOpenGLShaderProgram>
#include <QPainter>
#include <QScreen>

namespace PhysBAM{
//#####################################################################
// Constructor
//#####################################################################
QT_WINDOW::
QT_WINDOW(QT_MAIN_WINDOW& main_window)
    :main_window(main_window),gl(*new QOpenGLFunctions)
{
    QObject::connect(this,SIGNAL(resized()),this,SLOT(Resize()));
}
//#####################################################################
// Destructor
//#####################################################################
QT_WINDOW::
~QT_WINDOW()
{
    delete device;
    delete ru;
    delete &gl;
}
//#####################################################################
// Function event
//#####################################################################
bool QT_WINDOW::
event(QEvent *event)
{
    switch(event->type())
    {
        case QEvent::MouseButtonPress:
            {
                auto e=dynamic_cast<QMouseEvent*>(event);
                auto mod=e->modifiers();
                if(mod==Qt::NoModifier)
                {
                    if(e->button()&Qt::LeftButton)
                    {
                        main_window.camera.Begin_Rotation(e->x(), e->y());
                    }
                    else if(e->button()&Qt::MiddleButton)
                    {
                        main_window.camera.Begin_Translation(e->x(), e->y());
                    }
                    else if(e->button()&Qt::RightButton)
                    {
                        main_window.camera.Begin_Zoom(e->x(), e->y());
                    }
                }
                else if(mod==Qt::ShiftModifier)
                {
                    if(e->button()&Qt::LeftButton)
                    {
                        Select(e->x(), e->y());
                    }
                    else if(e->button()&Qt::MiddleButton)
                    {
                        Select(e->x(), e->y());
                        auto X=main_window.world.selected->Selection_Point();
                        main_window.camera.Look_At(X);
                    }
                }
            }
            return true;
        case QEvent::MouseButtonRelease:
            {
                auto e=dynamic_cast<QMouseEvent*>(event);
                if(e->button()&Qt::LeftButton)
                {
                    main_window.camera.End_Rotation(e->x(), e->y());
                    update();
                }
                else if(e->button()&Qt::MiddleButton)
                {
                    main_window.camera.End_Translation(e->x(), e->y());
                    update();
                }
                else if(e->button()&Qt::RightButton)
                {
                    main_window.camera.End_Zoom(e->x(), e->y());
                    update();
                }
            }
            return true;
        case QEvent::MouseMove:
            {
                auto e=dynamic_cast<QMouseEvent*>(event);
                if(e->buttons()&Qt::LeftButton)
                {
                    main_window.camera.Apply_Rotation(e->x(), e->y());
                    update();
                }
                else if(e->buttons()&Qt::MiddleButton)
                {
                    main_window.camera.Apply_Translation(e->x(), e->y());
                    update();
                }
                else if(e->buttons()&Qt::RightButton)
                {
                    main_window.camera.Apply_Zoom(e->x(), e->y());
                    update();
                }
            }
            return true;
        default:
            return QOpenGLWidget::event(event);
    }
}
//#####################################################################
// Function Select
//#####################################################################
void QT_WINDOW::
Select(int x,int y)
{
    QOpenGLFramebufferObjectFormat fmt;
    fmt.setAttachment(QOpenGLFramebufferObject::Depth);
    QOpenGLFramebufferObject fbo(width(),height(),fmt);
    fbo.bind();

    for(const auto& o:main_window.world.objects)
        for(const auto& it:o)
            it.second->Clear_Selection();
    main_window.world.selected=nullptr;
    
    main_window.world.Render(*ru,true);
    QImage select_image(fbo.toImage());
    if(save_select)
    {
        static int id=0;
        select_image.save(QString::fromStdString(LOG::sprintf("select-%03d.png",id++)));
    }
    auto c=select_image.pixel(x,y);
    int num = (qBlue(c)*256+qGreen(c))*256+qRed(c);
    CHECK_ERRORS(ru);
    if(num==0)
    {
        main_window.Update_Selected();
        return;
    }
    ARRAY<int> names;
    ru->pick.Find(names,num);
    PHYSBAM_ASSERT(names.m>=2);
    int i=0,k=names(1);
    for(const auto& o:main_window.world.objects[names(0)])
        if(i++==k)
        {
            if(o.second->parent)
            {
                LOG::printf("Selected child object %P\n",o.second->Name());
                break;
            }
            main_window.world.selected=&*o.second;
            main_window.world.selected->Set_Selection(names.Array_View(2,names.m-2));
            break;
        }
    main_window.Update_Selected();
}
//#####################################################################
// Function Capture
//#####################################################################
void QT_WINDOW::
Capture(const std::string& filename_base,int first_frame,int last_frame)
{
    VIEWER_DIR::SAVE_STATE save_state;
    main_window.world.viewer_dir.Save_State(save_state);
    main_window.world.viewer_dir.Set(first_frame);
    if(!main_window.world.viewer_dir.Valid_Frame(true))
    {
        main_window.world.viewer_dir.Restore_State(save_state);
        main_window.world.Read();
        return;
    }
    main_window.world.Read();
    QOpenGLFramebufferObjectFormat fmt;
    fmt.setAttachment(QOpenGLFramebufferObject::Depth);
    QOpenGLFramebufferObject fbo(width(),height(),fmt);
    fbo.bind();

    for(const auto& o:main_window.world.objects)
        for(const auto& it:o)
            it.second->Clear_Selection();
    main_window.world.selected=nullptr;

    for(int f=first_frame;f<=last_frame;f++)
    {
        auto file=LOG::sprintf("%P-%04d.png",filename_base,f-first_frame);
        LOG::printf("capturing %P\n",file);
        main_window.world.Render(*ru,false);
        fbo.toImage().save(QString::fromStdString(file));
        if(!main_window.world.Next_Frame(false)) break;
    }
    CHECK_ERRORS(ru);
    main_window.world.viewer_dir.Restore_State(save_state);
    main_window.world.Read();
}
//#####################################################################
// Function Render
//#####################################################################
void QT_WINDOW::
Render(bool dump_selection_info)
{
    makeCurrent();
    main_window.world.Render(*ru,render_pick_buffer);

    if(!render_pick_buffer)
    {
        std::string text=ru->info_text.str();
        if(dump_selection_info) LOG::printf("%P\n",text);
        QString qtext=QString::fromStdString(text);
        QPainter painter(this);
        painter.setPen(Qt::white);
        painter.setFont(QFont());
        painter.drawText(rect(),Qt::AlignTop,qtext);
        painter.end();
    }
}
//#####################################################################
// Function Dump_Selection_Info
//#####################################################################
void QT_WINDOW::
Dump_Selection_Info()
{
    Render(true);
}
//#####################################################################
// Function Resize
//#####################################################################
void QT_WINDOW::
Resize()
{
    main_window.camera.Set_Size(width(),height());
}
//#####################################################################
// Function initializeGL
//#####################################################################
void QT_WINDOW::
initializeGL()
{
    gl_context = context();
    auto gl_format=format();
    gl_format.setOption(QSurfaceFormat::DebugContext);
    makeCurrent();
    gl.initializeOpenGLFunctions();
    ru=new QT_RENDER_UTILS(this,gl,main_window.camera);
    main_window.world.Update_Settings();
}
//#####################################################################
// Function paintGL
//#####################################################################
void QT_WINDOW::
paintGL()
{
    Render();
}
}
