/*
4coder_default_framework.cpp - Sets up the basics of the framework that is used for default 
4coder behaviour.
*/

// TOP

static void
unlock_jump_buffer(void){
    locked_buffer.size = 0;
}

static void
lock_jump_buffer(char *name, i32 size){
    if (size <= locked_buffer.memory_size){
        copy(&locked_buffer, make_string(name, size));
    }
}

static void
lock_jump_buffer(Buffer_Summary buffer){
    lock_jump_buffer(buffer.buffer_name, buffer.buffer_name_len);
}

static void
lock_jump_buffer(Application_Links *app, Buffer_ID buffer_id){
    Buffer_Summary buffer = {};
    if (get_buffer_summary(app, buffer_id, AccessAll, &buffer)){
        lock_jump_buffer(buffer.buffer_name, buffer.buffer_name_len);
    }
}

static View_Summary
get_view_for_locked_jump_buffer(Application_Links *app){
    View_Summary view = {};
    if (locked_buffer.size > 0){
        Buffer_Summary buffer = get_buffer_by_name(app, locked_buffer.str, locked_buffer.size, AccessAll);
        if (buffer.exists){
            view = get_first_view_with_buffer(app, buffer.buffer_id);
        }
        else{
            unlock_jump_buffer();
        }
    }
    return(view);
}

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

static void
new_view_settings(Application_Links *app, View_Summary *view){
    if (!global_config.use_scroll_bars){
        view_set_setting(app, view, ViewSetting_ShowScrollbar, false);
    }
    if (!global_config.use_file_bars){
        view_set_setting(app, view, ViewSetting_ShowFileBar, false);
    }
}

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

static void
view_set_passive(Application_Links *app, View_ID view_id, b32 value){
    Managed_Scope scope = view_get_managed_scope(app, view_id);
    managed_variable_set(app, scope, view_is_passive_loc, (u64)value);
}

static b32
view_get_is_passive(Application_Links *app, View_ID view_id){
    Managed_Scope scope = view_get_managed_scope(app, view_id);
    u64 is_passive = 0;
    managed_variable_get(app, scope, view_is_passive_loc, &is_passive);
    return(is_passive != 0);
}

static View_Summary
open_footer_panel(Application_Links *app, View_Summary *view){
    View_Summary special_view = open_view(app, view, ViewSplit_Bottom);
    new_view_settings(app, &special_view);
    view_set_split_pixel_size(app, &special_view, (i32)(special_view.line_height*20.f));
    view_set_passive(app, special_view.view_id, true);
    return(special_view);
}

static void
close_build_footer_panel(Application_Links *app){
    View_Summary special_view = get_view(app, build_footer_panel_view_id, AccessAll);
    if (special_view.exists){
        close_view(app, &special_view);
    }
    build_footer_panel_view_id = 0;
}

static b32
open_build_footer_panel(Application_Links *app, View_ID *view_id_out){
    View_Summary special_view = {};
    get_view_summary(app, build_footer_panel_view_id, AccessAll, &special_view);
    if (!special_view.exists){
        View_Summary view = get_active_view(app, AccessAll);
        special_view = open_footer_panel(app, &view);
        set_active_view(app, &view);
        build_footer_panel_view_id = special_view.view_id;
    }
    *view_id_out = build_footer_panel_view_id;
    return(true);
}

static View_ID
get_next_view_looped_primary_panels(Application_Links *app, View_ID start_view_id, Access_Flag access){
    View_ID view_id = start_view_id;
    do{
        view_id = get_next_view_looped_all_panels(app, view_id, access);
        if (!view_get_is_passive(app, view_id)){
            break;
        }
    }while(view_id != start_view_id);
    return(view_id);
}

static View_ID
get_prev_view_looped_primary_panels(Application_Links *app, View_ID start_view_id, Access_Flag access){
    View_ID view_id = start_view_id;
    do{
        view_id = get_prev_view_looped_all_panels(app, view_id, access);
        if (!view_get_is_passive(app, view_id)){
            break;
        }
    }while(view_id != start_view_id);
    return(view_id);
}

////

static void
view_set_passive(Application_Links *app, View_Summary *view, b32 value){
    if (view != 0){
        view_set_passive(app, view->view_id, value);
    }
}

static b32
view_get_is_passive(Application_Links *app, View_Summary *view){
    return(view != 0 && view_get_is_passive(app, view->view_id));
}

static View_Summary
open_build_footer_panel(Application_Links *app){
    View_Summary summary = {};
    View_ID build_footer_id = 0;
    if (open_build_footer_panel(app, &build_footer_id)){
        get_view_summary(app, build_footer_id, AccessAll, &summary);
    }
    return(summary);
}

static void
get_next_view_looped_primary_panels(Application_Links *app, View_Summary *view_start, Access_Flag access){
    View_ID new_id = get_next_view_looped_primary_panels(app, view_start->view_id, access);
    get_view_summary(app, new_id, AccessAll, view_start);
}

static void
get_prev_view_looped_primary_panels(Application_Links *app, View_Summary *view_start, Access_Flag access){
    View_ID new_id = get_prev_view_looped_primary_panels(app, view_start->view_id, access);
    get_view_summary(app, new_id, AccessAll, view_start);
}

static View_Summary
get_next_view_after_active(Application_Links *app, u32 access){
    View_Summary view = get_active_view(app, access);
    if (view.exists){
        get_next_view_looped_primary_panels(app, &view, access);
    }
    return(view);
}

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

static void
view_buffer_set(Application_Links *app, Buffer_ID *buffers, i32 *positions, i32 count){
    if (count > 0){
        Arena *arena = context_get_arena(app);
        Temp_Memory_Arena temp = begin_temp_memory(arena);
        
        struct View_Node{
            View_Node *next;
            View_ID view_id;
        };
        
        View_ID active_view_id = 0;
        get_active_view(app, AccessAll, &active_view_id);
        View_ID first_view_id = active_view_id;
        if (view_get_is_passive(app, active_view_id)){
            first_view_id = get_next_view_looped_primary_panels(app, active_view_id, AccessAll);
        }
        
        View_ID view_id = first_view_id;
        
        View_Node *primary_view_first = 0;
        View_Node *primary_view_last = 0;
        i32 available_view_count = 0;
        
        primary_view_first = primary_view_last = push_array(arena, View_Node, 1);
        primary_view_last->next = 0;
        primary_view_last->view_id = view_id;
        available_view_count += 1;
        for (;;){
            view_id = get_next_view_looped_primary_panels(app, view_id, AccessAll);
            if (view_id == first_view_id){
                break;
            }
            View_Node *node = push_array(arena, View_Node, 1);
            primary_view_last->next = node;
            node->next = 0;
            node->view_id = view_id;
            primary_view_last = node;
            available_view_count += 1;
        }
        
        i32 buffer_set_count = clamp_top(count, available_view_count);
        View_Node *node = primary_view_first;
        for (i32 i = 0; i < buffer_set_count; i += 1, node = node->next){
            if (view_set_buffer(app, node->view_id, buffers[i], 0)){
                view_set_cursor(app, node->view_id, seek_pos(positions[i]), true);
            }
        }
        
        end_temp_memory(temp);
    }
}

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

CUSTOM_COMMAND_SIG(change_active_panel)
CUSTOM_DOC("Change the currently active panel, moving to the panel with the next highest view_id.")
{
    View_Summary view = get_active_view(app, AccessAll);
    get_next_view_looped_primary_panels(app, &view, AccessAll);
    if (view.exists){
        set_active_view(app, &view);
    }
}

CUSTOM_COMMAND_SIG(change_active_panel_backwards)
CUSTOM_DOC("Change the currently active panel, moving to the panel with the next lowest view_id.")
{
    View_Summary view = get_active_view(app, AccessAll);
    get_prev_view_looped_primary_panels(app, &view, AccessAll);
    if (view.exists){
        set_active_view(app, &view);
    }
}

CUSTOM_COMMAND_SIG(open_panel_vsplit)
CUSTOM_DOC("Create a new panel by vertically splitting the active panel.")
{
    View_Summary view = get_active_view(app, AccessAll);
    View_Summary new_view = open_view(app, &view, ViewSplit_Right);
    new_view_settings(app, &new_view);
    view_set_buffer(app, &new_view, view.buffer_id, 0);
}

CUSTOM_COMMAND_SIG(open_panel_hsplit)
CUSTOM_DOC("Create a new panel by horizontally splitting the active panel.")
{
    View_Summary view = get_active_view(app, AccessAll);
    View_Summary new_view = open_view(app, &view, ViewSplit_Bottom);
    new_view_settings(app, &new_view);
    view_set_buffer(app, &new_view, view.buffer_id, 0);
}

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

// NOTE(allen): Credits to nj/FlyingSolomon for authoring the original version of this helper.

static Buffer_ID
create_or_switch_to_buffer_by_name(Application_Links *app, char *name, i32 name_length, View_Summary default_target_view){
    u32 access = AccessAll;
    Buffer_Summary search_buffer = get_buffer_by_name(app, name, name_length, access);
    
    if (search_buffer.exists){
        buffer_set_setting(app, &search_buffer, BufferSetting_ReadOnly, true);
        buffer_set_edit_handler(app, search_buffer.buffer_id, 0);
        
        View_Summary target_view = default_target_view;
        
        View_Summary view_with_buffer_already_open = get_first_view_with_buffer(app, search_buffer.buffer_id);
        if (view_with_buffer_already_open.exists){
            target_view = view_with_buffer_already_open;
            view_end_ui_mode(app, &target_view);
        }
        else{
            view_set_buffer(app, &target_view, search_buffer.buffer_id, 0);
        }
        set_active_view(app, &target_view);
        
        buffer_send_end_signal(app, &search_buffer);
        buffer_replace_range(app, &search_buffer, 0, search_buffer.size, 0, 0);
    }
    else{
        search_buffer = create_buffer(app, name, name_length, BufferCreate_AlwaysNew);
        buffer_set_setting(app, &search_buffer, BufferSetting_Unimportant, true);
        buffer_set_setting(app, &search_buffer, BufferSetting_ReadOnly, true);
        buffer_set_setting(app, &search_buffer, BufferSetting_WrapLine, false);
        view_set_buffer(app, &default_target_view, search_buffer.buffer_id, 0);
        set_active_view(app, &default_target_view);
    }
    
    return(search_buffer.buffer_id);
}

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

static void
set_mouse_suppression(Application_Links *app, b32 suppress){
    if (suppress){
        suppressing_mouse = true;
        show_mouse_cursor(app, MouseCursorShow_Never);
    }
    else{
        suppressing_mouse = false;
        show_mouse_cursor(app, MouseCursorShow_Always);
    }
}

CUSTOM_COMMAND_SIG(suppress_mouse)
CUSTOM_DOC("Hides the mouse and causes all mosue input (clicks, position, wheel) to be ignored.")
{
    set_mouse_suppression(app, true);
}

CUSTOM_COMMAND_SIG(allow_mouse)
CUSTOM_DOC("Shows the mouse and causes all mouse input to be processed normally.")
{
    set_mouse_suppression(app, false);
}

CUSTOM_COMMAND_SIG(toggle_mouse)
CUSTOM_DOC("Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.")
{
    set_mouse_suppression(app, !suppressing_mouse);
}

CUSTOM_COMMAND_SIG(set_mode_to_original)
CUSTOM_DOC("Sets the edit mode to 4coder original.")
{
    fcoder_mode = FCoderMode_Original;
}

CUSTOM_COMMAND_SIG(set_mode_to_notepad_like)
CUSTOM_DOC("Sets the edit mode to Notepad like.")
{
    begin_notepad_mode(app);
}

CUSTOM_COMMAND_SIG(toggle_highlight_line_at_cursor)
CUSTOM_DOC("Toggles the line highlight at the cursor.")
{
    highlight_line_at_cursor = !highlight_line_at_cursor;
}

CUSTOM_COMMAND_SIG(toggle_highlight_enclosing_scopes)
CUSTOM_DOC("In code files scopes surrounding the cursor are highlighted with distinguishing colors.")
{
    do_matching_enclosure_highlight = !do_matching_enclosure_highlight;
}

CUSTOM_COMMAND_SIG(toggle_paren_matching_helper)
CUSTOM_DOC("In code files matching parentheses pairs are colored with distinguishing colors.")
{
    do_matching_paren_highlight = !do_matching_paren_highlight;
}

CUSTOM_COMMAND_SIG(toggle_fullscreen)
CUSTOM_DOC("Toggle fullscreen mode on or off.  The change(s) do not take effect until the next frame.")
{
    set_fullscreen(app, !is_fullscreen(app));
}

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

CUSTOM_COMMAND_SIG(remap_interactive)
CUSTOM_DOC("Switch to a named key binding map.")
{
    Query_Bar bar = {};
    char space[1024];
    bar.prompt = make_lit_string("Map Name: ");
    bar.string = make_fixed_width_string(space);
    if (!query_user_string(app, &bar)) return;
    change_mapping(app, bar.string);
}

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

static void
default_4coder_initialize(Application_Links *app, char **command_line_files, i32 file_count, i32 override_font_size, b32 override_hinting){
    i32 part_size = (32 << 20);
    void *part_mem = memory_allocate(app, part_size);
    global_part = make_part(part_mem, part_size);
    
    i32 heap_size = (4 << 20);
    void *heap_mem = memory_allocate(app, heap_size);
    heap_init(&global_heap);
    heap_extend(&global_heap, heap_mem, heap_size);
    
    static char message[] =
        "Welcome to " VERSION "\n"
        "If you're new to 4coder there are some tutorials at http://4coder.net/tutorials.html\n"
        "Direct bug reports and feature requests to https://github.com/4coder-editor/4coder/issues\n"
        "Other questions and discussion can be directed to editor@4coder.net or 4coder.handmade.network\n"
        "The change log can be found in CHANGES.txt\n"
        "\n";
    print_message(app, message, sizeof(message) - 1);
    
#if 0
    load_folder_of_themes_into_live_set(app, &global_part, "themes");
#endif
    load_config_and_apply(app, &global_part, &global_config, override_font_size, override_hinting);
    
    view_rewrite_loc         = managed_variable_create_or_get_id(app, "DEFAULT.rewrite"       , 0);
    view_next_rewrite_loc    = managed_variable_create_or_get_id(app, "DEFAULT.next_rewrite"  , 0);
    view_paste_index_loc     = managed_variable_create_or_get_id(app, "DEFAULT.paste_index"   , 0);
    view_is_passive_loc      = managed_variable_create_or_get_id(app, "DEFAULT.is_passive"    , 0);
    view_snap_mark_to_cursor = managed_variable_create_or_get_id(app, "DEFAULT.mark_to_cursor", 0);
    view_ui_data             = managed_variable_create_or_get_id(app, "DEFAULT.ui_data"       , 0);
    
    // open command line files
    Temp_Memory temp = begin_temp_memory(&global_part);
    char *space = push_array(&global_part, char, (32 << 10));
    String file_name = make_string_cap(space, 0, (32 << 10));
    i32 hot_directory_length = 0;
    if (get_hot_directory(app, &file_name, &hot_directory_length)){
        for (i32 i = 0; i < file_count; i += 1){
            String input_name = make_string_slowly(command_line_files[i]);
            file_name.size = hot_directory_length;
            append(&file_name, input_name);
            Buffer_ID ignore = 0;
            if (!create_buffer(app, file_name, BufferCreate_NeverNew|BufferCreate_MustAttachToFile, &ignore)){
                create_buffer(app, input_name, 0, &ignore);
            }
        }
    }
    
    end_temp_memory(temp);
}

static void
default_4coder_initialize(Application_Links *app, i32 override_font_size, b32 override_hinting){
    default_4coder_initialize(app, 0, 0, override_font_size, override_hinting);
}

static void
default_4coder_initialize(Application_Links *app, char **command_line_files, i32 file_count){
    Face_Description command_line_description = get_face_description(app, 0);
    default_4coder_initialize(app, command_line_files, file_count, command_line_description.pt_size, command_line_description.hinting);
}

static void
default_4coder_initialize(Application_Links *app){
    Face_Description command_line_description = get_face_description(app, 0);
    default_4coder_initialize(app, 0, 0, command_line_description.pt_size, command_line_description.hinting);
}

static void
default_4coder_side_by_side_panels(Application_Links *app, Buffer_Identifier left_buffer, Buffer_Identifier right_buffer){
    Buffer_ID left_id = buffer_identifier_to_id(app, left_buffer);
    Buffer_ID right_id = buffer_identifier_to_id(app, right_buffer);
    
    // Left Panel
    View_Summary view = get_active_view(app, AccessAll);
    new_view_settings(app, &view);
    view_set_buffer(app, &view, left_id, 0);
    
    // Right Panel
    open_panel_vsplit(app);
    View_Summary right_view = get_active_view(app, AccessAll);
    view_set_buffer(app, &right_view, right_id, 0);
    
    // Restore Active to Left
    set_active_view(app, &view);
}

static void
default_4coder_side_by_side_panels(Application_Links *app, char **command_line_files, i32 file_count){
    Buffer_Identifier left = buffer_identifier(literal("*scratch*"));
    Buffer_Identifier right = buffer_identifier(literal("*messages*"));
    
    if (file_count > 0){
        char *left_name = command_line_files[0];
        i32 left_len = str_size(left_name);
        left = buffer_identifier(left_name, left_len);
        
        if (file_count > 1){
            char *right_name = command_line_files[1];
            i32 right_len = str_size(right_name);
            right = buffer_identifier(right_name, right_len);
        }
    }
    
    default_4coder_side_by_side_panels(app, left, right);
}

static void
default_4coder_side_by_side_panels(Application_Links *app){
    default_4coder_side_by_side_panels(app, 0, 0);
}

static void
default_4coder_one_panel(Application_Links *app, Buffer_Identifier buffer){
    Buffer_ID id = buffer_identifier_to_id(app, buffer);
    
    View_Summary view = get_active_view(app, AccessAll);
    new_view_settings(app, &view);
    view_set_buffer(app, &view, id, 0);
}

static void
default_4coder_one_panel(Application_Links *app, char **command_line_files, i32 file_count){
    Buffer_Identifier buffer = buffer_identifier(literal("*messages*"));
    
    if (file_count > 0){
        char *name = command_line_files[0];
        i32 len = str_size(name);
        buffer = buffer_identifier(name, len);
    }
    
    default_4coder_one_panel(app, buffer);
}

static void
default_4coder_one_panel(Application_Links *app){
    default_4coder_one_panel(app, 0, 0);
}

// BOTTOM