/*
4coder_default_bidings.cpp - Supplies the default bindings used for default 4coder behavior.
*/

// TOP

#if !defined(FCODER_DEFAULT_BINDINGS_CPP)
#define FCODER_DEFAULT_BINDINGS_CPP

#include "4coder_default_include.cpp"

// NOTE(allen): Users can declare their own managed IDs here.

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

#define EXTERNAL_KEYBOARD 0
#if OS_MAC && !EXTERNAL_KEYBOARD
global u32 key_alt = KeyCode_Command;
#else
global u32 key_alt = KeyCode_Alt;
#endif

function String_Const_u8
get_lexeme_under_cursor(Application_Links* app, View_ID view, Buffer_ID buffer, Arena* arena)
{
  String_Const_u8 lexeme = {0};
  i64 pos = view_get_cursor_pos(app, view);
  Token* token = get_token_from_pos(app, buffer, pos);
  if (token != 0) {
    lexeme = push_token_lexeme(app, arena, buffer, token);
  }
  return lexeme;
}


function void
go_to_definition(Application_Links* app, String_Const_u8 lexeme, View_ID view)
{
  Code_Index_Note* note = 0;
  
  // if we're trying to go to the definition of the same lexeme as last time
  // then there are probably a typedef + declaration in different locations so
  // we want to advance to the next code index note that matches this lexeme
  // and then loop
  if (string_match(go_to_definition_last_lexeme, lexeme))
  {
    Code_Index_Note_List* list = code_index__list_from_string(lexeme);
    u64 i = 0;
    for (Code_Index_Note *it = list->first;
      it != 0;
      it = it->next_in_hash, i++){
      if (string_match(lexeme, it->text) && i > go_to_definition_last_lexeme_index){
        note = it;
        go_to_definition_last_lexeme_index = i;
        break;
      }
    }
  }
  
  if (!note)
  {
    note = code_index_note_from_string(lexeme);
    go_to_definition_last_lexeme = lexeme;
    go_to_definition_last_lexeme_index = 0;
  }
  if (note == 0) return;
  
  Buffer_ID buffer = note->file->buffer;
  view_set_buffer(app, view, buffer, 0);
  
  switch (note->note_kind)
  {
    case CodeIndexNote_Type:
    case CodeIndexNote_Function:
    case CodeIndexNote_Macro:
    {
      jump_to_location(app, view, note->file->buffer, note->pos.start);
    } break;
    
    default: {} break;
  }
}

CUSTOM_COMMAND_SIG(cmd_enter_behavior)
{
  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{
    Scratch_Block scratch(app);
    String_Const_u8 lexeme = get_lexeme_under_cursor(app, view, buffer, scratch);
    if (lexeme.size > 0) {
      go_to_definition(app, lexeme, view);
    }
  }
}

CUSTOM_COMMAND_SIG(cmd_alt_enter_behavior)
{
  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{
    Scratch_Block scratch(app);
    String_Const_u8 lexeme = get_lexeme_under_cursor(app, view, buffer, scratch);
    if (lexeme.size > 0) {
      view = get_next_view_looped_primary_panels(app, view, Access_Always);
      go_to_definition(app, lexeme, view);
    }
  }
}


function void
bindings_cmd_misc(Mapping* m, Command_Map* map)
{
  Bind(command_lister, KeyCode_W);
  Bind(change_active_panel, KeyCode_E);
  Bind(toggle_compilation_view, KeyCode_Minus);
}

function void
bindings_cmd_file_ops(Mapping* m, Command_Map* map)
{
  Bind(set_mark, KeyCode_Space);
  Bind(interactive_open_or_new, KeyCode_Comma);
  Bind(interactive_switch_buffer, KeyCode_Period);
  Bind(save, KeyCode_Semicolon);
}

function void
bindings_cmd_search(Mapping* m, Command_Map* map)
{
  Bind(query_replace, KeyCode_S);
  Bind(search, KeyCode_F);
  Bind(list_all_locations_of_identifier, KeyCode_D);
  Bind(list_all_substring_locations_case_insensitive, KeyCode_D, key_alt);
  Bind(goto_next_jump, KeyCode_T);
  Bind(goto_prev_jump, KeyCode_R);
  
  // Listers
  Bind(lister_search_all, KeyCode_1);
}

function void
bindings_cmd_nav(Mapping* m, Command_Map* map)
{
  Bind(seek_beginning_of_line, KeyCode_Y);
  Bind(seek_end_of_line, KeyCode_P);
  Bind(move_left_token_boundary, KeyCode_U);
  Bind(move_right_token_boundary, KeyCode_O);
  
  Bind(move_up, KeyCode_I);
  Bind(move_left, KeyCode_J);
  Bind(move_down, KeyCode_K);
  Bind(move_right, KeyCode_L);
  
  
  Bind(move_up_to_blank_line_end, KeyCode_H);
  Bind(move_down_to_blank_line_end, KeyCode_N);
  
  Bind(cmd_enter_behavior, KeyCode_Return);
  Bind(cmd_alt_enter_behavior, KeyCode_Return, key_alt);
  Bind(jump_to_last_point, KeyCode_Semicolon, KeyCode_Control);
}

function void
custom_keyboard_bindings()
{
  modal_set_cursor_color_u32(modal_mode_input, 0xFF00FF00);
  modal_set_cursor_color_u32(modal_mode_cmd,   0xFFFF0000);
  modal_set_cursor_color_u32(modal_mode_debug, 0xFF00F0FF);
  
  MappingScope();
  
  // Global commands
  modal_bind_all(modal_map_id_global, modal_set_mode_toggle, KeyCode_F, key_alt, 0);
  modal_bind_all(modal_map_id_global, modal_set_mode_next, KeyCode_F, KeyCode_Control, 0);
  modal_bind_all(modal_map_id_global, exit_4coder, KeyCode_F4, key_alt, 0);
  
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F1, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F2, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F3, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F4, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F5, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F6, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F7, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F8, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F9, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F10, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F11, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F12, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F13, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F14, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F15, 0, 0);
  modal_bind_all(modal_map_id_global, project_fkey_command, KeyCode_F16, 0, 0);
  
  SelectMapping(&modal_get_mode(modal_mode_cmd)->map);
  SelectMap(modal_map_id_global);
  {
    bindings_cmd_file_ops(m, map);
    bindings_cmd_misc(m, map);
    bindings_cmd_search(m, map);
    bindings_cmd_nav(m, map);
    
    // Text Editing
    Bind(delete_to_end_of_line, KeyCode_A);
    Bind(undo, KeyCode_Z);
    Bind(redo, KeyCode_B);
    Bind(copy, KeyCode_C);
    Bind(paste, KeyCode_V);
    Bind(cut, KeyCode_X);
    Bind(backspace_char, KeyCode_Backspace);
    Bind(backspace_alpha_numeric_or_camel_boundary, KeyCode_Backspace, key_alt);
    Bind(backspace_token_boundary, KeyCode_Backspace, KeyCode_Control);
    Bind(unindent_range, KeyCode_Tab, KeyCode_Shift);
    Bind(indent_range, KeyCode_Tab);
    
    // Macros
    Bind(keyboard_macro_start_recording, KeyCode_1, key_alt);
    Bind(keyboard_macro_finish_recording, KeyCode_2, key_alt);
    Bind(keyboard_macro_replay, KeyCode_3, key_alt);
  }
  
  SelectMapping(&modal_get_mode(modal_mode_input)->map);
  SelectMap(modal_map_id_global);
  {
    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);
    
    Bind(delete_char,            KeyCode_Delete);
    Bind(backspace_char,      KeyCode_Backspace);
    Bind(backspace_alpha_numeric_or_camel_boundary, KeyCode_Backspace, key_alt);
    Bind(backspace_token_boundary, KeyCode_Backspace, KeyCode_Control);
    
    Bind(move_up,                KeyCode_I, key_alt);
    Bind(move_down,              KeyCode_K, key_alt);
    
    BindTextInput(write_text_and_auto_indent);
    Bind(indent_or_autocomplete, KeyCode_Tab);
    Bind(unindent_line, KeyCode_Tab, KeyCode_Shift);
    Bind(write_todo,                 KeyCode_T, key_alt);
    Bind(write_note,                 KeyCode_G, key_alt);
    
    Bind(input_enter_behavior, KeyCode_Return);
    Bind(input_alt_enter_behavior, KeyCode_Return, key_alt);
  }
  
  SelectMapping(&modal_get_mode(modal_mode_debug)->map);
  SelectMap(modal_map_id_global);
  {
    bindings_cmd_file_ops(m, map);
    bindings_cmd_misc(m, map);
    bindings_cmd_search(m, map);
    bindings_cmd_nav(m, map);
  }
}

void
custom_layer_init(Application_Links *app){
    Thread_Context *tctx = get_thread_context(app);
    default_framework_init(app);
    
    set_all_default_hooks(app);
    modal_init(3, tctx);
    
    custom_keyboard_bindings();
    
    #if 0
    mapping_init(tctx, &framework_mapping);
    String_ID global_map_id = vars_save_string_lit("keys_global");
    String_ID file_map_id = vars_save_string_lit("keys_file");
    String_ID code_map_id = vars_save_string_lit("keys_code");
#if OS_MAC
    setup_mac_mapping(&framework_mapping, global_map_id, file_map_id, code_map_id);
#else
    setup_default_mapping(&framework_mapping, global_map_id, file_map_id, code_map_id);
#endif
	setup_essential_mapping(&framework_mapping, global_map_id, file_map_id, code_map_id);
  #endif
  
}

#endif //FCODER_DEFAULT_BINDINGS

// BOTTOM