use crate::astronomy::*;
use crate::dmath;
use chrono::{Date, Utc};
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum CalculationType {
Angle(f64),
Minutes(f64),
}
impl CalculationType {
pub fn unwrap(&self) -> f64 {
match self {
CalculationType::Angle(v) | CalculationType::Minutes(v) => *v,
}
}
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum MidnightMethod {
Standard,
Jafari,
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AsrJuristic {
Standard,
Hanafi,
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct CalculationMethod {
imsak: CalculationType,
fajr: f64,
dhuhr: f64,
asr: AsrJuristic,
maghrib: CalculationType,
isha: CalculationType,
midnight: MidnightMethod,
}
impl CalculationMethod {
pub fn new(
imsak: Option<CalculationType>,
fajr: f64,
asr: Option<AsrJuristic>,
maghrib: Option<CalculationType>,
isha: CalculationType,
midnight: Option<MidnightMethod>,
) -> CalculationMethod {
CalculationMethod {
imsak: imsak.unwrap_or(CalculationType::Minutes(10.0)),
fajr,
dhuhr: 0.0,
asr: asr.unwrap_or(AsrJuristic::Standard),
maghrib: maghrib.unwrap_or(CalculationType::Minutes(0.0)),
isha,
midnight: midnight.unwrap_or(MidnightMethod::Standard),
}
}
pub fn from(fajr: f64, isha: CalculationType) -> CalculationMethod {
CalculationMethod::new(None, fajr, None, None, isha, None)
}
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum CalculationMethods {
MWL,
ISNA,
Egypt,
Makkah(bool),
Karachi,
Tehran,
Jafari,
MF,
Custom(CalculationMethod),
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct PrayerTimes {
pub imsak: f64,
pub fajr: f64,
pub sunrise: f64,
pub dhuhr: f64,
pub asr: f64,
pub sunset: f64,
pub maghrib: f64,
pub isha: f64,
pub midnight: f64,
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum HightLatMethods {
NightMiddle,
AngleBased,
OneSeventh,
}
fn time_diff(time1: f64, time2: f64) -> f64 {
dmath::fix_hour(time2 - time1)
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct PrayerManager {
method: CalculationMethod,
high_lats: Option<HightLatMethods>,
}
impl PrayerManager {
pub fn new(method: CalculationMethods, high_lats: Option<HightLatMethods>) -> PrayerManager {
PrayerManager {
method: PrayerManager::get_calculation_method(method),
high_lats,
}
}
pub fn get_calculation_method(calculation_method: CalculationMethods) -> CalculationMethod {
match calculation_method {
CalculationMethods::MWL => CalculationMethod::from(18.0, CalculationType::Angle(17.0)),
CalculationMethods::ISNA => CalculationMethod::from(15.0, CalculationType::Angle(15.0)),
CalculationMethods::Egypt => CalculationMethod::from(19.5, CalculationType::Angle(17.5)),
CalculationMethods::Makkah(is_ramadan) => CalculationMethod::from(
18.5,
CalculationType::Minutes(if is_ramadan { 120.0 } else { 90.0 }),
),
CalculationMethods::Karachi => CalculationMethod::from(18.0, CalculationType::Angle(18.0)),
CalculationMethods::Tehran => CalculationMethod::new(
None,
17.7,
None,
Some(CalculationType::Angle(4.5)),
CalculationType::Angle(14.0),
Some(MidnightMethod::Jafari),
),
CalculationMethods::Jafari => CalculationMethod::new(
None,
16.0,
None,
Some(CalculationType::Angle(4.0)),
CalculationType::Angle(14.0),
Some(MidnightMethod::Jafari),
),
CalculationMethods::MF => CalculationMethod::from(12.0, CalculationType::Angle(12.0)),
CalculationMethods::Custom(value) => value,
}
}
pub fn get_times(&self, date: Date<Utc>, coords: Coordinates) -> PrayerTimes {
let julian_day = get_julian_day(&date) - coords.1 / (15.0 * 24.0);
let method = &self.method;
let adjust = coords.1 / 15.0;
let mut imsak = sun_angle_time(
julian_day,
coords.0,
method.imsak.unwrap(),
5.0 / 24.0,
true,
) - adjust;
let mut fajr = sun_angle_time(julian_day, coords.0, method.fajr, 5.0 / 24.0, true) - adjust;
let sunrise = sun_angle_time(
julian_day,
coords.0,
rise_set_angle(coords.2),
6.0 / 24.0,
true,
) - adjust;
let dhuhr = mid_day(julian_day, 12.0 / 24.0) - adjust + method.dhuhr / 60.0;
let asr = PrayerManager::asr_time(julian_day, coords.0, &method.asr, 13.0 / 24.0) - adjust;
let sunset = sun_angle_time(
julian_day,
coords.0,
rise_set_angle(coords.2),
18.0 / 24.0,
false,
) - adjust;
let mut maghrib = sun_angle_time(
julian_day,
coords.0,
method.maghrib.unwrap(),
18.0 / 24.0,
false,
) - adjust;
let mut isha = sun_angle_time(
julian_day,
coords.0,
method.isha.unwrap(),
18.0 / 24.0,
false,
) - adjust;
if self.high_lats.is_some() {
let night_time = time_diff(sunset, sunrise);
imsak = self.adjust_highlat_time(imsak, sunrise, method.imsak.unwrap(), night_time, true);
fajr = self.adjust_highlat_time(fajr, sunrise, method.fajr, night_time, true);
maghrib =
self.adjust_highlat_time(maghrib, sunset, method.maghrib.unwrap(), night_time, false);
isha = self.adjust_highlat_time(isha, sunset, method.isha.unwrap(), night_time, false);
}
if let CalculationType::Minutes(minutes) = method.imsak {
imsak = fajr - minutes / 60.0;
}
if let CalculationType::Minutes(minutes) = method.maghrib {
maghrib = sunset - minutes / 60.0;
}
if let CalculationType::Minutes(minutes) = method.isha {
isha = maghrib - minutes / 60.0;
}
let midnight = sunset
+ match method.midnight {
MidnightMethod::Standard => time_diff(sunset, sunrise),
MidnightMethod::Jafari => time_diff(sunset, fajr),
} / 2.0;
PrayerTimes {
imsak,
fajr,
sunrise,
dhuhr,
asr,
sunset,
maghrib,
isha,
midnight,
}
}
fn adjust_highlat_time(&self, time: f64, base: f64, angle: f64, night: f64, ccw: bool) -> f64 {
let portion = self.night_portion(angle, night);
let diff = if ccw {
time_diff(time, base)
} else {
time_diff(base, time)
};
if portion > diff {
return time;
}
base + if ccw { -portion } else { portion }
}
fn asr_time(julian_day: f64, latitude: f64, factor_type: &AsrJuristic, time: f64) -> f64 {
let decl = sun_position(julian_day + time).0;
let factor = match factor_type {
AsrJuristic::Standard => 1.0,
AsrJuristic::Hanafi => 2.0,
};
let angle = -dmath::arccot(&(factor + dmath::tan(&(latitude - decl).abs())));
sun_angle_time(julian_day, latitude, angle, time, false)
}
fn night_portion(&self, angle: f64, night: f64) -> f64 {
(match self.high_lats.as_ref().unwrap() {
HightLatMethods::NightMiddle => 1.0 / 2.0,
HightLatMethods::AngleBased => 1.0 / 60.0 * angle,
HightLatMethods::OneSeventh => 1.0 / 7.0,
}) * night
}
}