//! Cache of block resolution.

use crate::common::{diff::Hunk, lines::LinesSlice};
use foldhash::HashMap;
use super::{Blocks, BlockChangeInProgress, BlockCurrent, BlockType, undo};

pub(crate) struct ResolutionCacheEntry {
    hunk_key: Vec<u8>,
    pub(crate) current: BlockCurrent,
    pub(crate) undo_stack: Vec<Box<dyn undo::Undo>>,
}

/// The collection of block resolutions. Internally, it's indexed by old lines.
#[derive(Default)]
pub(crate) struct ResolutionCache(HashMap<Vec<u8>, Vec<ResolutionCacheEntry>>);

impl ResolutionCache {
    /// Consume the blocks and add them to the resolution cache.
    pub(crate) fn add_blocks(&mut self, blocks: Blocks) {
        for block in blocks.blocks {
            let BlockType::Change(change) = block.details else { continue; };
            if change.in_progress != BlockChangeInProgress::None {
                // Ignore blocks in progress. These will have to be evaluated again.
                continue;
            }
            let entry = ResolutionCacheEntry {
                hunk_key: change.hunk.iter_lines_by_bytes().collect(),
                current: change.current,
                undo_stack: block.undo_stack,
            };
            let key = blocks.old_lines.line_slice(change.range).byte_slice().to_vec();
            self.0.entry(key).or_insert_with(Vec::new).push(entry);
        }
    }

    /// Looks for a resolution matching the `hunk` applied to `old_lines`. If found, the
    /// resolution is removed from the cache and returned.
    pub(crate) fn pop(&mut self, old_lines: LinesSlice<'_>, hunk: &Hunk)
        -> Option<ResolutionCacheEntry>
    {
        let list = self.0.get_mut(old_lines.byte_slice())?;
        let index = list.iter().position(|entry|
            entry.hunk_key.iter().copied().eq(hunk.iter_lines_by_bytes())
        )?;
        let result = list.remove(index);
        if list.is_empty() {
            self.0.remove(old_lines.byte_slice());
        }
        Some(result)
    }
}
