#![allow(warnings, clippy::all, unexpected_cfgs)]
use super::NodeMatch;
use ast_grep_core::tree_sitter::DisplayContext;

/// merging overlapping/adjacent matches
/// adjacent matches: matches that starts or ends on the same line
pub struct MatchMerger<'a> {
    pub last_start_line: usize,
    pub last_end_line: usize,
    pub last_trailing: &'a str,
    pub last_end_offset: usize,
    pub context: (u16, u16),
}

impl<'a> MatchMerger<'a> {
    pub fn new(nm: &NodeMatch<'a>, (before, after): (u16, u16)) -> Self {
        let display = nm.display_context(before as usize, after as usize);
        let last_start_line = display.start_line + 1;
        let last_end_line = nm.end_pos().line() + 1;
        let last_trailing = display.trailing;
        let last_end_offset = nm.range().end;
        Self {
            last_start_line,
            last_end_line,
            last_end_offset,
            last_trailing,
            context: (before, after),
        }
    }

    // merge non-overlapping matches but start/end on the same line
    pub fn merge_adjacent(&mut self, nm: &NodeMatch<'a>) -> Option<usize> {
        let display = self.display(nm);
        let start_line = display.start_line;
        if start_line <= self.last_end_line + self.context.1 as usize {
            let last_end_offset = self.last_end_offset;
            self.last_end_offset = nm.range().end;
            self.last_trailing = display.trailing;
            Some(last_end_offset)
        } else {
            None
        }
    }

    pub fn conclude_match(&mut self, nm: &NodeMatch<'a>) {
        let display = self.display(nm);
        self.last_start_line = display.start_line + 1;
        self.last_end_line = nm.end_pos().line() + 1;
        self.last_trailing = display.trailing;
        self.last_end_offset = nm.range().end;
    }

    #[inline]
    pub fn check_overlapping(&self, nm: &NodeMatch<'a>) -> bool {
        let range = nm.range();

        // merge overlapping matches.
        // N.B. range.start == last_end_offset does not mean overlapping
        if range.start < self.last_end_offset {
            // guaranteed by pre-order
            debug_assert!(range.end <= self.last_end_offset);
            true
        } else {
            false
        }
    }

    pub fn display(&self, nm: &NodeMatch<'a>) -> DisplayContext<'a> {
        let (before, after) = self.context;
        nm.display_context(before as usize, after as usize)
    }
}
