// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// 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.
//
//! Rendering time meter
//!
//! Used to track rendering times and provide moving averages.
//!
//! # Examples
//!
//! ```rust
//! // create a meter
//! let mut meter = alacritty::meter::Meter::new();
//!
//! // Sample something.
//! {
//!     let _sampler = meter.sampler();
//! }
//!
//! // Get the moving average. The meter tracks a fixed number of samples, and
//! // the average won't mean much until it's filled up at least once.
//! println!("Average time: {}", meter.average());

use std::time::{Duration, Instant};

const NUM_SAMPLES: usize = 10;

/// The meter
#[derive(Default)]
pub struct Meter {
    /// Track last 60 timestamps
    times: [f64; NUM_SAMPLES],

    /// Average sample time in microseconds
    avg: f64,

    /// Index of next time to update.
    index: usize,
}

/// Sampler
///
/// Samplers record how long they are "alive" for and update the meter on drop.
pub struct Sampler<'a> {
    /// Reference to meter that created the sampler
    meter: &'a mut Meter,

    // When the sampler was created
    created_at: Instant,
}

impl<'a> Sampler<'a> {
    fn new(meter: &'a mut Meter) -> Sampler<'a> {
        Sampler { meter, created_at: Instant::now() }
    }

    #[inline]
    fn alive_duration(&self) -> Duration {
        self.created_at.elapsed()
    }
}

impl<'a> Drop for Sampler<'a> {
    fn drop(&mut self) {
        self.meter.add_sample(self.alive_duration());
    }
}

impl Meter {
    /// Create a meter
    pub fn new() -> Meter {
        Default::default()
    }

    /// Get a sampler
    pub fn sampler(&mut self) -> Sampler<'_> {
        Sampler::new(self)
    }

    /// Get the current average sample duration in microseconds
    pub fn average(&self) -> f64 {
        self.avg
    }

    /// Add a sample
    ///
    /// Used by Sampler::drop.
    fn add_sample(&mut self, sample: Duration) {
        let mut usec = 0f64;

        usec += f64::from(sample.subsec_nanos()) / 1e3;
        usec += (sample.as_secs() as f64) * 1e6;

        let prev = self.times[self.index];
        self.times[self.index] = usec;
        self.avg -= prev / NUM_SAMPLES as f64;
        self.avg += usec / NUM_SAMPLES as f64;
        self.index = (self.index + 1) % NUM_SAMPLES;
    }
}
