use crate::prelude::*;

use super::{ExpressionType, FResult, FunctionTypeTrait};

#[derive(Debug, Default)]
pub(super) struct ToLower {}

impl ToLower
{
  fn call_impl(string: String) -> FResult<String>
  {
    Ok(string.to_lowercase())
  }
}

super::declare_function!(tolower, ToLower, call_impl(String) -> String, null_handling(null_returns_null));

#[derive(Debug, Default)]
pub(super) struct ToUpper {}

impl ToUpper
{
  fn call_impl(string: String) -> FResult<String>
  {
    Ok(string.to_uppercase())
  }
}

super::declare_function!(toupper, ToUpper, call_impl(String) -> String, null_handling(null_returns_null));

#[derive(Debug, Default)]
pub(super) struct LTrim {}

impl LTrim
{
  fn call_impl(string: String) -> FResult<String>
  {
    Ok(string.trim_start().to_string())
  }
}

super::declare_function!(ltrim, LTrim, call_impl(String) -> String, null_handling(null_returns_null));

#[derive(Debug, Default)]
pub(super) struct RTrim {}

impl RTrim
{
  fn call_impl(string: String) -> FResult<String>
  {
    Ok(string.trim_end().to_string())
  }
}

super::declare_function!(rtrim, RTrim, call_impl(String) -> String, null_handling(null_returns_null));

#[derive(Debug, Default)]
pub(super) struct Trim {}

impl Trim
{
  fn call_impl(string: String) -> FResult<String>
  {
    Ok(string.trim().to_string())
  }
}

super::declare_function!(trim, Trim, call_impl(String) -> String, null_handling(null_returns_null));

#[derive(Debug, Default)]
pub(super) struct Reverse {}

impl Reverse
{
  fn call_impl(string: String) -> FResult<String>
  {
    Ok(string.chars().rev().collect())
  }
}

super::declare_function!(reverse, Reverse, call_impl(String) -> String, null_handling(null_returns_null));

#[derive(Debug, Default)]
pub(super) struct Split {}

impl Split
{
  fn call_impl(string: String, pat: String) -> FResult<Vec<graphcore::Value>>
  {
    Ok(string.split(&pat).map(|x: &str| x.into()).collect())
  }
}

super::declare_function!(
  split, Split, call_impl(String, String) -> String,
  null_handling(null_returns_null, ignore, ignore)
);

#[derive(Debug, Default)]
pub(super) struct Substring {}

impl Substring
{
  fn call_impl(string: String, start: i64, length: i64) -> FResult<String>
  {
    use substring::Substring;
    Ok(
      string
        .substring(start as usize, length as usize)
        .to_string(),
    )
  }
}

super::declare_function!(
  substring, Substring, call_impl(String, i64, i64) -> String,
  null_handling(null_returns_null, ignore, ignore),
  validate_args(ExpressionType::String | ExpressionType::Null, ExpressionType::Integer, ExpressionType::Integer),
  default_args(2, i64::MAX)
);

#[derive(Debug, Default)]
pub(super) struct ToString {}

impl ToString
{
  fn call_impl(value: value::Value) -> FResult<String>
  {
    match value
    {
      value::Value::Boolean(b) => Ok(if b { "true" } else { "false" }.into()),
      value::Value::Integer(i) => Ok(i.to_string()),
      value::Value::Float(f) => Ok(f.to_string()),
      value::Value::String(s) => Ok(s.to_owned()),
      _ => Err(RunTimeError::InvalidArgument {
        function_name: "toString",
        index: 0,
        expected_type: "boolean or integer or double",
        value: format!("{:?}", value),
      }),
    }
  }
}

super::declare_function!(
  tostring, ToString,
  call_impl(crate::value::Value) -> String,
  null_handling(null_returns_null),
  validate_args(ExpressionType::Boolean | ExpressionType::Integer | ExpressionType::Float | ExpressionType::String)
);
