use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::path::PathBuf;

use clap::Parser;
use sbp::json::{CompactFormatter, HaskellishFloatFormatter};

use converters::{ErrorHandlerOptions, Result, json2json};

#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

/// Convert "compact" SBP JSON data to an "exploded" form
#[derive(Debug, Parser)]
#[clap(name = "json2json", verbatim_doc_comment, version = env!("VERGEN_GIT_DESCRIBE"))]
struct Options {
    /// Path to input file
    input: Option<PathBuf>,

    /// Path to output file
    output: Option<PathBuf>,

    /// Print debugging messages to standard error
    #[clap(long)]
    debug: bool,

    /// Try to be compatible with the float formatting of the Haskell version of sbp2json
    #[clap(long = "float-compat")]
    float_compat: bool,

    /// Buffer output before flushing
    #[clap(short, long)]
    buffered: bool,

    /// What to do if encounter an error while parsing
    #[clap(long, default_value = "skip")]
    error_handler: ErrorHandlerOptions,
}

fn main() -> Result<()> {
    let options = Options::parse();

    let mut log_builder = env_logger::Builder::from_default_env();
    if options.debug {
        log_builder.parse_filters("debug");
    }
    log_builder.init();

    let stdin: Box<dyn Read> = match options.input {
        Some(path) => Box::new(File::open(path)?),
        _ => Box::new(io::stdin().lock()),
    };

    let stdout: Box<dyn Write> = match options.output {
        Some(path) => Box::new(File::create(path)?),
        _ => Box::new(io::stdout().lock()),
    };

    if options.float_compat {
        json2json(
            stdin,
            stdout,
            HaskellishFloatFormatter {},
            options.buffered,
            options.error_handler,
        )
    } else {
        json2json(
            stdin,
            stdout,
            CompactFormatter {},
            options.buffered,
            options.error_handler,
        )
    }
}
