#if OS_WASM
EM_ASYNC_JS(void, _SetClipboardText, (const char* c_str), {
  const type = "text/plain";
  const clipboardItemData = {
    [type]: UTF8ToString(c_str),
  };
  const clipboardItem = new ClipboardItem(clipboardItemData);
  await navigator.clipboard.write([clipboardItem]);
})

EM_ASYNC_JS(const char *, GetClipboardText, (), {
    const text = await navigator.clipboard.readText();
    return stringToNewUTF8(text);
})
#define FreeClipboardText free
#else
#define FreeClipboardText SDL_free
#define GetClipboardText SDL_GetClipboardText
#define _SetClipboardText SDL_SetClipboardText
#endif

void FreeClipboardGlobals() {
    Release(&ClipboardArena);
    SavedClipboardString = {};
    SavedClipboardCarets = {};
    SavedClipboardCarets.allocator = ClipboardArena;
}

void SaveStringInClipboard(String16 string) {
    Scratch scratch;
    FreeClipboardGlobals();
    SavedClipboardString = Copy16(ClipboardArena, string);
    _SetClipboardText(ToString(scratch, SavedClipboardString).data);
}

void ClipboardCopy(View *view) {
    Scratch scratch;
    Buffer *buffer = GetBuffer(view->active_buffer);

    // First, if there is no selection - select the entire line
    For(view->carets) {
        if (GetSize(it.range) == 0) {
            Int   line       = PosToLine(buffer, it.range.min);
            Range line_range = GetLineRange(buffer, line);
            it.range         = line_range;
        }
    }

    FreeClipboardGlobals();
    Reserve(&SavedClipboardCarets, view->carets.len);
    For(view->carets) {
        String16 string = GetString(buffer, it.range);
        String16 copy = Copy16(ClipboardArena, string);
        Add(&SavedClipboardCarets, copy);
    }

    // @todo: maybe only add new line if there is no new line at the end, experiment with it
    SavedClipboardString = Merge(ClipboardArena, SavedClipboardCarets, u"\n");
    _SetClipboardText(ToString(scratch, SavedClipboardString).data);
}

void ClipboardPaste(View *view) {
    Scratch     scratch;
    Buffer     *buffer = GetBuffer(view->active_buffer);
    const char *text   = GetClipboardText();
    defer { FreeClipboardText((void *)text); };

    String16 string = ToUnixString16(scratch, text);

    // Regular paste
    if (string != SavedClipboardString || SavedClipboardCarets.len != view->carets.len) {
        Array<Edit> edits = BeginEdit(scratch, buffer, view->carets);
        MergeCarets(buffer, &view->carets);
        For(view->carets) AddEdit(&edits, it.range, string);
        EndEdit(buffer, &edits, &view->carets, KILL_SELECTION);
        return;
    }

    // Multicursor paste
    Array<Edit> edits = BeginEdit(scratch, buffer, view->carets);
    MergeCarets(buffer, &view->carets);
    for (int64_t i = 0; i < view->carets.len; i += 1) {
        String16 string = SavedClipboardCarets[i];
        Caret   &it     = view->carets[i];
        AddEdit(&edits, it.range, string);
    }
    EndEdit(buffer, &edits, &view->carets, KILL_SELECTION);
}

void Command_Paste() {
    BSet active = GetBSet(ActiveWindowID);
    ClipboardPaste(active.view);
} RegisterCommand(Command_Paste, "ctrl-v");

void Command_Copy() {
    BSet active = GetBSet(ActiveWindowID);
    ClipboardCopy(active.view);
} RegisterCommand(Command_Copy, "ctrl-c");

void Command_Cut() {
    BSet active = GetBSet(ActiveWindowID);
    SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
    ClipboardCopy(active.view);
    Replace(active.view, u"");
} RegisterCommand(Command_Cut, "ctrl-x");
