void SearchWindowInit() {
    Window *window = CreateWind();
    SearchWindowID = window->id;
    Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(WorkDir, "search"));
    buffer->special = true;
    SearchBufferID = buffer->id;
    View *view = CreateView(buffer->id);
    view->special = true;
    SearchViewID = view->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->lose_visibility_on_escape = true;
    window->jump_history = false;
}

void SearchWindowLayout(Rect2I *rect, Int wx, Int wy) {
    Window *command_window = GetWindow(CommandWindowID);
    if (command_window->visible) {
        return;
    }
    Window *n = GetWindow(SearchWindowID);
    Rect2I copy_rect = *rect;
    if (!n->visible) {
        rect = &copy_rect;
    }
    Int barsize = GetExpandingBarSize(n);
    n->document_rect = n->total_rect = CutBottom(rect, barsize);
    n->line_numbers_rect = CutLeft(&n->document_rect, n->font->char_spacing * 6);
}

void Command_Search() {
    BSet main = GetBSet(ActiveWindowID);
    String16 string = {};
    if (main.view->carets.len == 1 && GetSize(main.view->carets[0]) > 0) {
        string = GetString(main.buffer, main.view->carets[0].range);
    }
    BSet set = GetBSet(SearchWindowID);
    set.window->visible = true;
    NextActiveWindowID = SearchWindowID;
    SelectEntireBuffer(set.view);
    if (string.len > 0) {
        Replace(set.view, string);
        SelectEntireBuffer(set.view);
    }
} RegisterCommand(Command_Search, "ctrl-f");

void SearchWindowFindNext(bool forward = true) {
    BSet main = GetBSet(LastActiveLayoutWindowID);
    BSet set = GetBSet(SearchWindowID);
    String16 seek = GetString(set.buffer, GetRange(set.buffer));
    Find(main.view, seek, forward);
    main.window->search_bar_anchor = main.view->carets[0];
}

void Command_SearchNextInSearch() {
    if (ActiveWindowID != SearchWindowID) {
        return;
    }
    SearchWindowFindNext(true);
} RegisterCommand(Command_SearchNextInSearch, "enter");

void Command_SearchPrevInSearch() {
    if (ActiveWindowID != SearchWindowID) {
        return;
    }
    SearchWindowFindNext(false);
} RegisterCommand(Command_SearchPrevInSearch, "shift-enter");

void Command_SearchNext() {
    SearchWindowFindNext(true);
} RegisterCommand(Command_SearchNext, "f3");

void Command_SearchPrev() {
    SearchWindowFindNext(false);
} RegisterCommand(Command_SearchPrev, "shift-f3");

void SearchAll() {
    Scratch scratch;
    BSet main = GetBSet(LastActiveLayoutWindowID);
    BSet set = GetBSet(SearchWindowID);
    String16 needle = GetString(set.buffer, GetRange(set.buffer));
    Array<Caret> all = FindAll(scratch, main.buffer, needle);
    if (all.len > 0) {
        main.view->carets.len = 0;
        For (all) {
            Add(&main.view->carets, it);
        }
        MergeCarets(main.buffer, &main.view->carets);
    }
}

void Command_SearchAll() {
    SearchAll();
    BSet set = GetBSet(SearchWindowID);
    set.window->visible = false;
} RegisterCommand(Command_SearchAll, "alt-f3");

void Command_SearchAllInSearch() {
    if (ActiveWindowID != SearchWindowID) {
        return;
    }
    SearchAll();
    BSet set = GetBSet(SearchWindowID);
    set.window->visible = false;
} RegisterCommand(Command_SearchAllInSearch, "alt-enter");

void Command_ToggleCaseSensitiveSearch() {
    SearchCaseSensitive = !SearchCaseSensitive;
} RegisterCommand(Command_ToggleCaseSensitiveSearch, "alt-c");

void Command_ToggleSearchWordBoundary() {
    SearchWordBoundary = !SearchWordBoundary;
} RegisterCommand(Command_ToggleSearchWordBoundary, "alt-w");

void SearchWindowUpdate() {
    BSet active = GetBSet(ActiveWindowID);
    if (active.window->id == SearchWindowID && active.buffer->begin_frame_change_id != active.buffer->change_id) {
        BSet main = GetBSet(LastActiveLayoutWindowID);
        BSet set = GetBSet(SearchWindowID);
        main.view->carets[0] = main.window->search_bar_anchor;
        String16 seek = GetString(set.buffer, GetRange(set.buffer));
        Find(main.view, seek, true);
    }
}
