use crate::{index_to_i32, Span};

#[derive(Debug, Clone)]
pub(crate) struct LineResolver {
    lines: Vec<i32>,
}

impl LineResolver {
    pub fn new(source_code: &str) -> Self {
        let lines = source_code
            .match_indices('\n')
            .map(|(index, _)| index_to_i32(index + 1))
            .collect();
        LineResolver { lines }
    }

    pub fn resolve(&self, offset: usize) -> (i32, i32) {
        match self.lines.binary_search(&index_to_i32(offset)) {
            Ok(index) => (index_to_i32(index + 1), 0),
            Err(0) => (0, index_to_i32(offset)),
            Err(index) => (
                index_to_i32(index),
                index_to_i32(offset) - self.lines[index - 1],
            ),
        }
    }

    pub fn resolve_span(&self, span: Span) -> Vec<i32> {
        let (start_line, start_col) = self.resolve(span.start);
        let (end_line, end_col) = self.resolve(span.end);

        if start_line == end_line {
            vec![start_line, start_col, end_col]
        } else {
            vec![start_line, start_col, end_line, end_col]
        }
    }
}

#[test]
fn resolve_line_number() {
    let resolver = LineResolver::new("hello\nworld\nfoo");

    assert_eq!(resolver.resolve(0), (0, 0));
    assert_eq!(resolver.resolve(4), (0, 4));
    assert_eq!(resolver.resolve(5), (0, 5));
    assert_eq!(resolver.resolve(6), (1, 0));
    assert_eq!(resolver.resolve(7), (1, 1));
    assert_eq!(resolver.resolve(10), (1, 4));
    assert_eq!(resolver.resolve(11), (1, 5));
    assert_eq!(resolver.resolve(12), (2, 0));
    assert_eq!(resolver.resolve(13), (2, 1));
    assert_eq!(resolver.resolve(14), (2, 2));
    assert_eq!(resolver.resolve(15), (2, 3));

    let resolver = LineResolver::new("f\n\noo\r\nbar");

    assert_eq!(resolver.resolve(0), (0, 0));
    assert_eq!(resolver.resolve(1), (0, 1));
    assert_eq!(resolver.resolve(2), (1, 0));
    assert_eq!(resolver.resolve(3), (2, 0));
    assert_eq!(resolver.resolve(4), (2, 1));
    assert_eq!(resolver.resolve(5), (2, 2));
    assert_eq!(resolver.resolve(6), (2, 3));
    assert_eq!(resolver.resolve(7), (3, 0));
    assert_eq!(resolver.resolve(8), (3, 1));
    assert_eq!(resolver.resolve(9), (3, 2));
    assert_eq!(resolver.resolve(10), (3, 3));
}
