#include "editor/editor.h"
#include "main.h"

void move_line_up(Editor *editor) {
  if (!editor || !editor->root || editor->cursor.row == 0)
    return;
  if (mode == NORMAL || mode == INSERT) {
    uint32_t line_len, line_cluster_len;
    std::shared_lock lock(editor->knot_mtx);
    LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
    char *line = next_line(it, &line_len);
    if (!line) {
      lock.unlock();
      return;
    }
    if (line_len > 0 && line[line_len - 1] == '\n')
      line_len--;
    line_cluster_len = count_clusters(line, line_len, 0, line_len);
    uint32_t target_row = editor->cursor.row - 1;
    uint32_t up_by = editor->cursor.row - target_row;
    if (up_by > 1)
      up_by--;
    lock.unlock();
    Coord cursor = editor->cursor;
    edit_erase(editor, {cursor.row, 0}, line_cluster_len);
    edit_erase(editor, {cursor.row, 0}, -1);
    edit_insert(editor, {cursor.row - up_by, 0}, (char *)"\n", 1);
    edit_insert(editor, {cursor.row - up_by, 0}, line, line_len);
    free(it->buffer);
    free(it);
    editor->cursor = {cursor.row - up_by, cursor.col};
  } else if (mode == SELECT) {
    uint32_t start_row = MIN(editor->cursor.row, editor->selection.row);
    uint32_t end_row = MAX(editor->cursor.row, editor->selection.row);
    uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr);
    uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr);
    char *selected_text = read(editor->root, start_byte, end_byte - start_byte);
    if (!selected_text)
      return;
    uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
                                           0, end_byte - start_byte);
    Coord cursor = editor->cursor;
    Coord selection = editor->selection;
    edit_erase(editor, {start_row, 0}, selected_len);
    edit_insert(editor, {start_row - 1, 0}, selected_text,
                end_byte - start_byte);
    free(selected_text);
    editor->cursor = {cursor.row - 1, cursor.col};
    editor->selection = {selection.row - 1, selection.col};
  }
}

void move_line_down(Editor *editor) {
  if (!editor || !editor->root)
    return;
  if (mode == NORMAL || mode == INSERT) {
    if (editor->cursor.row >= editor->root->line_count - 1)
      return;
    uint32_t line_len, line_cluster_len;
    std::shared_lock lock(editor->knot_mtx);
    LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
    char *line = next_line(it, &line_len);
    if (!line) {
      lock.unlock();
      return;
    }
    if (line_len && line[line_len - 1] == '\n')
      line_len--;
    line_cluster_len = count_clusters(line, line_len, 0, line_len);
    uint32_t target_row = editor->cursor.row + 1;
    if (target_row >= editor->root->line_count) {
      free(line);
      lock.unlock();
      return;
    }
    uint32_t down_by = target_row - editor->cursor.row;
    if (down_by > 1)
      down_by--;
    uint32_t ln;
    line_to_byte(editor->root, editor->cursor.row + down_by - 1, &ln);
    lock.unlock();
    Coord cursor = editor->cursor;
    edit_erase(editor, {cursor.row, 0}, line_cluster_len);
    edit_erase(editor, {cursor.row, 0}, -1);
    edit_insert(editor, {cursor.row + down_by, 0}, (char *)"\n", 1);
    edit_insert(editor, {cursor.row + down_by, 0}, line, line_len);
    free(it->buffer);
    free(it);
    editor->cursor = {cursor.row + down_by, cursor.col};
  } else if (mode == SELECT) {
    if (editor->cursor.row >= editor->root->line_count - 1 ||
        editor->selection.row >= editor->root->line_count - 1)
      return;
    std::shared_lock lock(editor->knot_mtx);
    uint32_t start_row = MIN(editor->cursor.row, editor->selection.row);
    uint32_t end_row = MAX(editor->cursor.row, editor->selection.row);
    uint32_t target_row = end_row + 1;
    if (target_row >= editor->root->line_count)
      return;
    uint32_t down_by = target_row - end_row;
    if (down_by > 1)
      down_by--;
    uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr);
    uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr);
    char *selected_text = read(editor->root, start_byte, end_byte - start_byte);
    lock.unlock();
    if (!selected_text)
      return;
    uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
                                           0, end_byte - start_byte);
    Coord cursor = editor->cursor;
    Coord selection = editor->selection;
    edit_erase(editor, {start_row, 0}, selected_len);
    edit_insert(editor, {start_row + down_by, 0}, selected_text,
                end_byte - start_byte);
    free(selected_text);
    editor->cursor = {cursor.row + down_by, cursor.col};
    editor->selection = {selection.row + down_by, selection.col};
  }
}
