use std::path::{Path, PathBuf};
use std::str::FromStr;

use vfile::util::location::Location;

#[derive(Clone, Debug)]
pub enum OutputType {
    Location(Location),
    StdOut,
}

#[derive(Clone, Debug)]
pub enum OutputFormat {
    Extension(String),
    Auto,
}

#[derive(Clone, Debug)]
pub struct Output {
    pub output: OutputType,
    pub format: OutputFormat,
}

macro_rules! output_from {
    ($t:ty) => {
        impl From<$t> for Output {
            fn from(loc: $t) -> Self {
                Output {
                    output: OutputType::Location(loc.into()),
                    format: OutputFormat::Auto,
                }
            }
        }
    };
}

output_from!(&str);
output_from!(&String);
output_from!(String);
output_from!(&Path);
output_from!(&PathBuf);
output_from!(PathBuf);

impl From<Location> for Output {
    fn from(loc: Location) -> Self {
        Output {
            output: OutputType::Location(loc),
            format: OutputFormat::Auto,
        }
    }
}

impl From<&Location> for Output {
    fn from(loc: &Location) -> Self {
        Output {
            output: OutputType::Location(loc.clone()),
            format: OutputFormat::Auto,
        }
    }
}

impl FromStr for Output {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Output::from(s))
    }
}

macro_rules! output_format_from {
    ($t:ty) => {
        impl From<$t> for OutputFormat {
            fn from(ext: $t) -> Self {
                OutputFormat::Extension(ext.to_string())
            }
        }
    };
}

output_format_from!(&str);
output_format_from!(&String);
output_format_from!(String);

impl FromStr for OutputFormat {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(OutputFormat::from(s))
    }
}
