use emgauwa_common::errors::EmgauwaError;
use emgauwa_common::{drivers, settings};
use rppal_pfd::PiFaceDigital;
use serde_derive::Deserialize;

use crate::driver::Driver;

#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
#[allow(unused)]
pub struct Relay {
	pub driver: Driver,
	pub name: String,
	pub number: i64,
	pub pin: u8,
	pub inverted: bool,
	pub pulse: Option<u64>,
}

#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
#[allow(unused)]
pub struct Settings {
	pub server: settings::Server,
	pub database: String,
	pub permissions: settings::Permissions,
	pub logging: settings::Logging,

	pub name: String,
	pub relays: Vec<Relay>,
}

impl Default for Settings {
	fn default() -> Self {
		Settings {
			server: settings::Server::default(),
			database: String::from("sqlite://emgauwa-controller.sqlite"),
			permissions: settings::Permissions::default(),
			logging: settings::Logging::default(),

			name: String::from("Emgauwa Controller"),
			relays: Vec::new(),
		}
	}
}

impl Default for Relay {
	fn default() -> Self {
		Relay {
			driver: Driver::Gpio,
			number: None,
			name: String::from("Relay"),
			pin: 0,
			inverted: false,
			pulse: None,
		}
	}
}

pub fn init() -> Result<Settings, EmgauwaError> {
	settings::load("controller", "CONTROLLER")
}

impl Settings {
	pub fn get_relay(&self, number: i64) -> Option<&Relay> {
		self.relays.iter().find(|r| r.number == number)
	}

	pub fn relays_make_drivers(
		&self,
		pfd: &mut Option<PiFaceDigital>,
	) -> Result<Vec<Box<dyn drivers::RelayDriver>>, EmgauwaError> {
		let mut drivers = Vec::new();
		for relay in &self.relays {
			drivers.push(relay.make_driver(pfd)?);
		}
		Ok(drivers)
	}
}

impl Relay {
	pub fn make_driver(
		&self,
		pfd: &mut Option<PiFaceDigital>,
	) -> Result<Box<dyn drivers::RelayDriver>, EmgauwaError> {
		let driver: Box<dyn drivers::RelayDriver> = match self.driver {
			Driver::Null => Box::new(drivers::NullDriver::new(self.pin)),
			Driver::Gpio => Box::new(drivers::GpioDriver::new(self.pin, self.inverted)?),
			Driver::PiFace => {
				if pfd.is_none() {
					*pfd = Some(drivers::PiFaceDriver::init_piface()?);
				}
				Box::new(drivers::PiFaceDriver::new(self.pin, pfd)?)
			}
		};
		Ok(driver)
	}
}