/*
 * This file is part of modulo.
 *
 * Copyright (C) 2020-2021 Federico Terzi
 *
 * modulo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * modulo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with modulo.  If not, see <https://www.gnu.org/licenses/>.
 */

#define _UNICODE

#include "../common/common.h"
#include "../interop/interop.h"
#include "./wizard_gui.h"

#include <memory>
#include <unordered_map>
#include <vector>

const int WELCOME_PAGE_INDEX = 0;
const int MOVE_BUNDLE_PAGE_INDEX = WELCOME_PAGE_INDEX + 1;
const int LEGACY_VERSION_PAGE_INDEX = MOVE_BUNDLE_PAGE_INDEX + 1;
const int WRONG_EDITION_PAGE_INDEX = LEGACY_VERSION_PAGE_INDEX + 1;
const int MIGRATE_PAGE_INDEX = WRONG_EDITION_PAGE_INDEX + 1;
const int AUTO_START_PAGE_INDEX = MIGRATE_PAGE_INDEX + 1;
const int ADD_PATH_PAGE_INDEX = AUTO_START_PAGE_INDEX + 1;
const int ACCESSIBILITY_PAGE_INDEX = ADD_PATH_PAGE_INDEX + 1;
const int MAX_PAGE_INDEX =
    ACCESSIBILITY_PAGE_INDEX + 1; // Update if a new page is added at the end

WizardMetadata *wizard_metadata = nullptr;
int completed_successfully = 0;

// App Code

class WizardApp : public wxApp {
  public:
    virtual bool OnInit();
};

int find_next_page(int current_index) {
    int next_index = current_index + 1;
    if (next_index >= MAX_PAGE_INDEX) {
        return -1;
    }

    switch (next_index) {
    case WELCOME_PAGE_INDEX:
        if (wizard_metadata->is_welcome_page_enabled) {
            return WELCOME_PAGE_INDEX;
        }
    case MOVE_BUNDLE_PAGE_INDEX:
        if (wizard_metadata->is_move_bundle_page_enabled) {
            return MOVE_BUNDLE_PAGE_INDEX;
        }
    case LEGACY_VERSION_PAGE_INDEX:
        if (wizard_metadata->is_legacy_version_page_enabled) {
            return LEGACY_VERSION_PAGE_INDEX;
        }
    case WRONG_EDITION_PAGE_INDEX:
        if (wizard_metadata->is_wrong_edition_page_enabled) {
            return WRONG_EDITION_PAGE_INDEX;
        }
    case MIGRATE_PAGE_INDEX:
        if (wizard_metadata->is_migrate_page_enabled) {
            return MIGRATE_PAGE_INDEX;
        }
    case AUTO_START_PAGE_INDEX:
        if (wizard_metadata->is_auto_start_page_enabled) {
            return AUTO_START_PAGE_INDEX;
        }
    case ADD_PATH_PAGE_INDEX:
        if (wizard_metadata->is_add_path_page_enabled) {
            return ADD_PATH_PAGE_INDEX;
        }
    case ACCESSIBILITY_PAGE_INDEX:
        if (wizard_metadata->is_accessibility_page_enabled) {
            return ACCESSIBILITY_PAGE_INDEX;
        }
    }

    return find_next_page(next_index);
}

class DerivedFrame : public WizardFrame {
  protected:
    void check_timer_tick(wxTimerEvent &event);
    void on_page_changed(wxBookCtrlEvent &event);
    void welcome_start_clicked(wxCommandEvent &event);
    void migrate_button_clicked(wxCommandEvent &event);
    void migrate_compatibility_mode_clicked(wxCommandEvent &event);
    void auto_start_continue_clicked(wxCommandEvent &event);
    void add_path_continue_clicked(wxCommandEvent &event);
    void accessibility_enable_clicked(wxCommandEvent &event);
    void quit_espanso_clicked(wxCommandEvent &event);
    void move_bundle_quit_clicked(wxCommandEvent &event);

    void navigate_to_next_page_or_close();
    void change_default_button(int target_page);

  public:
    DerivedFrame(wxWindow *parent);
};

DerivedFrame::DerivedFrame(wxWindow *parent) : WizardFrame(parent) {
    // Welcome images

    if (wizard_metadata->welcome_image_path) {
        wxBitmap welcomeBitmap =
            wxBitmap(wxString::FromUTF8(wizard_metadata->welcome_image_path),
                     wxBITMAP_TYPE_PNG);
        this->welcome_image->SetBitmap(welcomeBitmap);
    }

    this->welcome_version_text->SetLabel(
        wxString::Format("( version %s )", wizard_metadata->version));

    // Accessiblity images

    if (wizard_metadata->accessibility_image_1_path) {
        wxBitmap accessiblityImage1 = wxBitmap(
            wxString::FromUTF8(wizard_metadata->accessibility_image_1_path),
            wxBITMAP_TYPE_PNG);
        this->accessibility_image1->SetBitmap(accessiblityImage1);
    }
    if (wizard_metadata->accessibility_image_2_path) {
        wxBitmap accessiblityImage2 = wxBitmap(
            wxString::FromUTF8(wizard_metadata->accessibility_image_2_path),
            wxBITMAP_TYPE_PNG);
        this->accessibility_image2->SetBitmap(accessiblityImage2);
    }

    // Wrong edition
    if (wizard_metadata->is_wrong_edition_page_enabled) {
        if (wizard_metadata->detected_os == DETECTED_OS_X11) {
            this->wrong_edition_description_x11->Hide();
        }
        if (wizard_metadata->detected_os == DETECTED_OS_WAYLAND) {
            this->wrong_edition_description_wayland->Hide();
        }
    }

    // Load the first page
    int page = find_next_page(-1);
    if (page >= 0) {
        this->m_simplebook->SetSelection(page);
        this->change_default_button(page);
    } else {
        Close(true);
    }
}

void DerivedFrame::navigate_to_next_page_or_close() {
    int current_page = this->m_simplebook->GetSelection();
    int page = find_next_page(current_page);
    if (page >= 0) {
        this->m_simplebook->SetSelection(page);
    } else {
        if (wizard_metadata->on_completed) {
            wizard_metadata->on_completed();
            completed_successfully = 1;
        }

        Close(true);
    }
}

void DerivedFrame::welcome_start_clicked(wxCommandEvent &event) {
    this->navigate_to_next_page_or_close();
}

void DerivedFrame::migrate_compatibility_mode_clicked(wxCommandEvent &event) {
    this->navigate_to_next_page_or_close();
}

void DerivedFrame::migrate_button_clicked(wxCommandEvent &event) {
    if (wizard_metadata->backup_and_migrate) {
        int result = wizard_metadata->backup_and_migrate();
        if (result == MIGRATE_RESULT_SUCCESS) {
            this->navigate_to_next_page_or_close();
        } else if (result == MIGRATE_RESULT_CLEAN_FAILURE) {
            wxMessageBox(
                wxT("An error occurred during the migration, but your old "
                    "files were not modified.\n\nPlease run 'espanso log' in a "
                    "terminal for more information."),
                wxT("Migration error"), wxICON_ERROR);
        } else if (result == MIGRATE_RESULT_DIRTY_FAILURE) {
            wxMessageBox(
                wxT("An error occurred during the migration and espanso "
                    "couldn't complete the process. Some configuration files "
                    "might be missing, but you'll find the backup in the "
                    "Documents folder.\n\nPlease run 'espanso log' in a "
                    "terminal for more information."),
                wxT("Migration error"), wxICON_ERROR);
        } else if (result == MIGRATE_RESULT_UNKNOWN_FAILURE) {
            wxMessageBox(
                wxT("An error occurred during the migration.\n\nPlease run "
                    "'espanso log' in a terminal for more information."),
                wxT("Migration error"), wxICON_ERROR);
        }
    }
}

void DerivedFrame::auto_start_continue_clicked(wxCommandEvent &event) {
    if (!auto_start_checkbox->IsChecked()) {
        if (wizard_metadata->auto_start) {
            wizard_metadata->auto_start(0);
        }
        this->navigate_to_next_page_or_close();
        return;
    }

    if (wizard_metadata->auto_start) {
        while (true) {
            int result = wizard_metadata->auto_start(1);
            if (result == 1) {
                this->navigate_to_next_page_or_close();
                return;
            } else {
                wxMessageDialog *dialog = new wxMessageDialog(
                    this,
                    "An error occurred while registering Espanso as a service, "
                    "please check the logs for more information.\nDo you want "
                    "to retry? You can always configure this option later",
                    "Operation failed",
                    wxCENTER | wxOK_DEFAULT | wxOK | wxCANCEL |
                        wxICON_EXCLAMATION);

                dialog->SetOKLabel("Retry");

                int prompt_result = dialog->ShowModal();
                if (prompt_result == wxID_CANCEL) {
                    this->navigate_to_next_page_or_close();
                    break;
                }
            }
        }
    }
}

void DerivedFrame::add_path_continue_clicked(wxCommandEvent &event) {
    if (!add_path_checkbox->IsChecked()) {
        this->navigate_to_next_page_or_close();
        return;
    }

    if (wizard_metadata->add_to_path) {
        while (true) {
            int result = wizard_metadata->add_to_path();
            if (result == 1) {
                this->navigate_to_next_page_or_close();
                return;
            } else {
                wxMessageDialog *dialog = new wxMessageDialog(
                    this,
                    "An error occurred while registering the 'espanso' command "
                    "to the PATH, please check the logs for more "
                    "information.\nDo you want to retry? You can always add "
                    "espanso to the PATH later",
                    "Operation failed",
                    wxCENTER | wxOK_DEFAULT | wxOK | wxCANCEL |
                        wxICON_EXCLAMATION);

                dialog->SetOKLabel("Retry");

                int prompt_result = dialog->ShowModal();
                if (prompt_result == wxID_CANCEL) {
                    this->navigate_to_next_page_or_close();
                    break;
                }
            }
        }
    }
}

void DerivedFrame::accessibility_enable_clicked(wxCommandEvent &event) {
    if (wizard_metadata->enable_accessibility) {
        wizard_metadata->enable_accessibility();
    }
}

void DerivedFrame::quit_espanso_clicked(wxCommandEvent &event) { Close(true); }

void DerivedFrame::check_timer_tick(wxTimerEvent &event) {
    if (this->m_simplebook->GetSelection() == LEGACY_VERSION_PAGE_INDEX) {
        if (wizard_metadata->is_legacy_version_running) {
            if (wizard_metadata->is_legacy_version_running() == 0) {
                this->navigate_to_next_page_or_close();
            }
        }
    } else if (this->m_simplebook->GetSelection() == ACCESSIBILITY_PAGE_INDEX) {
        if (wizard_metadata->is_accessibility_enabled) {
            if (wizard_metadata->is_accessibility_enabled() == 1) {
                this->navigate_to_next_page_or_close();
            }
        }
    }
}

void DerivedFrame::on_page_changed(wxBookCtrlEvent &event) {
    int current_page = this->m_simplebook->GetSelection();
    this->change_default_button(current_page);
}

void DerivedFrame::change_default_button(int target_page) {
    switch (target_page) {
    case WELCOME_PAGE_INDEX: {
        this->welcome_start_button->SetDefault();
        break;
    }
    case MOVE_BUNDLE_PAGE_INDEX: {
        this->move_bundle_quit_button->SetDefault();
        break;
    }
    case MIGRATE_PAGE_INDEX: {
        this->migrate_backup_and_migrate_button->SetDefault();
        break;
    }
    case ADD_PATH_PAGE_INDEX: {
        this->add_path_continue_button->SetDefault();
        break;
    }
    case ACCESSIBILITY_PAGE_INDEX: {
        this->accessibility_enable_button->SetDefault();
        break;
    }
    }
}

void DerivedFrame::move_bundle_quit_clicked(wxCommandEvent &event) {
    Close(true);
}

bool WizardApp::OnInit() {
    wxInitAllImageHandlers();
    DerivedFrame *frame = new DerivedFrame(NULL);

    if (wizard_metadata->window_icon_path) {
        setFrameIcon(wxString::FromUTF8(wizard_metadata->window_icon_path),
                     frame);
    }

    frame->Show(true);

    Activate(frame);

    return true;
}

extern "C" int interop_show_wizard(WizardMetadata *_metadata) {
// Setup high DPI support on Windows
#ifdef __WXMSW__
    SetProcessDPIAware();
#endif

    wizard_metadata = _metadata;

    wxApp::SetInstance(new WizardApp());
    int argc = 0;
    wxEntry(argc, (char **)nullptr);

    return completed_successfully;
}