use std::process::exit;
use image::DynamicImage;
use showie::Trim;
use crate::{cli::Args, list::List, Data};
const DEFAULT_SHINY_RATE: u32 = 8192;
#[derive(PartialEq, Eq)]
pub enum Region {
Kanto,
Johto,
Hoenn,
Sinnoh,
Unova,
Kalos,
Alola,
Galar,
}
#[derive(PartialEq, Eq)]
pub enum Selection {
Random,
Region(Region),
DexId(usize),
Name(String),
}
impl Selection {
pub fn parse(arg: String) -> Self {
if let Ok(dex_id) = arg.parse::<usize>() {
match dex_id {
0 => Selection::Random,
id if (id > 0) => Selection::DexId(id - 1),
_ => Selection::Name(arg),
}
} else {
match arg.to_lowercase().as_str() {
"random" => Selection::Random,
"kanto" => Selection::Region(Region::Kanto),
"johto" => Selection::Region(Region::Johto),
"hoenn" => Selection::Region(Region::Hoenn),
"sinnoh" => Selection::Region(Region::Sinnoh),
"unova" => Selection::Region(Region::Unova),
"kalos" => Selection::Region(Region::Kalos),
"alola" => Selection::Region(Region::Alola),
"galar" => Selection::Region(Region::Galar),
_ => Selection::Name(arg),
}
}
}
pub fn eval(self, list: &List) -> String {
match self {
Selection::Random => list.random(),
Selection::Region(region) => list.get_by_region(region),
Selection::DexId(id) => list
.get_by_id(id)
.unwrap_or_else(|| {
eprintln!("{} is not a valid pokedex ID", id + 1);
exit(1)
})
.clone(),
Selection::Name(name) => name,
}
}
}
pub struct Pokemon<'a> {
pub path: String,
pub name: String,
pub sprite: DynamicImage,
pub attributes: &'a Attributes,
}
impl<'a> Pokemon<'a> {
pub fn new(arg: String, list: &List, attributes: &'a Attributes) -> Self {
let selection = Selection::parse(arg);
let is_random = selection == Selection::Random;
let is_region = matches!(selection, Selection::Region(_));
let name = selection.eval(list);
let path = attributes.path(&name, is_random, is_region);
let bytes = Data::get(&path)
.unwrap_or_else(|| {
eprintln!("pokemon not found");
exit(1)
})
.data
.into_owned();
let sprite = image::load_from_memory(&bytes).unwrap().trim();
Self {
path,
name: list.format_name(&name),
sprite,
attributes,
}
}
}
pub struct Attributes {
pub form: String,
pub female: bool,
pub shiny: bool,
}
impl Attributes {
fn rate_is_shiny() -> bool {
let rate = match std::env::var("POKEGET_SHINY_RATE")
.map_err(|_| false)
.and_then(|x| x.parse::<u32>().map_err(|_| true))
{
Ok(rate) => rate.max(1),
Err(notify) => {
if notify {
eprintln!("POKEGET_SHINY_RATE was improperly formatted, using default rate")
}
DEFAULT_SHINY_RATE
}
};
0 == rand::random_range(0..rate)
}
pub fn new(args: &Args) -> Self {
let mut form = match args {
Args { mega: true, .. } => "mega",
Args { mega_x: true, .. } => "mega-x",
Args { mega_y: true, .. } => "mega-y",
Args { alolan: true, .. } => "alola",
Args { gmax: true, .. } => "gmax",
Args { hisui: true, .. } => "hisui",
Args { galar: true, .. } => "galar",
_ => &args.form,
}
.to_string();
if args.noble {
form.push_str("-noble");
}
Self {
form,
female: args.female,
shiny: args.shiny || Self::rate_is_shiny(),
}
}
pub fn path(&self, name: &str, random: bool, region: bool) -> String {
let mut filename = name.to_owned();
let is_random = random || region;
if !self.form.is_empty() && !is_random {
filename.push_str(&format!("-{}", self.form));
}
filename = filename
.replace([' ', '_'], "-")
.replace(['.', '\'', ':'], "")
.to_lowercase();
let path = format!(
"{}/{}{}.png",
if self.shiny { "shiny" } else { "regular" },
if self.female && !is_random {
"female/"
} else {
""
}, filename.trim()
);
path
}
}