use std::cmp::{Ordering, Reverse};
use std::collections::BinaryHeap;

#[derive(Clone)]
pub(crate) struct Node {
    pub(crate) id: String,
    pub(crate) vector: Vec<f32>,
    pub(crate) deleted: bool,
    pub(crate) neighbors: Vec<Vec<usize>>,
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct ScoredNode {
    pub(crate) index: usize,
    pub(crate) score: f32,
}

#[derive(Default)]
pub(crate) struct SearchScratch {
    marks: Vec<u32>,
    mark: u32,
    pub(crate) candidates: BinaryHeap<ScoredNode>,
    pub(crate) results: BinaryHeap<Reverse<ScoredNode>>,
    pub(crate) found: Vec<ScoredNode>,
}

impl SearchScratch {
    pub(crate) fn reset(&mut self, nodes: usize) {
        if self.marks.len() < nodes {
            self.marks.resize(nodes, 0);
        }
        self.mark = self.mark.wrapping_add(1);
        if self.mark == 0 {
            self.marks.fill(0);
            self.mark = 1;
        }
        self.candidates.clear();
        self.results.clear();
        self.found.clear();
    }

    pub(crate) fn visit(&mut self, index: usize) -> bool {
        if self.marks[index] == self.mark {
            return false;
        }
        self.marks[index] = self.mark;
        true
    }
}

impl Eq for ScoredNode {}

impl PartialEq for ScoredNode {
    fn eq(&self, other: &Self) -> bool {
        self.index == other.index && self.score.to_bits() == other.score.to_bits()
    }
}

impl Ord for ScoredNode {
    fn cmp(&self, other: &Self) -> Ordering {
        self.score
            .total_cmp(&other.score)
            .then_with(|| other.index.cmp(&self.index))
    }
}

impl PartialOrd for ScoredNode {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

pub(crate) fn compare_scored_desc(left: &ScoredNode, right: &ScoredNode) -> Ordering {
    right
        .score
        .total_cmp(&left.score)
        .then_with(|| left.index.cmp(&right.index))
}
