use super::InputState;

pub(super) fn insert_at_cursor(input: &mut InputState, character: char) {
    input.value.insert(input.cursor, character);
    input.cursor += character.len_utf8();
}

pub(super) fn insert_str_at_cursor(input: &mut InputState, value: &str) {
    input.value.insert_str(input.cursor, value);
    input.cursor += value.len();
}

pub(super) fn delete_before_cursor(input: &mut InputState) {
    let previous = previous_boundary(&input.value, input.cursor);
    if previous != input.cursor {
        input.value.drain(previous..input.cursor);
        input.cursor = previous;
    }
}

pub(super) fn delete_at_cursor(input: &mut InputState) {
    let next = next_boundary(&input.value, input.cursor);
    if next != input.cursor {
        input.value.drain(input.cursor..next);
    }
}

pub(super) fn previous_boundary(value: &str, cursor: usize) -> usize {
    value[..cursor]
        .char_indices()
        .next_back()
        .map_or(0, |(index, _)| index)
}

pub(super) fn next_boundary(value: &str, cursor: usize) -> usize {
    value[cursor..]
        .chars()
        .next()
        .map_or(cursor, |character| cursor + character.len_utf8())
}

pub(super) fn line_start(value: &str, cursor: usize) -> usize {
    value[..cursor].rfind('\n').map_or(0, |index| index + 1)
}

pub(super) fn line_end(value: &str, cursor: usize) -> usize {
    value[cursor..]
        .find('\n')
        .map_or(value.len(), |index| cursor + index)
}

fn byte_at_character(value: &str, start: usize, end: usize, column: usize) -> usize {
    value[start..end]
        .char_indices()
        .nth(column)
        .map_or(end, |(offset, _)| start + offset)
}

pub(super) fn move_cursor_vertically(input: &mut InputState, delta: isize) {
    let start = line_start(&input.value, input.cursor);
    let end = line_end(&input.value, input.cursor);
    let column = input.value[start..input.cursor].chars().count();
    input.cursor = if delta < 0 {
        if start == 0 {
            input.cursor
        } else {
            let target_end = start - 1;
            let target_start = input.value[..target_end]
                .rfind('\n')
                .map_or(0, |index| index + 1);
            byte_at_character(&input.value, target_start, target_end, column)
        }
    } else if end == input.value.len() {
        input.cursor
    } else {
        let target_start = end + 1;
        let target_end = input.value[target_start..]
            .find('\n')
            .map_or(input.value.len(), |index| target_start + index);
        byte_at_character(&input.value, target_start, target_end, column)
    };
}
