#include "pch.h"
#include "app.h"

#ifdef __ANDROID__
void displayKeyboard(android_app* mApplication, bool pShow);
void android_pick_file(android_app* mApplication, std::function<void(std::string)> callback);
#elif _WIN32
std::string win32_open_file(const char* filter);
std::string win32_open_dir();
#endif

void App::crash_test()
{
#ifdef __IOS__
    [ios_view crash];
#elif __OSX__
    [osx_view hockeyapp_crash];
#elif defined(_WIN32)
    __debugbreak();
#elif defined(__ANDROID__)
#endif
}

void App::tick(float dt)
{
    layout[main_id]->tick(dt);
}

void App::resize(float w, float h)
{
    uirtt.create(w, h);
    redraw = true;
    width = w;
    height = h;
    if (auto* main = layout[main_id])
        main->update(w , h, zoom);
}

void App::showKeyboard()
{
    LOG("show keyboard");
    redraw = true;
#ifdef __IOS__
    [ios_view registerForKeyboardNotifications];
    [ios_view becomeFirstResponder];
#elif __ANDROID__
    displayKeyboard(and_app, true);
#endif
}

void App::hideKeyboard()
{
    LOG("hide keyboard");
    redraw = true;
#ifdef __IOS__
    [ios_view resignFirstResponder];
//    [ios_view unregisterForKeyboardNotifications];
#elif __ANDROID__
    displayKeyboard(and_app, false);
#endif
}

void App::pick_image(std::function<void(std::string path)> callback)
{
    redraw = true;
#ifdef __IOS__
    [ios_view pick_photo:callback];
#elif __OSX__
    dispatch_async(dispatch_get_main_queue(), ^{
        NSArray* fileTypes = [NSArray arrayWithObjects:@"png", @"PNG", @"jpg", @"JPG", @"jpeg", nil];
        std::string path = [osx_view pick_file:fileTypes];
        if (!path.empty())
            callback(path);
    });
#elif __ANDROID__
    android_pick_file(and_app, callback);
#elif _WIN32
    std::string path = win32_open_file("Image Files (*.jpg, *.png)\0*.jpg;*.png");
    if (!path.empty())
        callback(path);
#endif
}

void App::pick_file(std::vector<std::string> types, std::function<void (std::string)> callback)
{
    redraw = true;
#ifdef __IOS__
    // not implemented on ios
    // fallback to pick_image
    [ios_view pick_photo:callback];
#elif __OSX__
    dispatch_async(dispatch_get_main_queue(), ^{
        //NSArray* fileTypes = [NSArray arrayWithObjects:@"ppi", @"PPI", nil];
        NSMutableArray<NSString*>* fileTypes = [NSMutableArray arrayWithCapacity:types.size()];
        for (const auto& t : types)
            [fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]];
        std::string path = [osx_view pick_file:fileTypes];
        if (!path.empty())
            callback(path);
    });
#elif __ANDROID__
    android_pick_file(and_app, callback);
#elif _WIN32
    std::string filter = "Supported Files (";
    bool first_type = true;
    for (auto& t : types)
    {
        filter.append(std::string(first_type ? "" : " ,") + "*." + t);
        first_type = false;
    }
    filter.append(")");
    filter.push_back(0);
    first_type = true;
    for (auto& t : types)
    {
        filter.append(std::string(first_type ? "" : ";") + "*." + t);
        first_type = false;
    }
    filter.push_back(0);
    std::string path = win32_open_file(filter.c_str());
    if (!path.empty())
        callback(path);
#endif
}

void App::pick_dir(std::function<void(std::string path)> callback)
{
    redraw = true;
#ifdef __IOS__
    // NOT IMPLEMENTED
#elif __OSX__
    dispatch_async(dispatch_get_main_queue(), ^{
        std::string path = [osx_view pick_dir];
        if (!path.empty())
            callback(path);
    });
#elif __ANDROID__
    // NOT IMPLEMENTED
#elif _WIN32
    // TODO: to be implemented
   std::string path = win32_open_dir();
   if (!path.empty())
       callback(path);
#endif
}

void App::display_file(std::string path)
{
#ifdef __IOS__
    [ios_view display_file:path];
#elif __OSX__
    [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithUTF8String:path.c_str()]];
//    dispatch_async(dispatch_get_main_queue(), ^{
//        std::string path = [osx_view pick_file];
//        if (!path.empty())
//            callback(path);
//    });
#elif __ANDROID__
    //displayKeyboard(and_app, false);
#elif _WIN32
//    std::string path = win32_open_file();
//    if (!path.empty())
//        callback(path);
#endif
}

void App::share_file(std::string path)
{
    if (path.empty())
    {
        message_box("Sharing failed", "Please save the document before sharing it.");
        return;
    }
#ifdef __IOS__
    dispatch_async(dispatch_get_main_queue(), ^{
        [ios_view share_file:[NSString stringWithUTF8String:path.c_str()]];
    });
#elif __OSX__
    dispatch_async(dispatch_get_main_queue(), ^{
        [osx_view share_file:[NSString stringWithUTF8String:path.c_str()]];
    });
#elif __ANDROID__
#elif _WIN32
    // not implemented
#endif
}

bool App::mouse_down(int button, float x, float y, float pressure, kEventSource source, bool eraser)
{
    redraw = true;
    MouseEvent e;
    e.m_type = button ? kEventType::MouseDownR : kEventType::MouseDownL;
    e.m_pos = { x / zoom, y / zoom };
    e.m_pressure = pressure;
    e.m_source = source;
    e.m_eraser = eraser;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::mouse_move(float x, float y, float pressure, kEventSource source, bool eraser)
{
    cursor = { x / zoom, y / zoom };
    redraw = true;
    MouseEvent e;
    e.m_type = kEventType::MouseMove;
    e.m_pos = { x / zoom, y / zoom };
    e.m_pressure = pressure;
    e.m_source = source;
    e.m_eraser = eraser;
    kEventResult ret = kEventResult::Available;
    if (auto* main = layout[main_id])
        ret = main->on_event(&e);
    return ret == kEventResult::Consumed;
}
bool App::mouse_up(int button, float x, float y, kEventSource source, bool eraser)
{
    redraw = true;
    MouseEvent e;
    e.m_type = button ? kEventType::MouseUpR : kEventType::MouseUpL;
    e.m_pos = { x / zoom, y / zoom };
    e.m_source = source;
    e.m_eraser = eraser;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::mouse_scroll(float x, float y, float delta)
{
    redraw = true;
    MouseEvent e;
    e.m_type = kEventType::MouseScroll;
    e.m_pos = { x / zoom, y / zoom };
    e.m_scroll_delta = delta;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::mouse_cancel(int button)
{
    redraw = true;
    MouseEvent e;
    e.m_type = kEventType::MouseCancel;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::gesture_start(const glm::vec2& p0, const glm::vec2& p1)
{
    redraw = true;
    GestureEvent e;
    glm::vec2 p = glm::lerp(p0, p1, 0.5f);
    e.m_type = kEventType::GestureStart;
    e.m_pos = p / glm::vec2(zoom);
    e.m_distance = glm::distance(p0, p1);
    gesture_p0 = p0;
    gesture_p1 = p1;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::gesture_move(const glm::vec2& p0, const glm::vec2& p1)
{
    redraw = true;
    GestureEvent e;
    glm::vec2 p = glm::lerp(p0, p1, 0.5f);
    e.m_type = kEventType::GestureMove;
    e.m_pos = p / glm::vec2(zoom);
    e.m_distance = glm::distance(p0, p1);
    e.m_distance_delta = e.m_distance - glm::distance(gesture_p0, gesture_p1);
    e.m_pos_delta = p - glm::lerp(gesture_p0, gesture_p1, 0.5f);
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::gesture_end()
{
    redraw = true;
    GestureEvent e;
    e.m_type = kEventType::GestureEnd;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::key_down(kKey key)
{
    if (key == kKey::KeySpacebar && vr_active)
        canvas->m_canvas->m_cam_rot = vr_rot;
    redraw = true;
    keys[(int)key] = true;
    KeyEvent e;
    e.m_type = kEventType::KeyDown;
    e.m_key = key;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::key_up(kKey key)
{
    redraw = true;
    keys[(int)key] = false;
    KeyEvent e;
    e.m_type = kEventType::KeyUp;
    e.m_key = key;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}
bool App::key_char(char key)
{
    redraw = true;
    KeyEvent e;
    e.m_type = kEventType::KeyChar;
    e.m_char = key;
    auto ret = layout[main_id]->on_event(&e);
    layout[main_id]->update();
    return ret == kEventResult::Consumed;
}

void App::toggle_ui()
{
    auto m = layout[main_id]->m_children[1];
    ui_visible = !ui_visible;
    for (int i = 1; i < m->m_children.size(); i++)
        m->m_children[i]->m_display = ui_visible;
}

void App::set_stylus()
{
    has_stylus = true;
    if (canvas)
        canvas->m_canvas->m_touch_lock = true;
}
