// ============================================================= //
//                                                               //
//   File      : macro_gui.cxx                                   //
//   Purpose   :                                                 //
//                                                               //
//   Coded by Ralf Westram (coder@reallysoft.de) in May 2005     //
//   Institute of Microbiology (Technical University Munich)     //
//   http://www.arb-home.de/                                     //
//                                                               //
// ============================================================= //

#include "macros.hxx"
#include "macros_local.hxx"
#include "trackers.hxx"

#include <arbdb.h>
#include <arb_file.h>
#include <arb_match.h>

#include <aw_window.hxx>
#include <aw_edit.hxx>
#include <aw_file.hxx>
#include <aw_awar.hxx>
#include <aw_msg.hxx>
#include <arbdbt.h>
#include <ad_cb.h>

#define ma_assert(bed) arb_assert(bed)

#define AWAR_MACRO_RECORDING_MACRO_TEXT AWAR_MACRO_BASE "/button_label"
#define AWAR_MACRO_RECORDING_EXPAND     AWAR_MACRO_BASE "/expand"
#define AWAR_MACRO_RECORDING_RUNB4      AWAR_MACRO_BASE "/runb4"
#define AWAR_MACRO_INTERRUPT_PROMPT     AWAR_MACRO_BASE "/" NEVER_RECORDED_ID "/prompt"

static void delete_macro_cb(AW_window *aww) {
    AW_root *awr       = aww->get_root();
    char    *macroName = AW_get_selected_fullname(awr, AWAR_MACRO_BASE);

    if (GB_unlink(macroName)<0) aw_message(GB_await_error());
    else AW_refresh_fileselection(awr, AWAR_MACRO_BASE);

    free(macroName);
}

static void macro_execution_finished(AW_root *awr, char *macroName) {
#if defined(DEBUG)
    fprintf(stderr, "macro_execution_finished(%s)\n", macroName);
#endif

    AW_set_selected_fullname(awr, AWAR_MACRO_BASE, macroName); // reset selected macro (needed if macro calls other macro(s))

    free(macroName);
}

static void exec_macro_cb(AW_window *aww, bool loop_marked) {
    AW_root  *awr       = aww->get_root();
    char     *macroName = AW_get_selected_fullname(awr, AWAR_MACRO_BASE); // @@@ instead use function returning plain name w/o dir
    GB_ERROR  error     = getMacroRecorder(awr)->execute(macroName, loop_marked, makeRootCallback(macro_execution_finished, macroName));
    if (error) {
        aw_message(error);
        free(macroName); // only free in error-case (see macro_execution_finished)
    }
}

inline void update_macro_record_button(AW_root *awr) {
    awr->awar(AWAR_MACRO_RECORDING_MACRO_TEXT)->write_string(awr->is_tracking() ? "STOP" : "RECORD");
}

static void start_macro_cb(AW_window *aww) {
    AW_root  *awr   = aww->get_root();
    GB_ERROR  error = NULp;

    if (awr->is_tracking()) {
        error = getMacroRecorder(awr)->stop_recording();
    }
    else {
        bool expand = awr->awar(AWAR_MACRO_RECORDING_EXPAND)->read_int();
        bool runb4  = expand && awr->awar(AWAR_MACRO_RECORDING_RUNB4)->read_int();

        char *macroName = AW_get_selected_fullname(awr, AWAR_MACRO_BASE);
        if (GB_is_directory(macroName)) {
            error = "Please specify name of macro to record";
        }
        else {
            if (runb4) exec_macro_cb(aww, false);

            char *sac = aww->local_id_copy(MACRO_RECORD_ID);
            error = getMacroRecorder(awr)->start_recording(macroName, sac, expand);
            free(sac);
        }
        free(macroName);
    }

    AW_refresh_fileselection(awr, AWAR_MACRO_BASE);
    update_macro_record_button(awr);
    if (error) aw_message(error);
}

static void insert_interruption_to_macro_cb(AW_window *aww) {
    AW_root  *awr   = aww->get_root();
    GB_ERROR  error = NULp;

    if (!awr->is_tracking()) {
        error = "Only possible while recording macro.";
    }
    else {
        const char *text  = awr->awar(AWAR_MACRO_INTERRUPT_PROMPT)->read_char_pntr();
        char       *info  = GBS_regreplace(text, "/\\s* //", &error);

        if (!error) {
            if (info[0] == 0) {
                error = "Please insert some text as user prompt";
            }
            else {
                getMacroRecorder(awr)->add_planned_interruption(text);
            }
        }

        free(info);
    }

    aw_message_if(error);
}

static void popup_interrupt_macro_window_cb(AW_window *aww) {
    AW_root *awr = aww->get_root();
    if (!awr->is_tracking()) {
        aw_message("Note: this will only work while recording a macro!");
    }

    static AW_window_simple *aws = NULp;
    if (!aws) {
        aws = new AW_window_simple;

        aws->init(awr, "DEFINE_INTERRUPTION", "Define macro interruption");
        aws->load_xfig("macro_interruption.fig");

        aws->at("close"); aws->callback(AW_POPDOWN);
        aws->create_button(NEVER_RECORDED_ID "CLOSE", "CLOSE", "C");

        aws->at("help"); aws->callback(makeHelpCallback("macro_interruption.hlp"));
        aws->create_button(NEVER_RECORDED_ID "HELP", "HELP", "H");

        aws->at("box");
        aws->create_text_field(AWAR_MACRO_INTERRUPT_PROMPT);

        aws->at("add");
        aws->callback(insert_interruption_to_macro_cb);
        aws->create_autosize_button(NEVER_RECORDED_ID "Add", ".. and interrupt playback until user presses 'continue'");
    }

    aws->activate();
}

static void edit_macro_cb(AW_window *aww) {
    char *path = AW_get_selected_fullname(aww->get_root(), AWAR_MACRO_BASE);
    AW_edit(path);
    free(path);
}

static void macro_recording_changed_cb() {
    update_macro_record_button(AW_root::SINGLETON);
}

static void create_macro_variables(AW_root *aw_root) {
    AW_create_fileselection_awars(aw_root, AWAR_MACRO_BASE, ".", ".amc", "");
    aw_root->awar_string(AWAR_MACRO_RECORDING_MACRO_TEXT, "RECORD");
    aw_root->awar_int(AWAR_MACRO_RECORDING_EXPAND, 0);
    aw_root->awar_int(AWAR_MACRO_RECORDING_RUNB4, 0);
    aw_root->awar_string(AWAR_MACRO_INTERRUPT_PROMPT, "");

    {
        UserActionTracker *tracker       = aw_root->get_tracker();
        MacroRecorder     *macroRecorder = dynamic_cast<MacroRecorder*>(tracker);

        GBDATA   *gb_main = macroRecorder->get_gbmain();
        GB_ERROR  error   = NULp;

        GB_transaction ta(gb_main);

        GBDATA *gb_recording     = GB_searchOrCreate_int(gb_main, MACRO_TRIGGER_RECORDING, 0);
        if (!gb_recording) error = GB_await_error();
        else    error            = GB_add_callback(gb_recording, GB_CB_CHANGED, makeDatabaseCallback(macro_recording_changed_cb));

        if (error) aw_message(GBS_global_string("Failed to bind macro_recording_changed_cb (Reason: %s)", error));
    }

    update_macro_record_button(aw_root);
}

static void popup_macro_window(AW_window *aww) {
    static AW_window_simple *aws = NULp;
    if (!aws) {
        AW_root *aw_root = aww->get_root();

        aws = new AW_window_simple;
        aws->init(aw_root, MACRO_WINDOW_ID, "MACROS");
        aws->load_xfig("macro_select.fig");

        create_macro_variables(aw_root);

        aws->at("close"); aws->callback(AW_POPDOWN);
        aws->create_button("CLOSE", "CLOSE", "C");

        aws->at("help"); aws->callback(makeHelpCallback("macro.hlp"));
        aws->create_button("HELP", "HELP");

        aws->at("record"); aws->callback(start_macro_cb);
        aws->create_button(MACRO_RECORD_ID, AWAR_MACRO_RECORDING_MACRO_TEXT);

        aws->at("expand"); aws->create_toggle(AWAR_MACRO_RECORDING_EXPAND);
        aws->at("runb4");  aws->create_toggle(AWAR_MACRO_RECORDING_RUNB4);

        aws->at("interrupt"); aws->callback(popup_interrupt_macro_window_cb);
        aws->create_button(NEVER_RECORDED_ID, "Interrupt");

        aws->at("edit");   aws->callback(edit_macro_cb);   aws->create_button("EDIT",   "EDIT");
        aws->at("delete"); aws->callback(delete_macro_cb); aws->create_button("DELETE", "DELETE");

        aws->at("exec");
        aws->callback(makeWindowCallback(exec_macro_cb, false));
        aws->create_button(MACRO_PLAYBACK_ID, "EXECUTE");

        aws->at("execWith");
        aws->callback(makeWindowCallback(exec_macro_cb, true));
        aws->create_autosize_button(MACRO_PLAYBACK_MARKED_ID, "Execute with each marked species");

        AW_create_fileselection(aws, AWAR_MACRO_BASE, "", "ARBMACROHOME^ARBMACRO", ANY_DIR, false);
    }
    aws->activate();
}

void insert_macro_menu_entry(AW_window *awm, bool prepend_separator) {
    if (getMacroRecorder(awm->get_root())) {
        if (prepend_separator) awm->sep______________();
        awm->insert_menu_topic("macros", "Macros ", "M", "macro.hlp", AWM_ALL, popup_macro_window);
    }
}

static void dont_announce_done(AW_root*) {}

void execute_macro(AW_root *root, const char *macroname) {
    // used to execute macro passed via CLI
    GB_ERROR error = NULp;
    {
        // @@@ allow macro playback from client? (using server via AWAR)
        MacroRecorder *recorder = getMacroRecorder(root);
        if (!recorder) error    = "macro playback only available in server";
        else           error    = recorder->execute(macroname, false, makeRootCallback(dont_announce_done));
    }

    if (error) {
        aw_message(GBS_global_string("Can't execute macro '%s' (Reason: %s)", macroname, error));
    }
}

static bool is_executing_macro_INTERNAL(AW_root *root, bool ignore_client) {
    // return true, if the arb server is currently executing.a macro
    // (works from server and client)
    //
    // if 'ignore_client'==true -> always returns false in clients.

    bool                executing_macro = false;
    BoundActionTracker *tracker         = get_active_macro_recording_tracker(root);

    if (tracker) {
        MacroRecorder *recorder = dynamic_cast<MacroRecorder*>(tracker);

        if (recorder) {
            executing_macro = recorder->is_executing_macro();
        }
        else if (!ignore_client) {
            GBDATA         *gb_main = tracker->get_gbmain();
            GB_transaction  ta(gb_main);
            GBDATA         *gb_exec = GB_search(gb_main, MACRO_TRIGGER_EXECUTING, GB_FIND);

            if (gb_exec) {
                long value      = GB_read_int(gb_exec);
                executing_macro = value != 0;
            }
        }
    }

    return executing_macro;
}

bool is_executing_macro_as_server(AW_root *root) {
    // return true, if current application is not a client AND a macro is executing.
    return is_executing_macro_INTERNAL(root, true);
}

bool is_executing_macro(AW_root *root) {
    // return true, if the arb server is currently executing.a macro
    // (works from server and client)
    //
    // Note: use AW_root::is_tracking to check for active macro recording
    return is_executing_macro_INTERNAL(root, false);
}
