// This file is part of the Rust library and binary `aligner`.
//
// Copyright (C) 2017 kaegi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


use std;
use std::ops::*;
use internal::TimeDelta;
use std::cmp::max;

// these objects determine the precision/length of the rating (i32/i64) - lower
// values take less space and time, higher values have higher precision
type RatingIntern = i64;
const RATING_PRECISION: RatingIntern = (1 << 32);

/// Use an integer for internal rating, because we add MANY small values which
/// lead to precision issues for floats
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Rating(RatingIntern);
impl std::fmt::Display for Rating {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.0 as f64 / RATING_PRECISION as f64)
    }
}


impl Rating {
    pub fn zero() -> Rating {
        Rating(0)
    }
    pub fn from_overlapping_spans(a: TimeDelta, b: TimeDelta) -> Rating {
        let max: RatingIntern = max(a, b).into();
        // if min == 0 || max == 0 { return Self::zero() }

        // the score is "score := min/max" so it ges a score of 1 if both spans are equally long.
        // The score is added in all overlapping segments, so if we don't devide by a length value, we
        // end up with "score * num_overlapping_segments" which is dependent on the choosen resolution
        // and will priorize longer time spans.
        //
        // By deviding this value by "min", which is maximum number of overlapping segments, we can
        // "normalize" the score and get the final rating. But now we have "score/min == (min/max)/min == 1/max".
        //
        // The total score is now "overlapping_percentage_in_min / 100 * score".
        let x = RATING_PRECISION / max;

        Rating(x as RatingIntern)
    }

    pub fn nosplit_bonus(unnormalized: f64) -> Rating {
        Rating((RATING_PRECISION as f64 * unnormalized) as RatingIntern)
    }
}

// There is no absolute `Rating` so, there should be no way to construct one.
// This is pretty nifty for testing though.
#[cfg(test)]
impl From<i64> for Rating {
    fn from(f: i64) -> Rating {
        Rating(f as RatingIntern)
    }
}


impl Add for Rating {
    type Output = Rating;
    fn add(self, c: Rating) -> Rating {
        Rating(self.0 + c.0)
    }
}
impl Sub for Rating {
    type Output = Rating;
    fn sub(self, c: Rating) -> Rating {
        Rating(self.0 - c.0)
    }
}
impl AddAssign for Rating {
    fn add_assign(&mut self, c: Rating) {
        self.0 += c.0;
    }
}
impl SubAssign for Rating {
    fn sub_assign(&mut self, c: Rating) {
        self.0 -= c.0;
    }
}
impl std::iter::Sum for Rating {
    fn sum<I>(iter: I) -> Rating
        where I: Iterator<Item = Rating>
    {
        Rating(iter.map(|c| c.0).sum())
    }
}
impl Mul<u64> for Rating {
    type Output = Rating;
    fn mul(self, rhs: u64) -> Rating {
        Rating(self.0 * rhs as RatingIntern)
    }
}
impl Mul<i64> for Rating {
    type Output = Rating;
    fn mul(self, rhs: i64) -> Rating {
        Rating(self.0 * rhs as RatingIntern)
    }
}
impl Div<Rating> for Rating {
    type Output = i64;
    fn div(self, rhs: Rating) -> i64 {
        (self.0 / rhs.0) as i64
    }
}

impl Neg for Rating {
    type Output = Rating;

    fn neg(self) -> Rating {
        Rating(-self.0)
    }
}
