use log::warn;
use super::ansi_color::ANSIColor;
use std::error::Error;

/// Blinking state
#[derive(Debug)]
pub enum Blink {
  /// Blinking is disabled.
  Null, 

  /// Slow blinking.
  Slow,

  /// Fast blinking.
  Rapid,
}

/// Visitor trait for `ANSIParser`.
///
/// This trait must be implemented by any struct that want to be called back by
/// the `ANSIParser`.
pub trait ANSIVisitor {

  /// Called at the start of the parsing.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_begin(&mut self) -> Result<(), Box<dyn Error>> { Ok(()) }

  /// Called at the end of the parsing.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_end(&mut self) -> Result<(), Box<dyn Error>> { Ok(()) }

  /// Called when a *Fe Escape SS2* (Single Shift Two) is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_fe_esc_ss2(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled SS2 escape code.");
    Ok(())
  }

  /// Called when a *Fe Escape SS3* (Single Shift Three) is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_fe_esc_ss3(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled SS3 escape code.");
    Ok(())
  }

  /// Called when one of the following *Fe Escape string* command/data is met:
  ///  * DCS (Device Control String).
  ///  * OSC (Operating System Command).
  ///  * SOS (Start of String).
  ///  * PM (Privacy Message).
  ///  * APC (Application Program Command).
  ///
  /// The escape code is passed as an argument as well as the string.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_fe_esc_string(&mut self, code: u8, s: &str
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled Fe Escape 0x{code:02x} with string \"{s}\".");
    Ok(())
  }

  /// Called when an *unknown Fe Escape code* is met.
  ///
  /// The escape code is passed as an argument.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_fe_esc_unknown(&mut self, code: u8) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled Fe Escape code 0x{code:02x}.");
    Ok(())
  }

  /// Called when a *Fe Escape CSI sequence* is met.
  ///
  /// The character code (i.e.: the character that ends the CSI) of the CSI is
  /// passed as an argument as well as the whole CSI string.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_csi_command(&mut self, _code: char, _s: &str
    ) -> Result<(), Box<dyn Error>> { Ok(()) }

  /// Called when a *Fe Escape CSI unknown code* is met.
  ///
  /// The unknown character code is passed as an argument as well as the whole
  /// CSI String.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_unknown_csi_sgr_code(&mut self, code: u8, s: &str
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unknown CSI SGR code {code}. Full string was \"{s}\".");
    Ok(())
  }

  /// Called when a piece of string is visited.
  ///
  /// The text is passed as an argument string.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_text(&mut self, s: &str) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled normal text \"{s}\".");
    Ok(())
  }

  /// Called when a *reset* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_reset(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled reset.");
    Ok(())
  }

  /// Called when a *FG color reset* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_reset_fg_color(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled FG color reset.");
    Ok(())
  }

  /// Called when a *BG color reset* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_reset_bg_color(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled BG color reset.");
    Ok(())
  }

  /// Called when an *underline color reset* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_reset_underline_color(&mut self
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled underline reset.");
    Ok(())
  }

  /// Called when a *set/unset bold* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_bold(&mut self, bold: bool) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled bold {}.", if bold {"setting"} else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset faint* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_faint(&mut self, faint: bool) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled faint {}.", if faint {"setting"} else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset italic* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_italic(&mut self, italic: bool) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled italic {}.", if italic {"setting"} else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset underline* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_underline(&mut self, underline: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled underline {}.", if underline {"setting"}
      else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset blink* is met.
  ///
  /// Blinking may be set with two different speed, hence the enum type
  /// [`Blink`].
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_blink(&mut self, blink: Blink) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled blink setting to {blink:?}.");
    Ok(())
  }

  /// Called when a *set/unset invert* is met.
  ///
  /// *Invert* (or *reverse video*) is the swaping of foreground and background
  /// colors.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_invert(&mut self, invert: bool) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled invert {}.", if invert {"setting"} else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset concealed* is met.
  ///
  /// *Concealed* (or *hide*) means the text must not be displayed.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_concealed(&mut self, concealed: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled concealed {}.", if concealed {"setting"}
      else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset crossed-out* is met.
  ///
  /// *Crossed-out* means the stroking of a text.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_crossed_out(&mut self, crossed_out: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled crossed-out {}.", if crossed_out {"setting"}
      else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset superscript* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_superscript(&mut self, superscript: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled superscript {}.", if superscript {"setting"}
      else {"unsetting"});
    Ok(())
  }

  /// Called when a *set/unset subscript* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_subscript(&mut self, subscript: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled subscript {}.", if subscript {"setting"}
      else {"unsetting"});
    Ok(())
  }

  /// Called when a *set default font* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_default_font(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled default font setting.");
    Ok(())
  }

  /// Called when an *set alternative font* is met.
  ///
  /// The number of the alternative font is passed as an argument.
  /// Index goes from 1 to 9.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_alternative_font(&mut self, n: u8) -> Result<(), Box<dyn Error>>
  {
    warn!("Unhandled alternative font #{n} setting.");
    Ok(())
  }

  /// Called when a *set gothic* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_gothic(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled gothic setting.");
    Ok(())
  }

  /// Called when a *set doubly underlined* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_doubly_underlined(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled doubly underlined setting.");
    Ok(())
  }

  /// Called when a *set proportional spacing* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_proportional_spacing(&mut self, proportional_spacing: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled proportional spacing {}.", if proportional_spacing
      {"setting"} else {"unsetting"});
    Ok(())
  }

  /// Called when a *set framed* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_framed(&mut self, framed: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled framed {}.", if framed {"setting"} else {"unsetting"});
    Ok(())
  }

  /// Called when a *set encircled* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_encircled(&mut self, encircled: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled encircled {}.", if encircled {"setting"}
      else {"unsetting"});
    Ok(())
  }

  /// Called when a *set overlined* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_overlined(&mut self, overlined: bool
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled overlined {}.", if overlined {"setting"}
      else {"unsetting"});
    Ok(())
  }

  /// Called when a *set FG color* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_fg_color(&mut self, color: ANSIColor
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled FG color setting, with color {color}.");
    Ok(())
  }

  /// Called when a *set BG color* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_bg_color(&mut self, color: ANSIColor
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled BG color setting, with color {color}.");
    Ok(())
  }

  /// Called when a *set underline color* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_underline_color(&mut self, color: ANSIColor
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled underline color setting, with color {color}.");
    Ok(())
  }

  /// Called when a *set ideogram underline* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_ideogram_underline(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled ideogram underline setting.");
    Ok(())
  }

  /// Called when a *set ideogram double underline* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_ideogram_double_underline(&mut self
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled ideogram double underline setting.");
    Ok(())
  }

  /// Called when a *set ideogram overline* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_ideogram_overline(&mut self) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled ideogram overline setting.");
    Ok(())
  }

  /// Called when a *set ideogram double overline* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_ideogram_double_overline(&mut self
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled ideogram double overline setting.");
    Ok(())
  }

  /// Called when a *set ideogram stress marking* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_set_ideogram_stress_marking(&mut self
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled ideogram stress marking setting.");
    Ok(())
  }

  /// Called when a *reset ideogram attributes* is met.
  ///
  /// # Errors
  /// Any error may be thrown by the visitor.
  fn visit_reset_ideogram_attributes(&mut self
    ) -> Result<(), Box<dyn Error>> {
    warn!("Unhandled ideogram attributes resetting.");
    Ok(())
  }
}
