use std::sync::{Arc, Mutex};

use crate::{
  command::{CommandClause, Commander, PsdkCommand},
  types::psdk_const,
};

use super::{ArgContext, PsdkArg, PsdkArgBase, PsdkEasyArg};

#[derive(Clone, Debug, Default)]
pub struct Raw {
  commander: Commander,
  newline: bool,
  ctx: Arc<Mutex<ArgContext>>,
}

impl Raw {
  pub fn new() -> Self {
    Self::default()
  }

  pub fn text(text: impl AsRef<str>) -> Self {
    let mut raw = Self::new();
    raw.push_text(text).push_newline();
    raw
  }

  pub fn text_with_charset(text: impl AsRef<str>, charset: impl AsRef<str>) -> Self {
    let mut raw = Self::new();
    raw
      .commander
      .push_text_with_charset_and_newline(text, charset, false);
    raw.push_newline();
    raw
  }

  pub fn binary(binary: impl AsRef<[u8]>) -> Self {
    let mut raw = Self::new();
    raw.push_binary(binary).push_newline();
    raw
  }
}

impl Raw {
  pub fn push_text(&mut self, text: impl AsRef<str>) -> &mut Self {
    self.commander.push_text_with_newline(text.as_ref(), false);
    self
  }

  pub fn push_binary(&mut self, binary: impl AsRef<[u8]>) -> &mut Self {
    self
      .commander
      .push_binary_with_newline(binary.as_ref(), false);
    self
  }

  pub fn push_newline(&mut self) -> &mut Self {
    self.newline = true;
    self
  }
}

impl PsdkEasyArg<Vec<u8>> for Raw {
  fn context(&self) -> std::sync::Arc<std::sync::Mutex<super::ArgContext>> {
    self.ctx.clone()
  }
}

impl PsdkArgBase<Vec<u8>> for Raw {
  fn header(&self) -> Vec<u8> {
    psdk_const::EMPTY_BYTES.to_vec()
  }

  fn clause(&self) -> crate::errors::PsdkFatherResult<crate::command::CommandClause> {
    let command = self.commander.command()?;
    if command.binary().is_empty() {
      return Ok(CommandClause::none());
    }
    Ok(CommandClause::with_binary(command.binary()))
  }
}

impl PsdkArg<Vec<u8>> for Raw {
  fn newline(&self) -> bool {
    self.newline
  }

  fn prepend(&self, arg: &impl PsdkArg<Vec<u8>>) -> crate::errors::PsdkFatherResult<&Self> {
    <Self as PsdkEasyArg<Vec<u8>>>::prepend(self, arg)
  }

  fn append(&self, arg: &impl PsdkArg<Vec<u8>>) -> crate::errors::PsdkFatherResult<&Self> {
    <Self as PsdkEasyArg<Vec<u8>>>::append(self, arg)
  }

  fn clauses(&self) -> crate::errors::PsdkFatherResult<Vec<crate::command::CommandClause>> {
    <Self as PsdkEasyArg<Vec<u8>>>::clauses(self)
  }

  fn clear(&self) -> crate::errors::PsdkFatherResult<()> {
    <Self as PsdkEasyArg<Vec<u8>>>::clear(self)
  }
}
