void CommandWindowInit() {
    Window *window = CreateWind();
    CommandBarWindowID = window->id;
    Buffer *buffer = CreateBuffer(SysAllocator, "command_bar");
    View *view = CreateView(buffer->id);
    window->active_view = view->id;
    window->draw_line_numbers = false;
    window->draw_scrollbar = false;
    window->draw_darker = true;
    window->draw_line_highlight = false;
    window->layout = false;
    window->visible = false;
    window->sync_visibility_with_focus = true;
    window->lose_focus_on_escape = true;
    window->jump_history = false;
}

void CommandWindowLayout(Rect2I *rect, Int wx, Int wy) {
    Window *n = GetWindow(CommandBarWindowID);
    Rect2I copy_rect = *rect;
    if (!n->visible) {
        rect = &copy_rect;
    }
    Int barsize = Clamp((Int)n->font->line_spacing*10, (Int)0, (Int)wx - 100);
    n->document_rect = n->total_rect = CutBottom(rect, barsize);
}

void CommandWindowUpdate() {
    BSet active = GetBSet(ActiveWindowID);
    if (active.window->id == CommandBarWindowID) {
        if (!ProcessIsActive(active.view->id)) {
            Scratch scratch;
            String16 last_line_string = GetLineStringWithoutNL(active.buffer, active.buffer->line_starts.len - 1);
            if (active.view->prev_search_line != last_line_string) {
                active.view->prev_search_line = last_line_string;
                Array<FuzzyPair> ratings = FuzzySearchLines(scratch, active.buffer, 0, active.buffer->line_starts.len - 1, last_line_string);

                Buffer *temp_buffer = CreateTempBuffer(scratch, active.buffer->cap);
                For(IterateInReverse(&ratings)) {
                    String16 s = GetLineStringWithoutNL(active.buffer, it.index);
                    if (s.len == 0) continue;
                    RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), s);
                    RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), u"\n");
                }
                RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), last_line_string);

                Caret caret = active.view->carets[0];
                SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
                SelectEntireBuffer(active.view);
                Replace(active.view, GetString(temp_buffer));
                active.view->carets[0] = caret;
            }
        }
    }
}

void Command_ShowCommands() {
    BSet command_bar = GetBSet(CommandBarWindowID);
    command_bar.window->visible = true;
    command_bar.window->eval_command = true;
    ActiveWindowID = command_bar.window->id;
    ResetBuffer(command_bar.buffer);
    For(CommandFunctions) {
        Appendf(command_bar.view, "%S\n", it.name);
    }
    command_bar.view->update_scroll = true;
    SelectRange(command_bar.view, GetBufferEndAsRange(command_bar.buffer));
} RegisterCommand(Command_ShowCommands, "ctrl-shift-p");

void Command_ShowBufferList() {
    BSet command_bar = GetBSet(CommandBarWindowID);
    command_bar.window->visible = true;
    command_bar.window->eval_command = false;
    ActiveWindowID = command_bar.window->id;
    ResetBuffer(command_bar.buffer);
    For(Buffers) {
        RawAppendf(command_bar.buffer, "%-80S || %S\n", SkipToLastSlash(it->name), it->name);
    }
    command_bar.view->update_scroll = true;
    SelectRange(command_bar.view, GetBufferEndAsRange(command_bar.buffer));
} RegisterCommand(Command_ShowBufferList, "ctrl-p");

void EvalCommand(String command) {
    For (CommandFunctions) {
        if (it.name == command) {
            it.function();
            break;
        }
    }
}

void EvalCommand(String16 command) {
    Scratch scratch;
    EvalCommand(ToString(scratch, command));
}

void CommandWindowOpen(BSet active) {
    Range range = active.view->carets[0].range;
    String16 string = FetchLoadWord(active.view);
    if (GetSize(range) == 0) {
        Int line = PosToLine(active.buffer, range.min);
        if ((active.buffer->line_starts.len - 1) == line) {
            line = ClampBottom(0ll, line - 1ll);
        }

        string = GetLineStringWithoutNL(active.buffer, line);
        Int idx = 0;
        if (Seek(string, u"||", &idx)) {
            string = Skip(string, idx + 3);
        }
    }
    if (active.window->eval_command) {
        BSet main = GetBSet(LastActiveLayoutWindowID);
        ActiveWindowID = main.window->id;
        EvalCommand(string);
    } else {
        Open(string);
    }
}
