// Copyright 2019 Stichting Organism
// Copyright (c) 2018-2019 Prime Type Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


//! A topic on the p3p system. Data is organized around topics.
//! You can subscribe to recieve all events for a topic.


mod subscription;
pub use subscription::{
    Subscriptions,
    Subscription
};
use std::hash::{Hash, Hasher};

use serde::{Deserialize, Serialize};

/// A topic is a unique identifier to a subject of pub/sup one node
/// is interested about.
///
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct Topic(u32);

impl From<u32> for Topic {
    fn from(value: u32) -> Topic {
        Topic(value)
    }
}


/// This is the interest associated to a topic
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
pub enum InterestLevel {
    /// This describe a low interest level
    Low,
    /// This describe a normal interest level
    Normal,
    /// This describe an high interest level
    High,
}


#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ProximityScore(usize);

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct PriorityScore(usize);

#[derive(Copy, Clone, Debug)]
pub struct Proximity {
    priority: PriorityScore,
    proximity: ProximityScore,
}


impl InterestLevel {
    #[inline]
    fn priority_score(self, other: Self) -> usize {
        use InterestLevel::*;
        match (self, other) {
            (Low, Low) => 1,
            (Low, Normal) => 2,
            (Normal, Low) => 2,
            (Low, High) => 3,
            (High, Low) => 3,
            (Normal, Normal) => 5,
            (Normal, High) => 6,
            (High, Normal) => 6,
            (High, High) => 10,
        }
    }
}



impl PartialEq<Self> for Proximity {
    fn eq(&self, other: &Self) -> bool {
        self.cmp(other) == std::cmp::Ordering::Equal
    }
}
impl Eq for Proximity {}
impl PartialOrd<Self> for Proximity {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for Proximity {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        use std::cmp::Ordering::{Equal, Greater, Less};
        if self.priority > other.priority {
            Greater
        } else if self.priority < other.priority {
            Less
        } else if self.proximity > other.proximity {
            Greater
        } else if self.proximity < other.proximity {
            Less
        } else {
            Equal
        }
    }
}
impl Hash for Proximity {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.priority.hash(state);
        self.proximity.hash(state);
    }
}



#[cfg(test)]
mod test {
    use super::*;
    use quickcheck::{Arbitrary, Gen};

    impl Arbitrary for Topic {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            Topic::from(u32::arbitrary(g))
        }
    }

    impl Arbitrary for InterestLevel {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            match u8::arbitrary(g) % 3 {
                0 => InterestLevel::Low,
                1 => InterestLevel::Normal,
                _ => InterestLevel::High,
            }
        }
    }

    impl Arbitrary for Subscription {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            Subscription {
                topic: Topic::arbitrary(g),
                interest_level: InterestLevel::arbitrary(g),
            }
        }
    }

    impl Arbitrary for Subscriptions {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            let subscriptions: Vec<Subscription> = Arbitrary::arbitrary(g);

            let mut subs = Subscriptions::default();
            for subscription in subscriptions {
                subs.add(subscription);
            }
            subs
        }
    }
}