#include <RadiumNBR/Gui/VisualizationGui.hpp>
#include <RadiumNBR/NodeBasedRenderer.hpp>
#include <RadiumNBR/Renderer/Visualization.hpp>

#include <fstream>

namespace RadiumNBR {
using namespace Gui;

RadiumNBR::Gui::RendererPanel*
buildControllerGui( NodeBasedRenderer* renderer, const std::function<void()>& appUpdateCallback ) {
    auto& controller = dynamic_cast<VisualizationController&>( renderer->getController() );

    auto controlPanel = new RendererPanel( renderer->getRendererName() );

    auto defColor = Ra::Core::Utils::Color::linearRGBTosRGB( renderer->getBackgroundColor() );
    auto clrClbck = [renderer, appUpdateCallback]( const Ra::Core::Utils::Color& clr ) {
        // set the background color for all passes that need it
        renderer->setBackgroundColor( clr );
        appUpdateCallback();
    };
    controlPanel->addColorInput( "Background Color", clrClbck, defColor );

    controlPanel->addSeparator();

    controlPanel->beginLayout( QBoxLayout::LeftToRight );

    controlPanel->addOption(
        " Post processing",
        [&controller, appUpdateCallback]( bool b ) {
            controller.enablePostProcess( b );
            appUpdateCallback();
        },
        true );

    controlPanel->addOption(
        " Show Debug ",
        [renderer, appUpdateCallback]( bool b ) {
            renderer->showDebug( b );
            appUpdateCallback();
        },
        false );

    controlPanel->addOption(
        " Show UI ",
        [renderer, appUpdateCallback]( bool b ) {
            renderer->showUI( b );
            appUpdateCallback();
        },
        false );
    controlPanel->endLayout( true );

    controlPanel->beginLayout( QBoxLayout::LeftToRight );
    auto envmpClbck = [&controller, appUpdateCallback]( const std::string& files ) {
      controller.setEnvMap( files );
      appUpdateCallback();
    };
    controlPanel->addFileInput(
        "Environment map", envmpClbck, "../", "Images (*.png *.jpg *.pfm *.exr *hdr)" );

    controlPanel->beginLayout( QBoxLayout::TopToBottom );
    controlPanel->addOption(
        " Show envMap ",
        [&controller, appUpdateCallback]( bool b ) {
          controller.showEnvMap( b );
          appUpdateCallback();
        },
        true );

    controlPanel->addSliderInput(
        " Environment strength ",
        [&controller, appUpdateCallback]( int v ) {
          controller.setEnvStrength( v );
          appUpdateCallback();
        },
        100 );
    controlPanel->endLayout();
    controlPanel->endLayout( true );


    // TODO : add a double slider to get the good value. ask @dlyr for its doubleSliderClass ...
    controlPanel->beginLayout( QBoxLayout::LeftToRight );
    auto splatSizeCtrl = [&controller]( double v ) { controller.setSplatSize( v ); };
    controlPanel->addPowerSliderInput(
        "Splat size", splatSizeCtrl, controller.getSplatSize(), 0, 5 );
    controlPanel->endLayout( true );

    controlPanel->beginLayout( QBoxLayout::LeftToRight );
    controlPanel->beginLayout( QBoxLayout::TopToBottom );

    //    auto visualizationFunction = controller.getAttribToColorFunc();

    auto getAttribCode = [&controller]() {
        auto [vs, gs, fs] = controller.getAttribToColorFunc();
        return vs;
    };
    auto getColorCode = [&controller]() {
        auto [vs, gs, fs] = controller.getAttribToColorFunc();
        return fs;
    };
    auto getGeometryCode = [&controller]() {
        auto [vs, gs, fs] = controller.getAttribToColorFunc();
        return gs;
    };

    controlPanel->addCodeEditor(
        "Edit attribute function",
        [&controller, getColorCode, getGeometryCode, appUpdateCallback]( const std::string& s ) {
            controller.setAttribToColorFunc( s, getGeometryCode(), getColorCode() );
            appUpdateCallback();
        },
        getAttribCode );

    controlPanel->addCodeEditor(
        "Edit Geometry function",
        [&controller, getAttribCode, getColorCode, appUpdateCallback]( const std::string& s ) {
            controller.setAttribToColorFunc( getAttribCode(), s, getColorCode() );
            appUpdateCallback();
        },
        getGeometryCode );

    controlPanel->addCodeEditor(
        "Edit Color function",
        [&controller, getAttribCode, getGeometryCode, appUpdateCallback]( const std::string& s ) {
            controller.setAttribToColorFunc( getAttribCode(), getGeometryCode(), s );
            appUpdateCallback();
        },
        getColorCode );
    controlPanel->endLayout( false );

    controlPanel->beginLayout( QBoxLayout::TopToBottom );
    auto loadAttribFuncClbk =
        [&controller, getColorCode, getGeometryCode, appUpdateCallback]( const std::string& file ) {
            if ( file.empty() ) { return; }
            std::cout << "Loading vertex attribute function from file " << file << std::endl;
            std::ifstream inFile( file );
            std::stringstream strStream;
            strStream << inFile.rdbuf(); // read the file
            controller.setAttribToColorFunc( strStream.str(), getGeometryCode(), getColorCode() );
            appUpdateCallback();
        };

    auto loadGeomFuncClbk =
        [&controller, getAttribCode, getColorCode, appUpdateCallback]( const std::string& file ) {
            if ( file.empty() ) { return; }
            std::cout << "Loading geometry attribute function from file " << file << std::endl;
            std::ifstream inFile( file );
            std::stringstream strStream;
            strStream << inFile.rdbuf(); // read the file
            controller.setAttribToColorFunc( getAttribCode(), strStream.str(), getColorCode() );
            appUpdateCallback();
        };

    auto loadColorFuncClbk = [&controller, getAttribCode, getGeometryCode, appUpdateCallback](
                                 const std::string& file ) {
        if ( file.empty() ) { return; }
        std::cout << "Loading color function from file " << file << std::endl;
        std::ifstream inFile( file );
        std::stringstream strStream;
        strStream << inFile.rdbuf(); // read the file
        controller.setAttribToColorFunc( getAttribCode(), getGeometryCode(), strStream.str() );
        appUpdateCallback();
    };
    controlPanel->addFileInput(
        "Load vertex attribute func", loadAttribFuncClbk, "./", "Shaders (*.glsl)" );
    controlPanel->addFileInput(
        "Load geometry attribute func", loadGeomFuncClbk, "./", "Shaders (*.glsl)" );
    controlPanel->addFileInput( "Load color func", loadColorFuncClbk, "./", "Shaders (*.glsl)" );
    controlPanel->endLayout( false );

    controlPanel->beginLayout( QBoxLayout::TopToBottom );
    auto saveAttribFuncClbk = [getAttribCode, appUpdateCallback]( const std::string& file ) {
        std::ofstream outFile( file );
        outFile << getAttribCode();
    };
    auto saveGeomFuncClbk = [getGeometryCode, appUpdateCallback]( const std::string& file ) {
        std::ofstream outFile( file );
        outFile << getGeometryCode();
    };
    auto saveColorFuncClbk = [getColorCode, appUpdateCallback]( const std::string& file ) {
        std::ofstream outFile( file );
        outFile << getColorCode();
    };
    controlPanel->addFileOuput(
        "Save vertex attribute func", saveAttribFuncClbk, "./", "Shaders (*.glsl)" );
    controlPanel->addFileOuput(
        "Save geometry attribute func", saveGeomFuncClbk, "./", "Shaders (*.glsl)" );
    controlPanel->addFileOuput( "Save color func", saveColorFuncClbk, "./", "Shaders (*.glsl)" );
    controlPanel->endLayout( false );

    controlPanel->endLayout( true );

    return controlPanel;
}

} // namespace RadiumNBR