#pragma once

#include "log.h"
#include "shader.h"
#include "shape.h"
#include "texture.h"
#include "layout.h"
#include "font.h"
#include "rtt.h"
#include "node_message_box.h"
#include "node_settings.h"
#include "node_popup_menu.h"
#include "node_panel_brush.h"
#include "node_panel_layer.h"
#include "node_panel_color.h"
#include "node_panel_stroke.h"
#include "node_scroll.h"
#include "node_canvas.h"
#include "node_dialog_layer_rename.h"
#include "node_progress_bar.h"

#if defined(__OBJC__) && defined(__IOS__)
#import <Foundation/Foundation.h>
#import "GameViewController.h"
#endif

#if defined(__OBJC__) && defined(__OSX__)
#import "main.h"
#endif

#ifdef __ANDROID__
#include "main.h"
#endif
#include "node_panel_grid.h"

class App
{
public:
    static App I;
    std::string data_path{ "." };
    std::string work_path{ "." };
    std::string rec_path{ "." };
    std::thread rec_thread;
    bool rec_running = false;
    int rec_count = 0;
    std::mutex rec_mutex;
    std::condition_variable rec_cv;
    std::deque<std::unique_ptr<uint8_t[]>> rec_frames;
    
    RTT uirtt;
    Sampler sampler;
    Sampler sampler_stencil;
    Sampler sampler_linear;
    Plane m_face_plane;
    //Sphere sphere;
    LayoutManager layout;
    NodeMessageBox* msgbox;
    NodeSettings* settings;
    NodePopupMenu* menu_file = nullptr;
    NodePopupMenu* menu_edit = nullptr;
    NodePopupMenu* menu_layers = nullptr;
    NodeBorder* sidebar = nullptr;
    std::shared_ptr<NodePanelLayer> layers;
    std::shared_ptr<NodePanelColor> color;
    std::shared_ptr<NodePanelStroke> stroke;
    std::shared_ptr<NodePanelGrid> grid;
    NodeCanvas* canvas;
    Node* current_panel = nullptr;
    NodeScroll* panels;
    const uint16_t main_id = const_hash("main");
    const std::array<int, 6> res_map{ 512, 1024, 1536, 2048, 4096, 8192 };
    const std::array<std::string, 6> res_map_str{ "2K", "4K", "6K", "8K", "16K", "32K" };
    std::string doc_name = "no-name";
    std::string doc_path;
    std::string doc_dir;
    std::string doc_filename;
    bool has_stylus = false;
    bool has_vr = false;
    float off_x = 0;
    float off_y = 0;
    float width;
    float height;
    bool keys[256];
    bool redraw = true;
    bool animate = false;
    bool ui_visible = true;
    bool vr_active = false;
    glm::mat4 vr_controller;
    glm::vec3 vr_controller_pos;
    float vr_pressure = 1.f;
    glm::mat4 vr_rot{ 0 };
    glm::mat4 vr_uirot{ 0 };
    glm::vec2 cursor{ 0, 0 };
    glm::vec2 gesture_p0;
    glm::vec2 gesture_p1;
#ifdef __ANDROID__
    float zoom = 3.0;
#elif __IOS__
    float zoom = 2.0;
#else
    float zoom = 1.0;
#endif // __ANDROID__
    
#if defined(__IOS__) && defined(__OBJC__)
    GameViewController* ios_view;
#endif

#if defined(__OSX__) && defined(__OBJC__)
    View* osx_view;
#endif

#ifdef __ANDROID__
    struct android_app* and_app;
    struct engine* and_engine;
#endif
    void pick_image(std::function<void(std::string path)> callback);
    void pick_file(std::vector<std::string> types, std::function<void(std::string path)> callback);
    void pick_dir(std::function<void(std::string path)> callback);
    void display_file(std::string path);
    void share_file(std::string path);
    void showKeyboard();
    void hideKeyboard();
    void initLog();
    void init();
    void initShaders();
    void initAssets();
    void initLayout();
    void open_document(std::string path);
    void create();
    bool request_close();
    void terminate();
    void clear();
    void tick(float dt);
    void update(float dt);
    void vr_draw(const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose);
    void async_start();
    void async_update();
    void async_redraw();
    void async_end();
    void resize(float w, float h);
    bool mouse_down(int button, float x, float y, float pressure, kEventSource source, bool eraser);
    bool mouse_move(float x, float y, float pressure, kEventSource source, bool eraser);
    bool mouse_up(int button, float x, float y, kEventSource source, bool eraser);
    bool mouse_scroll(float x, float y, float delta);
    bool mouse_cancel(int button);
    bool gesture_start(const glm::vec2& p0, const glm::vec2& p1);
    bool gesture_move(const glm::vec2& p0, const glm::vec2& p1);
    bool gesture_end();
    bool key_down(kKey key);
    bool key_up(kKey key);
    bool key_char(char key);
    void toggle_ui();
    void set_stylus();
    void message_box(const std::string& title, const std::string& text);

    void rec_clear();
    void rec_loop();
    void rec_start();
    void rec_stop();
    void rec_export(std::string path);
    
    void init_toolbar_main();
    void init_toolbar_draw();
    void init_sidebar();
    void init_menu_file();
    void init_menu_edit();
    void init_menu_layer();
    void init_menu_timelapse();
    void init_menu_about();
    void dialog_usermanual();
    void dialog_changelog();
    void dialog_about();
    void dialog_newdoc();
    void dialog_save();
    void dialog_save_ver();
    void dialog_open();
    void dialog_browse();
    void dialog_export(std::string ext);
    void dialog_export_layers();
	void dialog_export_cubes();
    void dialog_layer_rename();
    void dialog_resize();

    void cloud_upload();
    void cloud_upload_all();
    void cloud_browse();
    void upload(std::string filename, std::string name = "", std::function<void(float)> progress = nullptr);
    void download(std::string url, std::string dest_filepath, std::function<void(float)> progress = nullptr);
    bool check_license();
    
    std::shared_ptr<NodeProgressBar> show_progress(const std::string& title);

    void brush_update();
    void title_update();
    void update_memory_usage(size_t bytes);
    void update_rec_frames();
    int res_from_index(int i);
    int res_to_index(int res);
    std::string res_to_string(int res);
    void crash_test();

    void cmd_convert(std::string pano_path, std::string out_path);
};
