use serde::{Deserialize, Serialize};

use crate::Max;
use crate::Min;
use std::cmp;
use std::fmt;

#[derive(PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct Cardinality {
    pub min: Min,
    pub max: Max,
}

impl Cardinality {
    pub fn from(min: Min, max: Max) -> Cardinality {
        Cardinality { min, max }
    }

    pub fn nullable(&self) -> bool {
        self.min == Min { value: 0 }
    }

    pub fn is_0_0(&self) -> bool {
        self.min == Min { value: 0 } && self.max == Max::IntMax(0)
    }

    pub fn is_1_1(&self) -> bool {
        self.min == Min { value: 1 } && self.max == Max::IntMax(1)
    }

    pub fn is_star(&self) -> bool {
        self.min == Min { value: 0 } && self.max == Max::Unbounded
    }

    pub fn is_plus(&self) -> bool {
        self.min == Min { value: 1 } && self.max == Max::Unbounded
    }

    pub fn contains(&self, n: usize) -> bool {
        n >= self.min.value && self.max.greater_or_equal(n)
    }

    pub fn minus(&self, n: usize) -> Cardinality {
        let min = self.min.value.saturating_sub(n);
        /*if self.min.value > n {
            self.min.value - n
        } else {
            0
        };*/
        Cardinality {
            min: Min {
                value: cmp::max(min, 0),
            },
            max: self.max.minus(n),
        }
    }
}

impl fmt::Display for Cardinality {
    fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result {
        match (&self.min, &self.max) {
            (Min { value: 1 }, Max::IntMax(1)) => write!(dest, ""),
            (Min { value: 0 }, Max::IntMax(1)) => write!(dest, "?"),
            (Min { value: 0 }, Max::Unbounded) => write!(dest, "*"),
            (Min { value: 1 }, Max::Unbounded) => write!(dest, "+"),
            (min, Max::Unbounded) => write!(dest, "{{{},}}", min.value),
            (min, max) => write!(dest, "{{{}, {}}}", min.value, max),
        }
    }
}

impl fmt::Debug for Cardinality {
    fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result {
        match (&self.min, &self.max) {
            (Min { value: 0 }, Max::IntMax(1)) => write!(dest, "?"),
            (Min { value: 0 }, Max::Unbounded) => write!(dest, "*"),
            (Min { value: 1 }, Max::Unbounded) => write!(dest, "+"),
            (min, Max::Unbounded) => write!(dest, "{{{},}}", min.value),
            (min, max) => write!(dest, "{{{}, {}}}", min.value, max),
        }
    }
}
