/*
The implementation for the custom API
*/

// TOP

internal void
fill_buffer_summary(Buffer_Summary *buffer, Editing_File *file, Working_Set *working_set){
    *buffer = buffer_summary_zero();
    if (!file->is_dummy){
        buffer->exists = 1;
        buffer->ready = file_is_ready(file);
        
        buffer->is_lexed = file->settings.tokens_exist;
        buffer->buffer_id = file->id.id;
        buffer->size = file->state.buffer.size;
        buffer->buffer_cursor_pos = file->state.cursor_pos;
        
        buffer->file_name_len = file->name.source_path.size;
        buffer->buffer_name_len = file->name.live_name.size;
        buffer->file_name = file->name.source_path.str;
        buffer->buffer_name = file->name.live_name.str;
        
        buffer->map_id = file->settings.base_map_id;
    }
}

internal void
fill_view_summary(View_Summary *view, View *vptr, Live_Views *live_set, Working_Set *working_set){
    i32 lock_level;
    int buffer_id;
    *view = view_summary_zero();
    
    if (vptr->in_use){
        view->exists = 1;
        view->view_id = (int)(vptr - live_set->views) + 1;
        view->line_height = (float)(vptr->line_height);
        view->unwrapped_lines = vptr->file_data.unwrapped_lines;
        view->show_whitespace = vptr->file_data.show_whitespace;
        
        
        if (vptr->file_data.file){
            lock_level = view_lock_level(vptr);
            buffer_id = vptr->file_data.file->id.id;
            
            if (lock_level <= 0){
                view->buffer_id = buffer_id;
            }
            else{
                view->buffer_id = 0;
            }
            
            if (lock_level <= 1){
                view->locked_buffer_id = buffer_id;
            }
            else{
                view->locked_buffer_id = 0;
            }
            
            if (lock_level <= 2){
                view->hidden_buffer_id = buffer_id;
            }
            else{
                view->hidden_buffer_id = 0;
            }
            
            view->mark = view_compute_cursor_from_pos(vptr, vptr->recent->mark);
            view->cursor = vptr->recent->cursor;
            view->preferred_x = vptr->recent->preferred_x;
            
            view->file_region = vptr->file_region;
            view->scroll_vars = *vptr->current_scroll;
        }
    }
}

internal Editing_File*
get_file_from_identifier(System_Functions *system, Working_Set *working_set, Buffer_Identifier buffer){
    i32 buffer_id = buffer.id;
    i32 buffer_name_len = buffer.name_len;
    char *buffer_name = buffer.name;
    
    Editing_File *file = 0;
    
    if (buffer_id){
        file = working_set_get_active_file(working_set, buffer_id);
    }
    else if (buffer_name){
        file = working_set_contains(system, working_set, make_string(buffer_name, buffer_name_len));
    }
    
    return(file);
}

String
make_string_terminated(Partition *part, char *str, int len){
    char *space = (char*)push_array(part, char, len + 1);
    String string = make_string(str, len, len+1);
    copy_fast_unsafe(space, string);
    string.str = space;
    terminate_with_null(&string);
    return(string);
}

EXEC_COMMAND_SIG(external_exec_command){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Command_Function function = command_table[command_id];
    Command_Binding binding = {};
    binding.function = function;
    if (function) function(cmd->system, cmd, binding);
    
    update_command_data(cmd->vars, cmd);
}

// TODO(allen): This is a bit of a mess and needs to be fixed soon
EXEC_SYSTEM_COMMAND_SIG(external_exec_system_command){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    App_Vars *vars = cmd->vars;
    Models *models = cmd->models;
    
    char feedback_space[256];
    String feedback_str = make_fixed_width_string(feedback_space);
    
    Working_Set *working_set = &models->working_set;
    CLI_Process *procs = vars->cli_processes.procs, *proc = 0;
    Editing_File *file = 0;
    b32 bind_to_new_view = true;
    General_Memory *general = &models->mem.general;
    
    Partition *part = &models->mem.part;
    Temp_Memory temp = begin_temp_memory(part);
    
    View *vptr = 0;
    
    int result = true;
    
    if (view->exists){
        Live_Views *live_set = cmd->live_set;
        i32 view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
        }
    }
    
    if (vptr == 0){
        result = false;
        goto done;
    }
    
    if (vars->cli_processes.count < vars->cli_processes.max){
        file = get_file_from_identifier(system, working_set, buffer);
        if (file){
            if (file->settings.read_only == 0){
                append(&feedback_str, "ERROR: ");
                append(&feedback_str, file->name.live_name);
                append(&feedback_str, " is not a read-only buffer\n");
                do_feedback_message(system, models, feedback_str);
                result = false;
                goto done;
            }
            if (file->settings.never_kill){
                append(&feedback_str, "The buffer ");
                append(&feedback_str, file->name.live_name);
                append(&feedback_str, " is not killable");
                do_feedback_message(system, models, feedback_str);
                result = false;
                goto done;
            }
        }
        else if (buffer.name){
            file = working_set_alloc_always(working_set, general);
            if (file == 0){
                append(&feedback_str, "ERROR: unable to  allocate a new buffer\n");
                do_feedback_message(system, models, feedback_str);
                result = false;
                goto done;
            }
            file_create_read_only(system, models, file, buffer.name);
            working_set_add(system, working_set, file, general);
        }
        
        if (file){
            i32 proc_count = vars->cli_processes.count;
            View_Iter iter;
            i32 i;
            
            for (i = 0; i < proc_count; ++i){
                if (procs[i].out_file == file){
                    if (flags & CLI_OverlapWithConflict){
                        procs[i].out_file = 0;
                    }
                    else{
                        file = 0;
                    }
                    break;
                }
            }
            
            if (file){
                file_clear(system, models, file, 1);
                file->settings.unimportant = 1;
                
                if (!(flags & CLI_AlwaysBindToView)){
                    iter = file_view_iter_init(&models->layout, file, 0);
                    if (file_view_iter_good(iter)){
                        bind_to_new_view = 0;
                    }
                }
            }
            else{
                append(&feedback_str, "did not begin command-line command because the target buffer is already in use\n");
                do_feedback_message(system, models, feedback_str);
                result = false;
                goto done;
            }
        }
        
        String path_string = {0};
        if (!path){
            terminate_with_null(&models->hot_directory.string);
            path_string = models->hot_directory.string;
        }
        else{
            path_string = make_string_terminated(part, path, path_len);
        }
        
        {
            String command_string = {0};
            
            if (!command){
#define NO_SCRIPT " echo no script specified"
                command_string.str = NO_SCRIPT;
                command_string.size = sizeof(NO_SCRIPT)-1;
#undef NO_SCRIPT
            }
            else{
                command_string = make_string_terminated(part, command, command_len);
            }
            
            if (bind_to_new_view){
                view_set_file(vptr, file, models);
                view_show_file(vptr);
            }
            
            proc = procs + vars->cli_processes.count++;
            proc->out_file = file;
            
            if (!system->cli_call(path_string.str, command_string.str, &proc->cli)){
                --vars->cli_processes.count;
            }
        }
    }
    else{
        append(&feedback_str, "ERROR: no available process slot\n");
        do_feedback_message(system, models, feedback_str);
        result = false;
        goto done;
    }
    
    done:
    end_temp_memory(temp);
    return(result);
}

DIRECTORY_GET_HOT_SIG(external_directory_get_hot){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Hot_Directory *hot = &cmd->models->hot_directory;
    i32 copy_max = capacity - 1;
    hot_directory_clean_end(hot);
    if (copy_max > hot->string.size)
        copy_max = hot->string.size;
    memcpy(out, hot->string.str, copy_max);
    out[copy_max] = 0;
    return(hot->string.size);
}

#define external_get_4ed_path system->get_4ed_path

#define external_file_exists  system->file_exists

#define external_directory_cd system->directory_cd

GET_FILE_LIST_SIG(external_get_file_list){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    File_List result = {};
    system->set_file_list(&result, make_string(dir, len));
    return(result);
}

FREE_FILE_LIST_SIG(external_free_file_list){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    system->set_file_list(&list, make_string(0, 0));
}

CLIPBOARD_POST_SIG(external_clipboard_post){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    Models *models = cmd->models;
    General_Memory *general = &models->mem.general;
    Working_Set *working = &models->working_set;
    int result = false;
    
    String *dest = working_set_next_clipboard_string(general, working, len);
    copy(dest, make_string(str, len));
    system->post_clipboard(*dest);
    
    return(result);
}

CLIPBOARD_COUNT_SIG(external_clipboard_count){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Working_Set *working = &cmd->models->working_set;
    int count = working->clipboard_size;
    return(count);
}

CLIPBOARD_INDEX_SIG(external_clipboard_index){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Working_Set *working = &cmd->models->working_set;
    
    int size = 0;
    String *str = working_set_clipboard_index(working, index);
    if (str){
        size = str->size;
        if (out){
            copy_fast_unsafe(out, *str);
        }
    }
    
    return(size);
}

GET_BUFFER_FIRST_SIG(external_get_buffer_first){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Working_Set *working_set = &cmd->models->working_set;
    Buffer_Summary result = {};
    if (working_set->file_count > 0){
        fill_buffer_summary(&result, (Editing_File*)working_set->used_sentinel.next, working_set);
    }
    return(result);
}

GET_BUFFER_NEXT_SIG(external_get_buffer_next){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Working_Set *working_set = &cmd->models->working_set;
    Editing_File *file;
    
    file = working_set_get_active_file(working_set, buffer->buffer_id);
    if (file){
        file = (Editing_File*)file->node.next;
        fill_buffer_summary(buffer, file, working_set);
    }
    else{
        *buffer = buffer_summary_zero();
    }
}

GET_BUFFER_SIG(external_get_buffer){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Working_Set *working_set = &cmd->models->working_set;
    Buffer_Summary buffer = {};
    Editing_File *file;
    
    file = working_set_get_active_file(working_set, index);
    if (file){
        fill_buffer_summary(&buffer, file, working_set);
    }
    
    return(buffer);
}

GET_PARAMETER_BUFFER_SIG(external_get_parameter_buffer){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Models *models = cmd->models;
    Buffer_Summary buffer = {};
    
    if (param_index >= 0 && param_index < models->buffer_param_count){
        buffer = external_get_buffer(app, models->buffer_param_indices[param_index]);
    }
    
    return(buffer);
}

GET_BUFFER_BY_NAME_SIG(external_get_buffer_by_name){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Buffer_Summary buffer = {};
    Editing_File *file;
    Working_Set *working_set = &cmd->models->working_set;
    
    file = working_set_contains(cmd->system, working_set, make_string(filename, len));
    if (file && !file->is_dummy){
        fill_buffer_summary(&buffer, file, working_set);
    }
    
    return(buffer);
}

REFRESH_BUFFER_SIG(external_refresh_buffer){
    int result;
    *buffer = external_get_buffer(app, buffer->buffer_id);
    result = buffer->exists;
    return(result);
}

BUFFER_SEEK_SIG(external_buffer_seek){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Editing_File *file;
    Working_Set *working_set;
    int result = 0;
    
    if (buffer->exists){
        working_set = &cmd->models->working_set;
        file = working_set_get_active_file(working_set, buffer->buffer_id);
        if (file && file_is_ready(file)){
            // TODO(allen): reduce duplication?
            {
                i32 size = buffer_size(&file->state.buffer);
                i32 pos[4] = {0};
                i32 new_pos = 0;
                
                if (start_pos < 0){
                    start_pos = 0;
                }
                else if (start_pos > size){
                    start_pos = size;
                }
                
                if (seek_forward){
                    for (i32 i = 0; i < ArrayCount(pos); ++i) pos[i] = size;
                    
                    if (flags & (1)){
                        pos[0] = buffer_seek_whitespace_right(&file->state.buffer, start_pos);
                    }
                    
                    if (flags & (1 << 1)){
                        if (file->state.tokens_complete){
                            pos[1] = seek_token_right(&file->state.token_stack, start_pos);
                        }
                        else{
                            pos[1] = buffer_seek_whitespace_right(&file->state.buffer, start_pos);
                        }
                    }
                    
                    if (flags & (1 << 2)){
                        pos[2] = buffer_seek_alphanumeric_right(&file->state.buffer, start_pos);
                        if (flags & (1 << 3)){
                            pos[3] = buffer_seek_range_camel_right(&file->state.buffer, start_pos, pos[2]);
                        }
                    }
                    else{
                        if (flags & (1 << 3)){
                            pos[3] = buffer_seek_alphanumeric_or_camel_right(&file->state.buffer, start_pos);
                        }
                    }
                    
                    new_pos = size;
                    for (i32 i = 0; i < ArrayCount(pos); ++i){
                        if (pos[i] < new_pos) new_pos = pos[i];
                    }
                }
                else{
                    if (flags & (1)){
                        pos[0] = buffer_seek_whitespace_left(&file->state.buffer, start_pos);
                    }
                    
                    if (flags & (1 << 1)){
                        if (file->state.tokens_complete){
                            pos[1] = seek_token_left(&file->state.token_stack, start_pos);
                        }
                        else{
                            pos[1] = buffer_seek_whitespace_left(&file->state.buffer, start_pos);
                        }
                    }
                    
                    if (flags & (1 << 2)){
                        pos[2] = buffer_seek_alphanumeric_left(&file->state.buffer, start_pos);
                        if (flags & (1 << 3)){
                            pos[3] = buffer_seek_range_camel_left(&file->state.buffer, start_pos, pos[2]);
                        }
                    }
                    else{
                        if (flags & (1 << 3)){
                            pos[3] = buffer_seek_alphanumeric_or_camel_left(&file->state.buffer, start_pos);
                        }
                    }
                    
                    new_pos = 0;
                    for (i32 i = 0; i < ArrayCount(pos); ++i){
                        if (pos[i] > new_pos) new_pos = pos[i];
                    }
                }
                result = new_pos;
            }
            
            fill_buffer_summary(buffer, file, working_set);
        }
    }
    
    return(result);
}

BUFFER_READ_RANGE_SIG(external_buffer_read_range){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Editing_File *file;
    Working_Set *working_set;
    int result = 0;
    int size;
    
    if (buffer->exists){
        working_set = &cmd->models->working_set;
        file = working_set_get_active_file(working_set, buffer->buffer_id);
        if (file && file_is_ready(file)){
            size = buffer_size(&file->state.buffer);
            if (0 <= start && start <= end && end <= size){
                result = 1;
                buffer_stringify(&file->state.buffer, start, end, out);
            }
            fill_buffer_summary(buffer, file, working_set);
        }
    }
    
    return(result);
}

BUFFER_REPLACE_RANGE_SIG(external_buffer_replace_range){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Editing_File *file;
    Working_Set *working_set;
    
    Models *models;
    
    int result = 0;
    int size;
    int next_cursor, pos;
    
    if (buffer->exists){
        models = cmd->models;
        working_set = &models->working_set;
        file = working_set_get_active_file(working_set, buffer->buffer_id);
        if (file && file_is_ready(file)){
            size = buffer_size(&file->state.buffer);
            if (0 <= start && start <= end && end <= size){
                result = 1;
                
                pos = file->state.cursor_pos;
                if (pos < start) next_cursor = pos;
                else if (pos < end) next_cursor = start;
                else next_cursor = pos + end - start - len;
                
                file_replace_range(cmd->system, models, file, start, end, str, len, next_cursor);
            }
            fill_buffer_summary(buffer, file, working_set);
        }
    }
    
    return(result);
}

#if 0
BUFFER_SET_POS_SIG(external_buffer_set_pos){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Editing_File *file;
    Working_Set *working_set;
    
    int result = 0;
    int size;
    
    if (buffer->exists){
        working_set = &cmd->models->working_set;
        file = working_set_get_active_file(working_set, buffer->buffer_id);
        if (file && file_is_ready(file)){
            result = 1;
            size = buffer_size(&file->state.buffer);
            if (pos < 0) pos = 0;
            if (pos > size) pos = size;
            file->state.cursor_pos = pos;
            fill_buffer_summary(buffer, file, working_set);
        }
    }
    
    return(result);
}
#endif

BUFFER_SET_SETTING_SIG(external_buffer_set_setting){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    Models *models = cmd->models;
    
    Editing_File *file;
    Working_Set *working_set;
    
    int result = false;
    
    i32 new_mapid = 0;
    
    if (buffer->exists){
        working_set = &models->working_set;
        file = working_set_get_active_file(working_set, buffer->buffer_id);
        if (file && file_is_ready(file)){
            result = true;
            switch (setting){
                case BufferSetting_Lex:
                {
#if BUFFER_EXPERIMENT_SCALPEL <= 0
                    if (file->settings.tokens_exist){
                        if (!value){
                            file_kill_tokens(system, &models->mem.general, file);
                        }
                    }
                    else{
                        if (value){
                            file_first_lex_parallel(system, &models->mem.general, file);
                        }
                    }
#endif
                }break;
                
                case BufferSetting_WrapLine:
                {
                    file->settings.unwrapped_lines = !value;
                }break;
                
                case BufferSetting_MapID:
                {
                    if (value == mapid_global){
                        file->settings.base_map_id = mapid_global;
                    }
                    else if (value == mapid_file){
                        file->settings.base_map_id = mapid_file;
                    }
                    else if (value < mapid_global){
                        new_mapid = get_map_index(models, value);
                        if (new_mapid  < models->user_map_count){
                            file->settings.base_map_id = value;
                        }
                        else{
                            file->settings.base_map_id = mapid_file;
                        }
                    }
                    
                    for (View_Iter iter = file_view_iter_init(&models->layout, file, 0);
                         file_view_iter_good(iter);
                         iter = file_view_iter_next(iter)){
                        iter.view->map = get_map(models, file->settings.base_map_id);
                    }
                }break;
            }
        }
        
        fill_buffer_summary(buffer, file, working_set);
    }
    
    return(result);
}

BUFFER_SAVE_SIG(external_buffer_save){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    Models *models = cmd->models;
    
    Editing_File *file;
    Working_Set *working_set;
    
    int result = false;
    
    if (buffer->exists){
        working_set = &models->working_set;
        file = working_set_get_active_file(working_set, buffer->buffer_id);
        if (file && !file->is_dummy && file_is_ready(file)){
            result = true;
            String name = make_string(filename, filename_len);
            view_save_file(system, models, file, 0, name, 0);
        }
    }
    
    return(result);
}

GET_VIEW_FIRST_SIG(external_get_view_first){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Editing_Layout *layout = &cmd->models->layout;
    View_Summary view = {};
    
    Panel *panel = layout->used_sentinel.next;
    
    Assert(panel != &layout->used_sentinel);
    fill_view_summary(&view, panel->view, &cmd->vars->live_set, &cmd->models->working_set);
    
    return(view);
}

GET_VIEW_NEXT_SIG(external_get_view_next){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Editing_Layout *layout = &cmd->models->layout;
    Live_Views *live_set = &cmd->vars->live_set;
    View *vptr;
    Panel *panel;
    int index = view->view_id - 1;
    
    if (index >= 0 && index < live_set->max){
        vptr = live_set->views + index;
        panel = vptr->panel;
        if (panel) panel = panel->next;
        if (panel && panel != &layout->used_sentinel){
            fill_view_summary(view, panel->view, &cmd->vars->live_set, &cmd->models->working_set);
        }
        else{
            *view = view_summary_zero();
        }
    }
    else{
        *view = view_summary_zero();
    }
}

GET_VIEW_SIG(external_get_view){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    View_Summary view = {};
    Live_Views *live_set = cmd->live_set;
    int max = live_set->max;
    View *vptr;
    
    index -= 1;
    if (index >= 0 && index < max){
        vptr = live_set->views + index;
        fill_view_summary(&view, vptr, live_set, &cmd->models->working_set);
    }
    
    return(view);
}

GET_ACTIVE_VIEW_SIG(external_get_active_view){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    View_Summary view = {};
    fill_view_summary(&view, cmd->view, &cmd->vars->live_set, &cmd->models->working_set);
    return(view);
}

REFRESH_VIEW_SIG(external_refresh_view){
    int result;
    *view = external_get_view(app, view->view_id);
    result = view->exists;
    return(result);
}

VIEW_AUTO_TAB_SIG(external_view_auto_tab){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    Models *models = cmd->models;
    
    int result = false;
    
    Live_Views *live_set;
    Editing_File *file;
    View *vptr;
    int view_id;
    
    if (view->exists){
        live_set = cmd->live_set;
        view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            file = vptr->file_data.file;
            
            if (file && file->state.token_stack.tokens &&
                file->state.tokens_complete && !file->state.still_lexing){
                result = true;
                
                Indent_Options opts;
                opts.empty_blank_lines = (flags & AutoTab_ClearLine);
                opts.use_tabs = (flags & AutoTab_UseTab);
                opts.tab_width = tab_width;
                
                view_auto_tab_tokens(system, models, vptr, start, end, opts);
            }
        }
    }
    
    return(result);
}

VIEW_COMPUTE_CURSOR_SIG(external_view_compute_cursor){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Live_Views *live_set;
    View *vptr;
    Editing_File *file;
    Full_Cursor result = {0};
    int view_id;
    
    if (view->exists){
        live_set = cmd->live_set;
        view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            file = vptr->file_data.file;
            if (file && !file->is_loading){
                if (seek.type == buffer_seek_line_char && seek.character <= 0){
                    seek.character = 1;
                }
                result = view_compute_cursor(vptr, seek);
                fill_view_summary(view, vptr, live_set, &cmd->models->working_set);
            }
        }
    }
    
    return(result);
}

VIEW_SET_CURSOR_SIG(external_view_set_cursor){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Live_Views *live_set;
    View *vptr;
    Editing_File *file;
    int result = 0;
    int view_id;
    
    if (view->exists){
        live_set = cmd->live_set;
        view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            file = vptr->file_data.file;
            if (file && !file->is_loading){
                result = 1;
                if (seek.type == buffer_seek_line_char && seek.character <= 0){
                    seek.character = 1;
                }
                vptr->recent->cursor = view_compute_cursor(vptr, seek);
                if (set_preferred_x){
                    vptr->recent->preferred_x = view_get_cursor_x(vptr);
                }
                fill_view_summary(view, vptr, live_set, &cmd->models->working_set);
                file->state.cursor_pos = vptr->recent->cursor.pos;
            }
        }
    }
    
    return(result);
}

VIEW_SET_MARK_SIG(external_view_set_mark){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Live_Views *live_set;
    View *vptr;
    Full_Cursor cursor;
    int result = 0;
    int view_id;
    
    if (view->exists){
        live_set = cmd->live_set;
        view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            result = 1;
            if (seek.type != buffer_seek_pos){
                cursor = view_compute_cursor(vptr, seek);
                vptr->recent->mark = cursor.pos;
            }
            else{
                vptr->recent->mark = seek.pos;
            }
            fill_view_summary(view, vptr, live_set, &cmd->models->working_set);
        }
    }
    
    return(result);
}

VIEW_SET_HIGHLIGHT_SIG(external_view_set_highlight){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Live_Views *live_set;
    View *vptr;
    int result = 0;
    int view_id;
    
    if (view->exists){
        live_set = cmd->live_set;
        view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            result = 1;
            if (turn_on){
                view_set_temp_highlight(vptr, start, end);
            }
            else{
                vptr->file_data.show_temp_highlight = 0;
            }
            fill_view_summary(view, vptr, live_set, &cmd->models->working_set);
        }
    }
    
    return(result);
}

VIEW_SET_BUFFER_SIG(external_view_set_buffer){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Live_Views *live_set;
    View *vptr;
    Editing_File *file;
    Working_Set *working_set;
    Models *models;
    int result = 0;
    int view_id;
    
    if (view->exists){
        models = cmd->models;
        live_set = cmd->live_set;
        view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            working_set = &models->working_set;
            file = working_set_get_active_file(working_set, buffer_id);
            
            if (file){
                result = 1;
                if (file != vptr->file_data.file){
                    view_set_file(vptr, file, models);
                    view_show_file(vptr);
                }
            }
            
            fill_view_summary(view, vptr, live_set, working_set);
        }
    }
    
    return(result);
}

VIEW_OPEN_FILE_SIG(external_view_open_file){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    
    System_Functions *system = cmd->system;
    Models *models = cmd->models;
    
    Working_Set *working_set = &models->working_set;
    
    Live_Views *live_set = cmd->live_set;
    
    int result = false;
    
    // TODO(allen): do in background option
    
    Partition *part = &models->mem.part;
    Temp_Memory temp = begin_temp_memory(part);
    String string = make_string_terminated(part, filename, filename_len);
    
    if (do_in_background){
        result = true;
        view_open_file(system, models, 0, string);
    }
    else if (view){
        View *vptr = 0;
        int view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            result = true;
            
            view_open_file(system, models, 0, string);
            
            fill_view_summary(view, vptr, live_set, working_set);
        }
    }
    
    end_temp_memory(temp);
    
    return(result);
}

VIEW_KILL_BUFFER_SIG(external_view_kill_buffer){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    Live_Views *live_set;
    View *vptr;
    Editing_File *file;
    Working_Set *working_set;
    Models *models;
    int result = false;
    int view_id;
    
    if (view->exists){
        models = cmd->models;
        live_set = cmd->live_set;
        view_id = view->view_id - 1;
        if (view_id >= 0 && view_id < live_set->max){
            vptr = live_set->views + view_id;
            working_set = &models->working_set;
            file = get_file_from_identifier(system, working_set, buffer);
            
            if (file){
                result = true;
                try_kill_file(system, models, file, vptr, string_zero());
                fill_view_summary(view, vptr, live_set, working_set);
            }
        }
    }
    
    return(result);
}

GET_USER_INPUT_SIG(external_get_user_input){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    System_Functions *system = cmd->system;
    Coroutine *coroutine = (Coroutine*)app->current_coroutine;
    User_Input result = {0};
    
    if (app->type_coroutine == Co_Command){
        Assert(coroutine);
        *((u32*)coroutine->out+0) = get_type;
        *((u32*)coroutine->out+1) = abort_type;
        system->yield_coroutine(coroutine);
        result = *(User_Input*)coroutine->in;
    }
    
    return(result);
}

GET_COMMAND_INPUT_SIG(external_get_command_input){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    User_Input result;
    
    result.type = UserInputKey;
    result.abort = 0;
    result.key = cmd->key;
    result.command = 0;
    
    return(result);
}

GET_MOUSE_STATE_SIG(external_get_mouse_state){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    App_Vars *vars = cmd->vars;
    Mouse_State mouse = direct_get_mouse_state(&vars->available_input);
    return(mouse);
}

GET_EVENT_MESSAGE_SIG(external_get_event_message){
    Event_Message message = {0};
    System_Functions *system = (System_Functions*)app->system_links;
    Coroutine *coroutine = (Coroutine*)app->current_coroutine;
    
    if (app->type_coroutine == Co_View){
        Assert(coroutine);
        system->yield_coroutine(coroutine);
        message = *(Event_Message*)coroutine->in;
    }
    
    return(message);
}

START_QUERY_BAR_SIG(external_start_query_bar){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Query_Slot *slot = 0;
    View *vptr;
    
    vptr = cmd->view;
    
    slot = alloc_query_slot(&vptr->query_set);
    slot->query_bar = bar;
    
    return(slot != 0);
}

END_QUERY_BAR_SIG(external_end_query_bar){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    View *vptr;
    vptr = cmd->view;
    free_query_slot(&vptr->query_set, bar);
}

PRINT_MESSAGE_SIG(external_print_message){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Models *models = cmd->models;
    do_feedback_message(cmd->system, models, make_string(string, len));
}

#if 0
GET_GUI_FUNCTIONS_SIG(external_get_gui_functions){
    GUI_Functions *guifn = 0;
    NotImplemented;
    return(guifn);
}

GET_GUI_SIG(external_get_gui){
    GUI *gui = 0;
    NotImplemented;
    return(gui);
}
#endif

CHANGE_THEME_SIG(external_change_theme){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Style_Library *styles = &cmd->models->styles;
    String theme_name = make_string(name, len);
    Style *s;
    i32 i, count;
    
    count = styles->count;
    s = styles->styles;
    for (i = 0; i < count; ++i, ++s){
        if (match(s->name, theme_name)){
            style_copy(main_style(cmd->models), s);
            break;
        }
    }
}

CHANGE_FONT_SIG(external_change_font){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Font_Set *set = cmd->models->font_set;
    Style_Font *global_font = &cmd->models->global_font;
    String font_name = make_string(name, len);
    i16 font_id;
    
    if (font_set_extract(set, font_name, &font_id)){
        global_font->font_id = font_id;
        global_font->font_changed = 1;
    }
}

SET_THEME_COLORS_SIG(external_set_theme_colors){
    Command_Data *cmd = (Command_Data*)app->cmd_context;
    Style *style = main_style(cmd->models);
    Theme_Color *theme_color;
    u32 *color;
    i32 i;
    
    theme_color = colors;
    for (i = 0; i < count; ++i, ++theme_color){
        color = style_index_by_tag(&style->main, theme_color->tag);
        if (color) *color = theme_color->color | 0xFF000000;
    }
}

// BOTTOM