use std::io::{self, Cursor, Read};

use regex::bytes::Regex;

pub fn consume_cdx_header<R: Read>(reader: &mut R) -> io::Result<bool> {
    let mut buf = [0u8; 5];

    reader.read_exact(&mut buf)?;

    Ok(&buf == b" CDX ")
}

type RecombobulatedReader<R> = io::Chain<Cursor<Vec<u8>>, R>;

pub fn consume_header_until<R: Read>(
    reader: R,
    pattern: &Regex,
) -> io::Result<Option<(u64, RecombobulatedReader<R>)>> {
    let mut line_reader = simd_csv::LineReader::from_reader(reader);
    let mut pos = line_reader.position();
    let mut header_opt: Option<Vec<u8>> = None;

    while let Some(line) = line_reader.read_line()? {
        if !pattern.is_match(line) {
            pos = line_reader.position();
            continue;
        }

        header_opt = Some(line.to_vec());

        break;
    }

    if let Some(mut fixed_data) = header_opt {
        let bufreader = line_reader.into_bufreader();
        fixed_data.push(b'\n');
        fixed_data.extend(bufreader.buffer());

        let fixed_reader = Cursor::new(fixed_data).chain(bufreader.into_inner());

        Ok(Some((pos, fixed_reader)))
    } else {
        Ok(None)
    }
}

pub fn consume_header_while<R: Read>(
    reader: R,
    pattern: &Regex,
) -> io::Result<Option<(u64, RecombobulatedReader<R>)>> {
    let mut line_reader = simd_csv::LineReader::from_reader(reader);
    let mut pos = line_reader.position();
    let mut header_opt: Option<Vec<u8>> = None;

    while let Some(line) = line_reader.read_line()? {
        if pattern.is_match(line) {
            pos = line_reader.position();
            continue;
        }

        header_opt = Some(line.to_vec());

        break;
    }

    if let Some(mut fixed_data) = header_opt {
        let bufreader = line_reader.into_bufreader();
        fixed_data.push(b'\n');
        fixed_data.extend(bufreader.buffer());

        let fixed_reader = Cursor::new(fixed_data).chain(bufreader.into_inner());

        Ok(Some((pos, fixed_reader)))
    } else {
        Ok(None)
    }
}

pub fn consume_lines<R: Read>(
    reader: R,
    limit: usize,
) -> io::Result<Option<(u64, RecombobulatedReader<R>)>> {
    let mut line_reader = simd_csv::LineReader::from_reader(reader);
    let mut pos = line_reader.position();
    let mut seen: usize = 0;
    let mut header_opt: Option<Vec<u8>> = None;

    while let Some(line) = line_reader.read_line()? {
        seen += 1;

        if seen <= limit {
            pos = line_reader.position();
            continue;
        }

        header_opt = Some(line.to_vec());

        break;
    }

    if let Some(mut fixed_data) = header_opt {
        let bufreader = line_reader.into_bufreader();
        fixed_data.push(b'\n');
        fixed_data.extend(bufreader.buffer());

        let fixed_reader = Cursor::new(fixed_data).chain(bufreader.into_inner());

        Ok(Some((pos, fixed_reader)))
    } else {
        Ok(None)
    }
}