#ifndef FCODER_CUSTOM_H
#define FCODER_CUSTOM_H

#include "4coder_version.h"
#include "4coder_keycodes.h"
#include "4coder_style.h"
#include "4coder_buffer_types.h"

#ifndef FRED_STRING_STRUCT
#define FRED_STRING_STRUCT
typedef struct String{
    char *str;
    int size;
    int memory_size;
} String;

typedef struct Offset_String{
    int offset;
    int size;
} Offset_String;
#endif

typedef unsigned char Code;

typedef enum{
	MDFR_SHIFT_INDEX,
	MDFR_CONTROL_INDEX,
	MDFR_ALT_INDEX,
    MDFR_CAPS_INDEX,
	// always last
	MDFR_INDEX_COUNT
} Key_Control;

typedef struct Key_Event_Data{
	Code keycode;
	Code character;
	Code character_no_caps_lock;
    
	char modifiers[MDFR_INDEX_COUNT];
} Key_Event_Data;
inline Key_Event_Data
key_event_data_zero(){
    Key_Event_Data data={0};
    return(data);
}

typedef struct Mouse_State{
	char l, r;
    char press_l, press_r;
	char release_l, release_r;
	char wheel;
	char out_of_window;
	int x, y;
} Mouse_State;


typedef union Range{
    struct{
        int min, max;
    };
    struct{
        int start, end;
    };
} Range;

inline Range
make_range(int p1, int p2){
    Range range;
    if (p1 < p2){
        range.min = p1;
        range.max = p2;
    }
    else{
        range.min = p2;
        range.max = p1;
    }
    return(range);
}


typedef enum Dynamic_Type{
    dynamic_type_int,
    dynamic_type_string,
    // never below this
    dynamic_type_count
} Dynamic_Type;

typedef struct Dynamic{
    int type;
    union{
        struct{
            int str_len;
            char *str_value;
        };
        int int_value;
    };
} Dynamic;

inline Dynamic
dynamic_int(int x){
    Dynamic result;
    result.type = dynamic_type_int;
    result.int_value = x;
    return result;
}

inline Dynamic
dynamic_string(const char *string, int len){
    Dynamic result;
    result.type = dynamic_type_string;
    result.str_len = len;
    result.str_value = (char*)(string);
    return result;
}

inline int
dynamic_to_int(Dynamic *dynamic){
    int result = 0;
    if (dynamic->type == dynamic_type_int){
        result = dynamic->int_value;
    }
    return result;
}

inline char*
dynamic_to_string(Dynamic *dynamic, int *len){
    char *result = 0;
    if (dynamic->type == dynamic_type_string){
        result = dynamic->str_value;
        *len = dynamic->str_len;
    }
    return result;
}

inline int
dynamic_to_bool(Dynamic *dynamic){
    int result = 0;
    if (dynamic->type == dynamic_type_int){
        result = (dynamic->int_value != 0);
    }
    else{
        result = 1;
    }
    return result;
}

typedef struct File_Info{
    String filename;
    int folder;
} File_Info;

typedef struct File_List{
    // Ignore this, it's for internal stuff.
    void *block;
    
    // The list of files and folders.
    File_Info *infos;
    int count;
    
    // Ignore this, it's for internal stuff.
    int block_size;
} File_List;

#define MDFR_NONE 0x0
#define MDFR_CTRL 0x1
#define MDFR_ALT 0x2
#define MDFR_SHIFT 0x4

enum Command_ID{
    cmdid_null,
    cmdid_write_character,
    cmdid_seek_left,
    cmdid_seek_right,
    cmdid_seek_whitespace_up,
    cmdid_seek_whitespace_down,
    cmdid_center_view,
    cmdid_word_complete,
    cmdid_set_mark,
    cmdid_copy,
    cmdid_cut,
    cmdid_paste,
    cmdid_paste_next,
    cmdid_delete_range,
    cmdid_undo,
    cmdid_redo,
    cmdid_history_backward,
    cmdid_history_forward,
    cmdid_interactive_new,
    cmdid_interactive_open,
    cmdid_reopen,
    cmdid_save,
    cmdid_change_active_panel,
    cmdid_interactive_switch_buffer,
    cmdid_interactive_kill_buffer,
    cmdid_kill_buffer,
    cmdid_toggle_line_wrap,
    cmdid_toggle_endline_mode,
    cmdid_to_uppercase,
    cmdid_to_lowercase,
    cmdid_toggle_show_whitespace,
    cmdid_clean_all_lines,
    cmdid_eol_dosify,
    cmdid_eol_nixify,
    cmdid_auto_tab_range,
    cmdid_open_panel_vsplit,
    cmdid_open_panel_hsplit,
    cmdid_close_panel,
    cmdid_move_left,
    cmdid_move_right,
    cmdid_delete,
    cmdid_backspace,
    cmdid_move_up,
    cmdid_move_down,
    cmdid_seek_end_of_line,
    cmdid_seek_beginning_of_line,
    cmdid_page_up,
    cmdid_page_down,
    cmdid_open_color_tweaker,
    cmdid_cursor_mark_swap,
    cmdid_open_menu,
    cmdid_set_settings,
    cmdid_command_line,
    //
    cmdid_count
};

enum Param_ID{
    par_range_start,
    par_range_end,
    par_name,
    par_buffer_id,
    par_do_in_background,
    par_flags,
    par_lex_as_cpp_file,
    par_wrap_lines,
    par_key_mapid,
    par_cli_path,
    par_cli_command,
    par_clear_blank_lines,
    par_use_tabs,
    par_save_update_name,
    
    // never below this
    par_type_count
};

#define CLI_OverlapWithConflict 0x1
#define CLI_AlwaysBindToView 0x2

// These are regular hooks, any of them can be set to any function
// that matches the HOOK_SIG pattern.
enum Hook_ID{
    hook_start,
    hook_open_file,
    hook_new_file,
    hook_file_out_of_sync,
    // never below this
    hook_type_count
};

// These are for special hooks, each must bind to specialized signatures
// that do not necessarily have access to the app pointer.
enum Special_Hook_ID{
    _hook_scroll_rule = hook_type_count,
};

// NOTE(allen): None of the members of *_Summary structs nor any of the
// data pointed to by the members should be modified, I would have made
// them all const... but that causes a lot problems for C++ reasons.
struct Buffer_Summary{
    int exists;
    int ready;
    int buffer_id;
    
    int size;
    
    int file_name_len;
    int buffer_name_len;
    char *file_name;
    char *buffer_name;
    
    int buffer_cursor_pos;
    int is_lexed;
    int map_id;
};
inline Buffer_Summary
buffer_summary_zero(){
    Buffer_Summary summary={0};
    return(summary);
}

struct View_Summary{
    int exists;
    int view_id;
    int buffer_id;
    int locked_buffer_id;
    int hidden_buffer_id;
    
    Full_Cursor cursor;
    Full_Cursor mark;
    float preferred_x;
    int line_height;
    int unwrapped_lines;
};
inline View_Summary
view_summary_zero(){
    View_Summary summary={0};
    return(summary);
}

#define UserInputKey 0
#define UserInputMouse 1

struct User_Input{
    int type;
    int abort;
    union{
        Key_Event_Data key;
        Mouse_State mouse;
    };
    unsigned long long command;
};

#define CommandEqual(c1,c2) ((unsigned long long)(c1) == (unsigned long long)(c2))

struct Query_Bar{
    String prompt;
    String string;
};

struct Theme_Color{
    Style_Tag tag;
    unsigned int color;
};



#define GET_BINDING_DATA(name) int name(void *data, int size)
#define CUSTOM_COMMAND_SIG(name) void name(struct Application_Links *app)
#define HOOK_SIG(name) int name(struct Application_Links *app)
#define SCROLL_RULE_SIG(name) int name(float target_x, float target_y, float *scroll_x, float *scroll_y, int view_id, int is_new_target)

extern "C"{
    typedef CUSTOM_COMMAND_SIG(Custom_Command_Function);
    typedef GET_BINDING_DATA(Get_Binding_Data_Function);
    typedef HOOK_SIG(Hook_Function);
    typedef SCROLL_RULE_SIG(Scroll_Rule_Function);
}

struct Application_Links;

// Command exectuion
#define PUSH_PARAMETER_SIG(n) void n(Application_Links *app, Dynamic param, Dynamic value)
#define PUSH_MEMORY_SIG(n) char* n(Application_Links *app, int len)
#define EXECUTE_COMMAND_SIG(n) void n(Application_Links *app, int command_id)
#define CLEAR_PARAMETERS_SIG(n) void n(Application_Links *app)

// File system navigation
#define DIRECTORY_GET_HOT_SIG(n) int n(Application_Links *app, char *out, int capacity)
#define GET_4ED_PATH_SIG(n) int n(Application_Links *app, char *out, int capacity)
#define FILE_EXISTS_SIG(n) int n(Application_Links *app, char *filename, int len)
#define DIRECTORY_CD_SIG(n) int n(Application_Links *app, char *dir, int *len, int capacity, char *rel_path, int rel_len)
#define GET_FILE_LIST_SIG(n) File_List n(Application_Links *app, char *dir, int len)
#define FREE_FILE_LIST_SIG(n) void n(Application_Links *app, File_List list)

// Direct buffer manipulation
#define GET_BUFFER_FIRST_SIG(n) Buffer_Summary n(Application_Links *app)
#define GET_BUFFER_NEXT_SIG(n) void n(Application_Links *app, Buffer_Summary *buffer)

#define GET_BUFFER_SIG(n) Buffer_Summary n(Application_Links *app, int index)
#define GET_ACTIVE_BUFFER_SIG(n) Buffer_Summary n(Application_Links *app)
#define GET_PARAMETER_BUFFER_SIG(n) Buffer_Summary n(Application_Links *app, int param_index)
#define GET_BUFFER_BY_NAME(n) Buffer_Summary n(Application_Links *app, char *filename, int len)

// TODO(allen): Need more flexible seek system somehow.  Regex?  Just expose the text stream to the user?
#define BUFFER_SEEK_DELIMITER_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, char delim, int seek_forward, int *out)
#define BUFFER_SEEK_STRING_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, char *str, int len, int seek_forward, int *out)
#define BUFFER_SEEK_STRING_INSENSITIVE_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, char *str, int len, int seek_forward, int *out)

#define REFRESH_BUFFER_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer)
#define BUFFER_READ_RANGE_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, int end, char *out)
#define BUFFER_REPLACE_RANGE_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, int end, char *str, int len)
#define BUFFER_SET_POS_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int pos)

// File view manipulation
#define GET_VIEW_FIRST_SIG(n) View_Summary n(Application_Links *app)
#define GET_VIEW_NEXT_SIG(n) void n(Application_Links *app, View_Summary *view)

#define GET_VIEW_SIG(n) View_Summary n(Application_Links *app, int index)
#define GET_ACTIVE_VIEW_SIG(n) View_Summary n(Application_Links *app)

#define REFRESH_VIEW_SIG(n) int n(Application_Links *app, View_Summary *view)
#define VIEW_COMPUTE_CURSOR_SIG(n) Full_Cursor n(Application_Links *app, View_Summary *view, Buffer_Seek seek)
#define VIEW_SET_CURSOR_SIG(n) int n(Application_Links *app, View_Summary *view, Buffer_Seek seek, int set_preferred_x)
#define VIEW_SET_MARK_SIG(n) int n(Application_Links *app, View_Summary *view, Buffer_Seek seek)
#define VIEW_SET_HIGHLIGHT_SIG(n) int n(Application_Links *app, View_Summary *view, int start, int end, int turn_on)
#define VIEW_SET_BUFFER_SIG(n) int n(Application_Links *app, View_Summary *view, int buffer_id)

// Directly get user input
#define GET_USER_INPUT_SIG(n) User_Input n(Application_Links *app, unsigned int get_type, unsigned int abort_type)
#define GET_COMMAND_INPUT_SIG(n) User_Input n(Application_Links *app)

// Queries and information display
#define START_QUERY_BAR_SIG(n) int n(Application_Links *app, Query_Bar *bar, unsigned int flags)
#define END_QUERY_BAR_SIG(n) void n(Application_Links *app, Query_Bar *bar, unsigned int flags)
#define PRINT_MESSAGE_SIG(n) void n(Application_Links *app, char *string, int len)

// Color settings
#define CHANGE_THEME_SIG(n) void n(Application_Links *app, char *name, int len)
#define CHANGE_FONT_SIG(n) void n(Application_Links *app, char *name, int len)
#define SET_THEME_COLORS_SIG(n) void n(Application_Links *app, Theme_Color *colors, int count)


// Boundry type flags
#define BoundryWhitespace 0x1
#define BoundryToken 0x2
#define BoundryAlphanumeric 0x4
#define BoundryCamelCase 0x8



// Input type flags
#define EventOnAnyKey 0x1
#define EventOnEsc 0x2
#define EventOnLeftButton 0x4
#define EventOnRightButton 0x8
#define EventOnWheel 0x10
#define EventOnButton (EventOnLeftButton | EventOnRightButton | EventOnWheel)

// NOTE(allen): These don't work so much, so let's pretend they're not here for now.
#define EventOnMouseMove 0x20
#define EventOnMouse (EventOnButton | EventOnMouseMove)



extern "C"{
    // Command exectuion
    typedef EXECUTE_COMMAND_SIG(Exec_Command_Function);
    typedef PUSH_PARAMETER_SIG(Push_Parameter_Function);
    typedef PUSH_MEMORY_SIG(Push_Memory_Function);
    typedef CLEAR_PARAMETERS_SIG(Clear_Parameters_Function);
    
    // File system navigation
    typedef DIRECTORY_GET_HOT_SIG(Directory_Get_Hot_Function);
    typedef GET_4ED_PATH_SIG(Get_4ed_Path_Function);
    typedef FILE_EXISTS_SIG(File_Exists_Function);
    typedef DIRECTORY_CD_SIG(Directory_CD_Function);
    typedef GET_FILE_LIST_SIG(Get_File_List_Function);
    typedef FREE_FILE_LIST_SIG(Free_File_List_Function);
    
    // Buffer manipulation
    typedef GET_BUFFER_FIRST_SIG(Get_Buffer_First_Function);
    typedef GET_BUFFER_NEXT_SIG(Get_Buffer_Next_Function);
    
    typedef GET_BUFFER_SIG(Get_Buffer_Function);
    typedef GET_ACTIVE_BUFFER_SIG(Get_Active_Buffer_Function);
    typedef GET_PARAMETER_BUFFER_SIG(Get_Parameter_Buffer_Function);
    typedef GET_BUFFER_BY_NAME(Get_Buffer_By_Name_Function);
    
    typedef BUFFER_SEEK_DELIMITER_SIG(Buffer_Seek_Delimiter_Function);
    typedef BUFFER_SEEK_STRING_SIG(Buffer_Seek_String_Function);
    typedef BUFFER_SEEK_STRING_INSENSITIVE_SIG(Buffer_Seek_String_Insensitive_Function);
    
    typedef REFRESH_BUFFER_SIG(Refresh_Buffer_Function);
    typedef BUFFER_READ_RANGE_SIG(Buffer_Read_Range_Function);
    typedef BUFFER_REPLACE_RANGE_SIG(Buffer_Replace_Range_Function);
    typedef BUFFER_SET_POS_SIG(Buffer_Set_Pos_Function);
    
    // View manipulation
    typedef GET_VIEW_FIRST_SIG(Get_View_First_Function);
    typedef GET_VIEW_NEXT_SIG(Get_View_Next_Function);
    
    typedef GET_VIEW_SIG(Get_View_Function);
    typedef GET_ACTIVE_VIEW_SIG(Get_Active_View_Function);
    
    typedef REFRESH_VIEW_SIG(Refresh_View_Function);
    typedef VIEW_COMPUTE_CURSOR_SIG(View_Compute_Cursor_Function);
    typedef VIEW_SET_CURSOR_SIG(View_Set_Cursor_Function);
    typedef VIEW_SET_MARK_SIG(View_Set_Mark_Function);
    typedef VIEW_SET_HIGHLIGHT_SIG(View_Set_Highlight_Function);
    typedef VIEW_SET_BUFFER_SIG(View_Set_Buffer_Function);
    
    // Directly get user input
    typedef GET_USER_INPUT_SIG(Get_User_Input_Function);
    typedef GET_COMMAND_INPUT_SIG(Get_Command_Input_Function);
    
    // Queries
    typedef START_QUERY_BAR_SIG(Start_Query_Bar_Function);
    typedef END_QUERY_BAR_SIG(End_Query_Bar_Function);
    typedef PRINT_MESSAGE_SIG(Print_Message_Function);
    
    // Color settings
    typedef CHANGE_THEME_SIG(Change_Theme_Function);
    typedef CHANGE_FONT_SIG(Change_Font_Function);
    typedef SET_THEME_COLORS_SIG(Set_Theme_Colors_Function);
}

struct Application_Links{
    // User data
    void *memory;
    int memory_size;
    
    // Command exectuion
    Exec_Command_Function *exec_command_keep_stack;
    Push_Parameter_Function *push_parameter;
    Push_Memory_Function *push_memory;
    Clear_Parameters_Function *clear_parameters;
    
    // File system navigation
    Directory_Get_Hot_Function *directory_get_hot;
    Get_4ed_Path_Function *get_4ed_path;
    File_Exists_Function *file_exists;
    Directory_CD_Function *directory_cd;
    Get_File_List_Function *get_file_list;
    Free_File_List_Function *free_file_list;
    
    // Buffer manipulation
    Get_Buffer_First_Function *get_buffer_first;
    Get_Buffer_Next_Function *get_buffer_next;
    
    Get_Buffer_Function *get_buffer;
    Get_Active_Buffer_Function *get_active_buffer;
    Get_Parameter_Buffer_Function *get_parameter_buffer;
    Get_Buffer_By_Name_Function *get_buffer_by_name;
    
    Buffer_Seek_Delimiter_Function *buffer_seek_delimiter;
    Buffer_Seek_String_Function *buffer_seek_string;
    Buffer_Seek_String_Insensitive_Function *buffer_seek_string_insensitive;
    
    Refresh_Buffer_Function *refresh_buffer;
    Buffer_Read_Range_Function *buffer_read_range;
    Buffer_Replace_Range_Function *buffer_replace_range;
    Buffer_Set_Pos_Function *buffer_set_pos;
    
    // View manipulation
    Get_View_First_Function *get_view_first;
    Get_View_Next_Function *get_view_next;
    
    Get_View_Function *get_view;
    Get_Active_View_Function *get_active_view;
    
    Refresh_View_Function *refresh_view;
    View_Compute_Cursor_Function *view_compute_cursor;
    View_Set_Cursor_Function *view_set_cursor;
    View_Set_Mark_Function *view_set_mark;
    View_Set_Highlight_Function *view_set_highlight;
    View_Set_Buffer_Function *view_set_buffer;
    
    // Directly get user input
    Get_User_Input_Function *get_user_input;
    Get_Command_Input_Function *get_command_input;
    
    // Queries
    Start_Query_Bar_Function *start_query_bar;
    End_Query_Bar_Function *end_query_bar;
    Print_Message_Function *print_message;
    
    // Theme
    Change_Theme_Function *change_theme;
    Change_Font_Function *change_font;
    Set_Theme_Colors_Function *set_theme_colors;
    
    // Internal
    void *cmd_context;
};

#define _GET_VERSION_SIG(n) int n(int maj, int min, int patch)
typedef _GET_VERSION_SIG(_Get_Version_Function);

extern "C" _GET_VERSION_SIG(get_alpha_4coder_version){
    int result = (maj == MAJOR && min == MINOR && patch == PATCH);
    return(result);
}

struct Custom_API{
    Get_Binding_Data_Function *get_bindings;
    _Get_Version_Function *get_alpha_4coder_version;
};

// NOTE(allen): definitions for the buffer that communicates to 4ed.exe

enum Binding_Unit_Type{
    unit_header,
    unit_map_begin,
    unit_binding,
    unit_callback,
    unit_inherit,
    unit_hook
};

enum Map_ID{
    mapid_global = (1 << 24),
    mapid_file,
    
    // NOTE(allen): mapid_nomap will remain empty even if you attempt to fill it
    // it is for setting a map's parent to nothing, in cases where you don't want
    // to inherit from global (which is the default).
    mapid_nomap
};

struct Binding_Unit{
    Binding_Unit_Type type;
    union{
        struct{ int total_size; int user_map_count; int error; } header;
        
        struct{ int mapid; int replace; int bind_count; } map_begin;
        struct{ int mapid; } map_inherit;
        struct{
            short code;
            unsigned char modifiers;
            int command_id;
        } binding;
        struct{
            short code;
            unsigned char modifiers;
            Custom_Command_Function *func;
        } callback;
        struct{
            int hook_id;
            void *func;
        } hook;
    };
};

#endif