device 0.0.4

A generative engine
use {
  super::*,
  clap::{
    ArgAction,
    builder::styling::{AnsiColor, Effects, Styles},
  },
};

const AUDIO: &str = "audio";

#[derive(Clone, Default, Parser)]
#[command(
  version,
  styles = Styles::styled()
    .error(AnsiColor::Red.on_default() | Effects::BOLD)
    .header(AnsiColor::Yellow.on_default() | Effects::BOLD)
    .invalid(AnsiColor::Red.on_default())
    .literal(AnsiColor::Blue.on_default())
    .placeholder(AnsiColor::Cyan.on_default())
    .usage(AnsiColor::Yellow.on_default() | Effects::BOLD)
    .valid(AnsiColor::Green.on_default())
)]
pub(crate) struct Options {
  #[arg(allow_hyphen_values = true, long)]
  pub(crate) db: Option<f32>,
  #[arg(long, action = ArgAction::SetTrue)]
  pub(crate) fit: Option<bool>,
  #[arg(long)]
  pub(crate) fps: Option<Fps>,
  #[arg(long)]
  pub(crate) height: Option<NonZeroU32>,
  #[arg(long)]
  pub(crate) image_format: Option<ImageFormat>,
  #[arg(group = AUDIO, long)]
  pub(crate) input: bool,
  #[arg(long)]
  pub(crate) interpolate: Option<bool>,
  #[arg(long)]
  pub(crate) mute: bool,
  #[arg(long)]
  pub(crate) preset: Option<Vec<Preset>>,
  #[arg(long)]
  pub(crate) program: Option<Program>,
  #[arg(long)]
  pub(crate) resolution: Option<NonZeroU32>,
  #[arg(long)]
  pub(crate) scene: Option<Scene>,
  #[arg(group = AUDIO, long)]
  pub(crate) score: Option<Score>,
  #[arg(long)]
  pub(crate) seed: Option<u64>,
  #[arg(group = AUDIO, long)]
  pub(crate) song: Option<String>,
  #[arg(group = AUDIO, long)]
  pub(crate) track: Option<Utf8PathBuf>,
  #[arg(long)]
  pub(crate) verbose: bool,
  #[arg(allow_hyphen_values = true, long)]
  pub(crate) vw: Option<f32>,
  #[arg(allow_hyphen_values = true, long)]
  pub(crate) vx: Option<f32>,
  #[arg(allow_hyphen_values = true, long)]
  pub(crate) vy: Option<f32>,
  #[arg(allow_hyphen_values = true, long)]
  pub(crate) vz: Option<f32>,
  #[arg(long)]
  pub(crate) width: Option<NonZeroU32>,
}

impl Options {
  pub(crate) fn add_source(&self, config: &Config, tap: &mut Tap) -> Result {
    if let Some(song) = &self.song {
      let wave = tap.load_wave(&config.find_song(song)?)?;
      tap.sequence_wave(&wave, 0.0, 0.0);
    } else if let Some(score) = self.score {
      score.sequence(tap);
    } else if let Some(track) = &self.track {
      let wave = tap.load_wave(track)?;
      tap.sequence_wave(&wave, 0.0, 0.0);
    } else if let Some(program) = self.program {
      program.add_source(config, tap)?;
    }

    Ok(())
  }

  pub(crate) fn image_format(&self) -> Option<ImageFormat> {
    self
      .image_format
      .or_else(|| self.scene.and_then(Scene::image_format))
      .or_else(|| {
        self
          .program
          .and_then(|program| program.scene().image_format())
      })
  }

  pub(crate) fn size(&self, size: Size) -> (Size, NonZeroU32) {
    let size = Size::new(self.width.unwrap_or(size.x), self.height.unwrap_or(size.y));
    let resolution = self.resolution.unwrap_or(size.x.max(size.y));
    (size, resolution)
  }

  pub(crate) fn state(&self) -> State {
    let mut state = if let Some(scene) = self.scene {
      scene.state(self.seed)
    } else if let Some(program) = self.program {
      let mut state = program.scene().state(self.seed);
      if let Some(db) = program.db() {
        state.db = db;
      }
      state
    } else {
      default()
    };

    if let Some(presets) = &self.preset {
      for preset in presets {
        state.filters.push(preset.filter());
      }
    }

    state.db = self.db.unwrap_or(state.db);
    state.fit = self.fit.unwrap_or(state.fit);
    state.interpolate = self.interpolate.unwrap_or(state.interpolate);
    state.velocity = Vec4f::new(
      self.vx.unwrap_or(state.velocity.x),
      self.vy.unwrap_or(state.velocity.y),
      self.vz.unwrap_or(state.velocity.z),
      self.vw.unwrap_or(state.velocity.w),
    );

    state
  }

  pub(crate) fn stdio(&self) -> Stdio {
    if self.verbose {
      Stdio::inherit()
    } else {
      Stdio::piped()
    }
  }
}