/*
4coder_fusion.cpp - 4coder major mode
*/

// TOP

#include "4coder_default_include.cpp"

////////////////////////////////

// NOTE(allen): Users can declare their own managed IDs here.
CUSTOM_ID(command_map, fusion_map_command);
CUSTOM_ID(command_map, fusion_map_insert);

////////////////////////////////

#if !defined(META_PASS)
#include "generated/managed_id_metadata.cpp"
#endif

////////////////////////////////

typedef i32 Fusion_Mode;
enum{
    FusionMode_Command,
    FusionMode_Insert,
};

Fusion_Mode fusion_mode = FusionMode_Command;

////////////////////////////////

function void
fusion_set_mode(Fusion_Mode mode){
    local_persist ARGB_Color col_margin = finalize_color(defcolor_margin, 0);
    local_persist ARGB_Color col_margin_hover = finalize_color(defcolor_margin_hover, 0);
    local_persist ARGB_Color col_margin_active = finalize_color(defcolor_margin_active, 0);
    local_persist ARGB_Color col_back = finalize_color(defcolor_back, 0);
    
    fusion_mode = mode;
    switch (mode){
        case FusionMode_Command:
        {
            col_margin = finalize_color(defcolor_margin, 0);
            col_margin_hover = finalize_color(defcolor_margin_hover, 0);
            col_margin_active = finalize_color(defcolor_margin_active, 0);
            
            global_config.highlight_line_at_cursor = true;
            global_config.mark_thickness = 2.f;
            
            set_single_active_color(defcolor_margin, col_back);
            set_single_active_color(defcolor_margin_hover, col_back);
            set_single_active_color(defcolor_margin_active, col_back);
        }break;
        
        case FusionMode_Insert:
        {
            global_config.highlight_line_at_cursor = false;
            global_config.mark_thickness = 0.f;
            
            set_single_active_color(defcolor_margin, col_margin);
            set_single_active_color(defcolor_margin_hover, col_margin_hover);
            set_single_active_color(defcolor_margin_active, col_margin_active);
        }break;
    }
}

CUSTOM_COMMAND_SIG(fusion_toggle_mode)
CUSTOM_DOC("TODO - document fusion mode")
{
    if (fusion_mode == FusionMode_Command){
        fusion_set_mode(FusionMode_Insert);
    }
    else{
        fusion_set_mode(FusionMode_Command);
    }
}

CUSTOM_COMMAND_SIG(fusion_input_handler)
CUSTOM_DOC("TODO - document fusion mode")
{
    Scratch_Block scratch(app);
    default_input_handler_init(app, scratch);
    
    View_ID view = get_this_ctx_view(app, Access_Always);
    Managed_Scope scope = view_get_managed_scope(app, view);
    
    for (;;){
        // NOTE(allen): Get input
        User_Input input = get_next_input(app, EventPropertyGroup_Any, 0);
        ProfileScopeNamed(app, "before view input", view_input_profile);
        if (input.abort){
            break;
        }
        
        // NOTE(allen): Get map_id
        Command_Map_ID map_id = fusion_map_command;
        if (fusion_mode == FusionMode_Insert){
            map_id = fusion_map_insert;
        }
        
        // NOTE(allen): Get binding
        Command_Binding binding = map_get_binding_recursive(&framework_mapping, map_id, &input.event);
        if (binding.custom == 0){
            leave_current_input_unhandled(app);
            continue;
        }
        
        // NOTE(allen): Run the command
        default_pre_command(app, scope);
        ProfileCloseNow(view_input_profile);
        
        binding.custom(app);
        
        ProfileScope(app, "after view input");
        
        default_post_command(app, scope);
    }
}

////////////////////////////////

CUSTOM_COMMAND_SIG(fusion_return)
CUSTOM_DOC("TODO")
{
    View_ID view = get_active_view(app, Access_ReadVisible);
    Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
    if (buffer == 0){
        buffer = view_get_buffer(app, view, Access_ReadVisible);
        if (buffer != 0){
            goto_jump_at_cursor(app);
            lock_jump_buffer(app, buffer);
        }
    }
    else{
        write_text(app, string_u8_litexpr("\n"));
    }
}

CUSTOM_COMMAND_SIG(fusion_return_shift)
CUSTOM_DOC("TODO")
{
    View_ID view = get_active_view(app, Access_ReadVisible);
    Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
    if (buffer == 0){
        buffer = view_get_buffer(app, view, Access_ReadVisible);
        if (buffer != 0){
            goto_jump_at_cursor_same_panel(app);
            lock_jump_buffer(app, buffer);
        }
    }
    else{
        write_text(app, string_u8_litexpr("\n"));
    }
}

function b32
string_is_blank(String_Const_u8 string){
    b32 is_blank = true;
    for (u64 i = 0; i < string.size; i += 1){
        if (!character_is_whitespace(string.str[i])){
            is_blank = false;
            break;
        }
    }
    return(is_blank);
}

CUSTOM_COMMAND_SIG(fusion_backspace)
CUSTOM_DOC("TODO")
{
    View_ID view = get_active_view(app, Access_ReadWriteVisible);
    if (view != 0){
        Scratch_Block scratch(app);
        Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWrite);
        i64 pos = view_get_cursor_pos(app, view);
        i64 line_number = get_line_number_from_pos(app, buffer, pos);
        Buffer_Cursor beginning_of_line = get_line_side(app, buffer, line_number, Side_Min);
        Range_i64 range = Ii64(beginning_of_line.pos, pos);
        String_Const_u8 string = push_buffer_range(app, scratch, buffer, range);
        if (string_is_blank(string)){
            if (line_number > 1){
                Buffer_Cursor end_of_prev_line = get_line_side(app, buffer, line_number - 1, Side_Max);
                range.min = end_of_prev_line.pos;
            }
            buffer_replace_range(app, buffer, range, string_u8_litexpr(""));
        }
        else{
            current_view_boundary_delete(app, Scan_Backward,
                                         push_boundary_list(scratch, boundary_alpha_numeric, boundary_token,
                                                            boundary_non_whitespace));
        }
    }
}

CUSTOM_COMMAND_SIG(fusion_delete)
CUSTOM_DOC("TODO")
{
    View_ID view = get_active_view(app, Access_ReadWriteVisible);
    if (view != 0){
        Scratch_Block scratch(app);
        Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWrite);
        i64 pos = view_get_cursor_pos(app, view);
        i64 line_number = get_line_number_from_pos(app, buffer, pos);
        Buffer_Cursor end_of_line = get_line_side(app, buffer, line_number, Side_Max);
        Range_i64 range = Ii64(pos, end_of_line.pos);
        String_Const_u8 string = push_buffer_range(app, scratch, buffer, range);
        if (string_is_blank(string)){
            i64 line_count = buffer_get_line_count(app, buffer);
            if (line_number < line_count){
                Buffer_Cursor beginning_of_prev_line = get_line_side(app, buffer, line_number + 1, Side_Min);
                range.max = beginning_of_prev_line.pos;
            }
            buffer_replace_range(app, buffer, range, string_u8_litexpr(""));
        }
        else{
            current_view_boundary_delete(app, Scan_Forward,
                                         push_boundary_list(scratch, boundary_alpha_numeric, boundary_token,
                                                            boundary_non_whitespace));
        }
    }
}

////////////////////////////////

function void
setup_fusion_mapping(Mapping *mapping){
    MappingScope();
    SelectMapping(mapping);
    
    SelectMap(mapid_global);
    BindCore(default_startup, CoreCode_Startup);
    BindCore(default_try_exit, CoreCode_TryExit);
    BindCore(clipboard_record_clip, CoreCode_NewClipboardContents);
    Bind(exit_4coder,          KeyCode_F4, KeyCode_Alt);
    Bind(project_fkey_command, KeyCode_F1);
    Bind(project_fkey_command, KeyCode_F2);
    Bind(project_fkey_command, KeyCode_F3);
    Bind(project_fkey_command, KeyCode_F4);
    Bind(project_fkey_command, KeyCode_F5);
    Bind(project_fkey_command, KeyCode_F6);
    Bind(project_fkey_command, KeyCode_F7);
    Bind(project_fkey_command, KeyCode_F8);
    Bind(project_fkey_command, KeyCode_F9);
    Bind(project_fkey_command, KeyCode_F10);
    Bind(project_fkey_command, KeyCode_F11);
    Bind(project_fkey_command, KeyCode_F12);
    Bind(project_fkey_command, KeyCode_F13);
    Bind(project_fkey_command, KeyCode_F14);
    Bind(project_fkey_command, KeyCode_F15);
    Bind(project_fkey_command, KeyCode_F16);
    Bind(fusion_toggle_mode, KeyCode_Insert);
    Bind(fusion_toggle_mode, KeyCode_Escape);
    
    BindMouseWheel(mouse_wheel_scroll);
    BindMouseWheel(mouse_wheel_change_face_size, KeyCode_Control);
    BindMouse(click_set_cursor_and_mark, MouseCode_Left);
    BindMouseRelease(click_set_cursor, MouseCode_Left);
    BindCore(click_set_cursor_and_mark, CoreCode_ClickActivateView);
    BindMouseMove(click_set_cursor_if_lbutton);
    
    SelectMap(fusion_map_command);
    ParentMap(mapid_global);
    Bind(set_mark,                        KeyCode_Space);
    
    Bind(move_up,                         KeyCode_Up);
    Bind(move_down,                       KeyCode_Down);
    Bind(move_line_up,                    KeyCode_Up, KeyCode_Shift);
    Bind(move_line_down,                  KeyCode_Down, KeyCode_Shift);
    Bind(move_left_alpha_numeric_boundary, KeyCode_Left);
    Bind(move_right_alpha_numeric_boundary, KeyCode_Right);
    Bind(move_left_alpha_numeric_or_camel_boundary,  KeyCode_Left, KeyCode_Shift);
    Bind(move_right_alpha_numeric_or_camel_boundary, KeyCode_Right, KeyCode_Shift);
    Bind(seek_end_of_line,                KeyCode_End);
    Bind(seek_beginning_of_line,          KeyCode_Home);
    Bind(page_up,                         KeyCode_PageUp);
    Bind(page_down,                       KeyCode_PageDown);
    
    Bind(fusion_backspace, KeyCode_Backspace);
    Bind(fusion_delete,    KeyCode_Delete);
    Bind(snipe_backward_whitespace_or_token_boundary, KeyCode_Backspace, KeyCode_Shift);
    Bind(snipe_forward_whitespace_or_token_boundary,  KeyCode_Delete, KeyCode_Shift);
    
    Bind(fusion_return,            KeyCode_Return);
    Bind(fusion_return_shift,      KeyCode_Return, KeyCode_Shift);
    
    Bind(change_active_panel,             KeyCode_Comma);
    Bind(change_active_panel_backwards,   KeyCode_Comma, KeyCode_Shift);
    Bind(change_to_build_panel,           KeyCode_Period);
    Bind(close_build_panel,               KeyCode_Period, KeyCode_Shift);
    
    Bind(comment_line_toggle,             KeyCode_Semicolon);
    
    Bind(keyboard_macro_start_recording , KeyCode_Control);
    Bind(keyboard_macro_finish_recording, KeyCode_Alt);
    Bind(keyboard_macro_replay,           KeyCode_U);
    Bind(interactive_open_or_new,         KeyCode_O);
    Bind(interactive_switch_buffer,       KeyCode_I);
    Bind(goto_next_jump,                  KeyCode_N);
    Bind(goto_prev_jump,                  KeyCode_N, KeyCode_Shift);
    Bind(goto_first_jump,                 KeyCode_M);
    Bind(command_lister,                  KeyCode_X);
    Bind(jump_to_last_point,              KeyCode_P);
    Bind(replace_in_range,                KeyCode_A);
    Bind(copy,                            KeyCode_C);
    Bind(paste_next_and_indent,           KeyCode_V);
    Bind(delete_range,                    KeyCode_D);
    Bind(delete_line,                     KeyCode_D, KeyCode_Shift);
    Bind(center_view,                     KeyCode_E);
    Bind(search,                          KeyCode_F);
    Bind(list_all_substring_locations_case_insensitive, KeyCode_S);
    Bind(goto_line,                       KeyCode_G);
    Bind(snippet_lister,                  KeyCode_J);
    Bind(kill_buffer,                     KeyCode_K, KeyCode_Shift);
    Bind(duplicate_line,                  KeyCode_L);
    Bind(query_replace,                   KeyCode_Q);
    Bind(query_replace_identifier,        KeyCode_Q, KeyCode_Shift);
    Bind(list_all_locations_of_identifier, KeyCode_T);
    Bind(redo,                            KeyCode_Y);
    Bind(undo,                            KeyCode_Z);
    Bind(jump_to_definition_at_cursor,    KeyCode_W);
    
    Bind(view_buffer_other_panel,     KeyCode_1);
    Bind(swap_panels,                 KeyCode_2);
    Bind(open_matching_file_cpp,      KeyCode_3);
    
    Bind(write_zero_struct,          KeyCode_0);
    Bind(open_long_braces,           KeyCode_LeftBracket);
    Bind(open_long_braces_semicolon, KeyCode_LeftBracket, KeyCode_Shift);
    Bind(open_long_braces_break,     KeyCode_RightBracket, KeyCode_Shift);
    
    Bind(select_surrounding_scope,   KeyCode_RightBracket);
    Bind(select_prev_top_most_scope, KeyCode_Quote);
    Bind(select_next_scope_after_current, KeyCode_ForwardSlash);
    Bind(place_in_scope,             KeyCode_ForwardSlash, KeyCode_Shift);
    Bind(delete_current_scope,       KeyCode_Minus);
    Bind(if0_off,                    KeyCode_9, KeyCode_Alt);
    
    SelectMap(fusion_map_insert);
    ParentMap(mapid_global);
    BindTextInput(write_text_and_auto_indent);
    Bind(move_up,                KeyCode_Up);
    Bind(move_down,              KeyCode_Down);
    Bind(move_left,              KeyCode_Left);
    Bind(move_right,             KeyCode_Right);
    Bind(delete_char,            KeyCode_Delete);
    Bind(backspace_char,         KeyCode_Backspace);
    Bind(seek_end_of_line,       KeyCode_End);
    Bind(seek_beginning_of_line, KeyCode_Home);
    Bind(move_up_to_blank_line_end,   KeyCode_PageUp);
    Bind(move_down_to_blank_line_end, KeyCode_PageDown);
    Bind(word_complete,          KeyCode_Tab);
}

void
custom_layer_init(Application_Links *app){
    Thread_Context *tctx = get_thread_context(app);
    
    // NOTE(allen): setup for default framework
    default_framework_init(app);
    
    // NOTE(allen): default hooks and command maps
    set_all_default_hooks(app);
    set_custom_hook(app, HookID_ViewEventHandler, fusion_input_handler);
    
    mapping_init(tctx, &framework_mapping);
    setup_fusion_mapping(&framework_mapping);
}

// BOTTOM