#![doc(
    html_logo_url = "https://openstratos.org/wp-content/uploads/2017/05/OpenStratos-768x226.png",
    html_favicon_url = "https://openstratos.org/wp-content/uploads/2015/10/OpenStratos-mark.png",
    html_root_url = "https://openstratos.github.io/server-rs/"
)]
#![deny(clippy::all)]
#![forbid(anonymous_parameters)]
#![warn(clippy::pedantic)]
#![deny(
    variant_size_differences,
    unused_results,
    unused_qualifications,
    unused_import_braces,
    unsafe_code,
    trivial_numeric_casts,
    trivial_casts,
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    box_pointers,
    unused_extern_crates
)]
#![allow(unsafe_code, box_pointers, clippy::use_self)]
pub const CONFIG_FILE: &str = "config.toml";
pub const STATE_FILE: &str = "last_state";
pub mod config;
pub mod error;
#[cfg(feature = "fona")]
pub mod fona;
#[cfg(feature = "gps")]
pub mod gps;
pub mod logic;
#[cfg(feature = "raspicam")]
pub mod raspicam;
#[cfg(feature = "telemetry")]
pub mod telemetry;
use std::fs;
use failure::{Error, ResultExt};
pub use crate::config::CONFIG;
use crate::logic::{MainLogic, State};
pub fn run() -> Result<(), Error> {
    initialize_data_filesystem().context(error::Fs::DataInit)?;
    if let Some(_state) = State::get_last().context(error::LastState::Read)? {
        
        unimplemented!()
    } else {
        logic::init().context(error::Logic::Init)?.main_logic()
    }
}
pub fn initialize_data_filesystem() -> Result<(), Error> {
    let video_path = CONFIG.data_dir().join("video");
    fs::create_dir_all(&video_path).context(error::Fs::DirectoryCreation { path: video_path })?;
    let img_path = CONFIG.data_dir().join("img");
    fs::create_dir_all(&img_path).context(error::Fs::DirectoryCreation { path: img_path })?;
    Ok(())
}
#[allow(clippy::use_debug)]
pub fn generate_error_string<S>(error: &Error, main_error: S) -> String
where
    S: AsRef<str>,
{
    let mut result = format!("{}:\n{}\n", main_error.as_ref(), error);
    for e in error.iter_causes() {
        result.push_str(&format!("\tcaused by: {}\n", e));
    }
    
    result.push_str(&format!("\tbacktrace: {:?}\n", error.backtrace()));
    result
}
pub fn init_loggers() -> Result<log4rs::Handle, Error> {
    use chrono::Utc;
    use log::LevelFilter;
    use log4rs::{
        append::{console::ConsoleAppender, file::FileAppender},
        config::{Appender, Config, Logger, Root},
        encode::pattern::PatternEncoder,
    };
    
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    use log::Record;
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    use log4rs::filter::{threshold::ThresholdFilter, Filter, Response};
    
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    #[derive(Debug, Clone, Copy)]
    struct DebugFilter;
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    impl Filter for DebugFilter {
        fn filter(&self, record: &Record) -> Response {
            if record.level() == LevelFilter::Debug {
                Response::Neutral
            } else {
                Response::Reject
            }
        }
    }
    
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    #[derive(Debug, Clone, Copy)]
    struct TraceFilter;
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    impl Filter for TraceFilter {
        fn filter(&self, record: &Record) -> Response {
            if record.level() == LevelFilter::Trace {
                Response::Neutral
            } else {
                Response::Reject
            }
        }
    }
    let now = Utc::now().format("%Y-%m-%d-%H-%M-%S");
    let pattern_naive = "[{d(%Y-%m-%d %H:%M:%S %Z)(utc)}][{l}] - {m}{n}";
    
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    let pattern_exact = "[{d(%Y-%m-%d %H:%M:%S%.3f %Z)(utc)}][{l}] - {m}{n}";
    let stdout = ConsoleAppender::builder().build();
    let main = FileAppender::builder()
        .encoder(Box::new(PatternEncoder::new(pattern_naive)))
        .build(format!("data/logs/main-{}.log", now))
        .context(error::Log::Appender { name: "main" })?;
    let system = FileAppender::builder()
        .encoder(Box::new(PatternEncoder::new(pattern_naive)))
        .build(format!("data/logs/system-{}.log", now))
        .context(error::Log::Appender { name: "system" })?;
    
    #[cfg(any(feature = "gps", feature = "fona", feature = "telemetry"))]
    let log_level = if CONFIG.debug() {
        LevelFilter::Trace
    } else {
        LevelFilter::Debug
    };
    let config = Config::builder()
        
        .appender(Appender::builder().build("stdout", Box::new(stdout)))
        .appender(Appender::builder().build("main", Box::new(main)))
        .appender(Appender::builder().build("system", Box::new(system)))
        
        .logger(
            Logger::builder()
                .appender("system")
                .additive(false)
                .build("system", LevelFilter::Info),
        );
    #[cfg(feature = "raspicam")]
    let config = {
        let camera = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(pattern_naive)))
            .build(format!("data/logs/camera-{}.log", now))
            .context(error::Log::Appender { name: "camera" })?;
        config
            .appender(Appender::builder().build("camera", Box::new(camera)))
            .logger(
                Logger::builder()
                    .appender("camera")
                    .additive(false)
                    .build("os_balloon::camera", LevelFilter::Info),
            )
    };
    #[cfg(feature = "gps")]
    let config = {
        let gps = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(pattern_naive)))
            .build(format!("data/logs/gps-{}.log", now))
            .context(error::Log::Appender {
                name: "os_balloon::gps",
            })?;
        let gps_frames = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(pattern_exact)))
            .build(format!("data/logs/gps_frames-{}.log", now))
            .context(error::Log::Appender {
                name: "os_balloon::gps_frames",
            })?;
        let gps_logger = {
            let mut builder = Logger::builder()
                .appender("gps")
                .appender("gps_frames")
                .additive(false);
            if CONFIG.debug() {
                builder = builder.appender("gps_serial");
            }
            builder.build("os_balloon::gps", log_level)
        };
        let config = config
            .appender(
                Appender::builder()
                    .filter(Box::new(ThresholdFilter::new(LevelFilter::Info)))
                    .build("gps", Box::new(gps)),
            )
            .appender(
                Appender::builder()
                    .filter(Box::new(DebugFilter))
                    .build("gps_frames", Box::new(gps_frames)),
            )
            .logger(gps_logger);
        if CONFIG.debug() {
            let gps_serial = FileAppender::builder()
                .encoder(Box::new(PatternEncoder::new(pattern_exact)))
                .build(format!("data/logs/gps_serial-{}.log", now))
                .context(error::Log::Appender { name: "gps_serial" })?;
            config.appender(
                Appender::builder()
                    .filter(Box::new(TraceFilter))
                    .build("gps_serial", Box::new(gps_serial)),
            )
        } else {
            config
        }
    };
    #[cfg(feature = "fona")]
    let config = {
        let fona = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(pattern_naive)))
            .build(format!("data/logs/fona-{}.log", now))
            .context(error::Log::Appender {
                name: "os_balloon::fona",
            })?;
        let fona_frames = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(pattern_exact)))
            .build(format!("data/logs/fona_frames-{}.log", now))
            .context(error::Log::Appender {
                name: "os_balloon::fona_frames",
            })?;
        let fona_logger = {
            let mut builder = Logger::builder()
                .appender("fona")
                .appender("fona_frames")
                .additive(false);
            if CONFIG.debug() {
                builder = builder.appender("fona_serial");
            }
            builder.build("os_balloon::fona", log_level)
        };
        let config = config
            .appender(
                Appender::builder()
                    .filter(Box::new(ThresholdFilter::new(LevelFilter::Info)))
                    .build("fona", Box::new(fona)),
            )
            .appender(
                Appender::builder()
                    .filter(Box::new(DebugFilter))
                    .build("fona_frames", Box::new(fona_frames)),
            )
            .logger(fona_logger);
        if CONFIG.debug() {
            let gsm_serial = FileAppender::builder()
                .encoder(Box::new(PatternEncoder::new(pattern_exact)))
                .build(format!("data/logs/fona_serial-{}.log", now))
                .context(error::Log::Appender {
                    name: "fona_serial",
                })?;
            config.appender(
                Appender::builder()
                    .filter(Box::new(TraceFilter))
                    .build("fona_serial", Box::new(gsm_serial)),
            )
        } else {
            config
        }
    };
    #[cfg(feature = "telemetry")]
    let config = {
        let telemetry = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(pattern_exact)))
            .build(format!("data/logs/telemetry-{}.log", now))
            .context(error::Log::Appender { name: "telemetry" })?;
        let telemetry_frames = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(pattern_exact)))
            .build(format!("data/logs/telemetry_frames-{}.log", now))
            .context(error::Log::Appender {
                name: "telemetry_frames",
            })?;
        let telemetry_logger = {
            let mut builder = Logger::builder()
                .appender("telemetry")
                .appender("telemetry_frames")
                .additive(false);
            if CONFIG.debug() {
                builder = builder.appender("telemetry_serial");
            }
            builder.build("os_balloon::telemetry", log_level)
        };
        let config = config
            .appender(
                Appender::builder()
                    .filter(Box::new(ThresholdFilter::new(LevelFilter::Info)))
                    .build("telemetry", Box::new(telemetry)),
            )
            .appender(
                Appender::builder()
                    .filter(Box::new(DebugFilter))
                    .build("telemetry_frames", Box::new(telemetry_frames)),
            )
            .logger(telemetry_logger);
        if CONFIG.debug() {
            let telemetry_serial = FileAppender::builder()
                .encoder(Box::new(PatternEncoder::new(pattern_exact)))
                .build(format!("data/logs/telemetry_serial-{}.log", now))
                .context(error::Log::Appender {
                    name: "telemetry_serial",
                })?;
            config.appender(
                Appender::builder()
                    .filter(Box::new(TraceFilter))
                    .build("telemetry_serial", Box::new(telemetry_serial)),
            )
        } else {
            config
        }
    };
    let config = config
        .build(
            Root::builder()
                .appender("stdout")
                .appender("main")
                .build(LevelFilter::Info),
        )
        .context(error::Log::Build)?;
    Ok(log4rs::init_config(config)?)
}