/*
 *  Copyright (c) 2000-2022 Inria
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  * Neither the name of the ALICE Project-Team nor the names of its
 *  contributors may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  Contact: Bruno Levy
 *
 *     https://www.inria.fr/fr/bruno-levy
 *
 *     Inria,
 *     Domaine de Voluceau,
 *     78150 Le Chesnay - Rocquencourt
 *     FRANCE
 *
 */

#include <geogram_gfx/gui/simple_application.h>
#include <geogram_gfx/lua/lua_glup.h>
#include <geogram/lua/lua_io.h>
#include <geogram/basic/command_line.h>
#include <geogram/basic/file_system.h>
#include <algorithm>

extern "C" {
#include <geogram/third_party/lua/lua.h>
#include <geogram/third_party/lua/lauxlib.h>
#include <geogram/third_party/lua/lualib.h>
}

extern void register_embedded_lua_files(void);

namespace {

    using namespace GEO;

    /**
     * \brief An application that demonstrates both
     *  GLUP primitives and glup_viewer application
     *  framework.
     */
    class GeoCodApplication : public SimpleApplication {
    public:

        /**
         * \brief GeoCodApplication constructor.
         */
        GeoCodApplication() : SimpleApplication("geocod") {
	    set_default_filename("hello.lua");
	    init_graphics_called_ = false;
	    use_text_editor_ = true;
	    text_editor_was_visible_ = false;
            // Define the 3d region that we want to display
            // (xmin, ymin, zmin, xmax, ymax, zmax)
            set_region_of_interest(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
	    register_embedded_lua_files();
	    exec_command("require(\"preamble\")");
	    console_->show_command_prompt();
        }

        /**
         * \brief GeoCodApplication destructor.
         */
	~GeoCodApplication() override {
        }
        
        /**
         * \brief Displays and handles the GUI for object properties.
         * \details Overloads Application::draw_object_properties().
         */
	void draw_object_properties() override {
	    if(!lua_error_occured_) {
		exec_command("imgui.draw_object_properties()");
	    }
        }

        /**
         * \brief Draws the scene according to currently set primitive and
         *  drawing modes.
         */
	void draw_scene() override {
	    if(text_editor_was_visible_ && !text_editor_visible_) {
		run_program();
	    }
	    if(!lua_error_occured_) {
		exec_command("GLUP.draw_scene()");
	    }
	    text_editor_was_visible_ = text_editor_visible_;
        }

        /**
         * \brief Initializes graphics objects.
         * \details This function overloads Application::init_graphics(). It
         *  is called as soon as the OpenGL context is ready for rendering. It
         *  is meant to initialize the graphic objects used by the application.
         */
	void GL_initialize() override {
            SimpleApplication::GL_initialize();
	    if(!lua_error_occured_) {
		exec_command("GLUP.init_graphics()");
	    }
	    init_graphics_called_ = true;
        }

	void embedded_files_menu(const std::string& prefix) {
	    std::vector<std::string> embedded_files;
	    list_embedded_lua_files(embedded_files);
	    std::sort(embedded_files.begin(), embedded_files.end());
	    for(index_t i=0; i<embedded_files.size(); ++i) {
		if(String::string_starts_with(
		       embedded_files[i],prefix) &&
		   ImGui::MenuItem(embedded_files[i].c_str())
		) {
		    const char* data;
		    get_embedded_lua_file(embedded_files[i].c_str(), &data);
		    text_editor_.load_data(data);
		    run_program();
		    current_file_ = "";		    			
		}
	    }
	}

	
	virtual void draw_fileops_menu() override {
	    if(ImGui::BeginMenu("New...")) {
		if(ImGui::MenuItem("empty file")) {
		    text_editor_.clear();
		    current_file_ = "";
		    exec_command("require(\"preamble\")");
		}
		ImGui::Separator();
		ImGui::MenuItem("From template...", nullptr, false, false);
		embedded_files_menu("templates/");
		ImGui::EndMenu();
	    }
	    if(ImGui::BeginMenu("Load example...")) {
		embedded_files_menu("examples/");		
		ImGui::EndMenu();
	    }
	    if(ImGui::BeginMenu("Load game...")) {
		embedded_files_menu("games/");		
		ImGui::EndMenu();
	    }
	    /*
	    if(ImGui::BeginMenu("Load Shift and Tab\'s adventure...")) {
		embedded_files_menu("book/");		
		ImGui::EndMenu();
	    }
	    */
	    if(ImGui::BeginMenu("Load internal lib...")) {
		ImGui::MenuItem("These files are those", nullptr, false, false);
		ImGui::MenuItem(
		    "that are read by require(\"...\")",
		    nullptr, false, false
	        );
		ImGui::MenuItem("You cannot modify them",
				nullptr, false, false);
		ImGui::MenuItem("(but you can take a look)",
				nullptr, false, false);
		ImGui::Separator();
		embedded_files_menu("lib/");		
		ImGui::EndMenu();
	    }
	    if(ImGui::MenuItem(
		   "Run program",
		   phone_screen_ ? nullptr : "[F5]"
	    )) {
		run_program();
	    }
	}

	void run_program() {
	    exec_command("require(\"preamble\")");	    
	    if(exec_command(text_editor_.text().c_str())) {
		Logger::out("LUA") << "Program is OK." << std::endl;
	    } else {
		adjust_lua_glup_state(lua_state_);		
	    }
	    if(!lua_error_occured_ && init_graphics_called_) {
		exec_command("GLUP.init_graphics()");
	    }
	    start_animation();
	}
	
        /**
         * \brief Draws the application menus.
         * \details This function overloads 
         *  Application::draw_application_menus(). It can be used to create
         *  additional menus in the main menu bar.
         */
	void draw_application_menus() override {
	    if(!lua_error_occured_) {
		exec_command("imgui.draw_application_menus()");
	    }
	}

	/**
	 * \copydoc Application::load()
	 */
	bool load(const std::string& filename) override {
	    text_editor_.load(filename);
	    run_program();
	    current_file_ = filename;
	    return true;
	}

	/**
	 * \copydoc Application::save()
	 */
	bool save(const std::string& filename) override {
	    text_editor_.save(filename);
	    current_file_ = filename;
	    return true;
	}

	/**
	 * \copydoc Application::supported_read_file_extensions()
	 */
	std::string supported_read_file_extensions() override {
	    return "lua";
	}

	/**
	 * \copydoc Application::supported_write_file_extensions()
	 */
	std::string supported_write_file_extensions() override {
	    return "lua";
	}

	/**
	 * \copydoc SimpleApplication::key_callback
	 */
	void key_callback(
	    int key, int scancode, int action, int mods
	) override {
	    SimpleApplication::key_callback(key, scancode, action, mods);
	    const char* k_str = key_to_string(key);
	    // Get string from printable character
	    // (note: with GLFW, this does not interpret the key mapping,
	    //  so Q and A are swapped on a French keyboard...)
	    if(*k_str == '\0') {
		static char buff[2];
		k_str = buff;
		buff[0] = char(key);
		buff[1] = '\0';
	    }
	    if(action == 1) {
		on_key_pressed(k_str);
	    } else if(action == 0) {
		on_key_released(k_str);
	    }
	}
	
	bool on_key_pressed(const char* c) {
	    if(!strcmp(c,"F5")) {
		run_program();
	    } else if(!strcmp(c,"F2") && current_file_ != "") {
		if(save(current_file_)) {
		    Logger::out("I/O")
			<< "Saved " << current_file_ << std::endl;
		}
	    } else {
		exec_command(
		    (
			std::string("imgui.on_key_pressed(\"") + c + "\")"
			).c_str()
		    );
	    }
	    return true;
	}

	bool on_key_released(const char* c) {
	    exec_command(
		(
		    std::string("imgui.on_key_released(\"") + c + "\")"
		).c_str()
	    );
	    return true;
	}
	
    protected:
	void draw_viewer_properties() override {
	    if(ImGui::Button(
		   "run",
		   ImVec2(-ImGui::GetContentRegionAvail().x/2.0f,0.0f))
	    ) {
		run_program();
	    }
	    ImGui::SameLine();
	    if(ImGui::Button("edit", ImVec2(-1.0f, 0.0f))) {
                text_editor_visible_ = !text_editor_visible_;
	    }
            ImGui::Separator();
	    SimpleApplication::draw_viewer_properties();
	}

    private:
	bool init_graphics_called_;
	bool text_editor_was_visible_;
    };
      
}

int main(int argc, char** argv) {
    GeoCodApplication app;
    app.start(argc, argv);
    return 0;
}
