use super::result::{CQLValue, Row};
use std::net::IpAddr;
pub trait FromCQLVal<T>: Sized {
    fn from_cql(cql_val: T) -> Result<Self, FromCQLValError>;
}
#[derive(Debug, PartialEq, Eq)]
pub enum FromCQLValError {
    BadCQLType,
    ValIsNull,
}
pub trait FromRow: Sized {
    fn from_row(row: Row) -> Result<Self, FromRowError>;
}
#[derive(Debug, PartialEq, Eq)]
pub enum FromRowError {
    BadCQLVal(FromCQLValError),
    RowTooShort,
}
impl std::fmt::Display for FromCQLValError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}
impl std::fmt::Display for FromRowError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}
impl std::error::Error for FromCQLValError {}
impl std::error::Error for FromRowError {}
impl From<FromCQLValError> for FromRowError {
    fn from(from_cql_err: FromCQLValError) -> FromRowError {
        FromRowError::BadCQLVal(from_cql_err)
    }
}
impl<T: FromCQLVal<CQLValue>> FromCQLVal<Option<CQLValue>> for T {
    fn from_cql(cql_val_opt: Option<CQLValue>) -> Result<Self, FromCQLValError> {
        T::from_cql(cql_val_opt.ok_or(FromCQLValError::ValIsNull)?)
    }
}
impl<T: FromCQLVal<CQLValue>> FromCQLVal<Option<CQLValue>> for Option<T> {
    fn from_cql(cql_val_opt: Option<CQLValue>) -> Result<Self, FromCQLValError> {
        match cql_val_opt {
            Some(cql_val) => Ok(Some(T::from_cql(cql_val)?)),
            None => Ok(None),
        }
    }
}
macro_rules! impl_from_cql_val {
    ($T:ty, $convert_func:ident) => {
        impl FromCQLVal<CQLValue> for $T {
            fn from_cql(cql_val: CQLValue) -> Result<$T, FromCQLValError> {
                cql_val.$convert_func().ok_or(FromCQLValError::BadCQLType)
            }
        }
    };
}
impl_from_cql_val!(i32, as_int); 
impl_from_cql_val!(i64, as_bigint); 
impl_from_cql_val!(String, into_string); 
impl_from_cql_val!(IpAddr, as_inet); 
impl<T: FromCQLVal<CQLValue>> FromCQLVal<CQLValue> for Vec<T> {
    fn from_cql(cql_val: CQLValue) -> Result<Self, FromCQLValError> {
        cql_val
            .into_vec()
            .ok_or(FromCQLValError::BadCQLType)?
            .into_iter()
            .map(T::from_cql)
            .collect::<Result<Vec<T>, FromCQLValError>>()
    }
}
macro_rules! impl_tuple_from_row {
    ( $($Ti:tt),+ ) => {
        impl<$($Ti),+> FromRow for ($($Ti,)+)
        where
            $($Ti: FromCQLVal<Option<CQLValue>>),+
        {
            fn from_row(row: Row) -> Result<Self, FromRowError> {
                let mut vals_iter = row.columns.into_iter();
                Ok((
                    $(
                        $Ti::from_cql(vals_iter
                                      .next()
                                      .ok_or(FromRowError::RowTooShort) ?
                                     ) ?
                    ,)+
                ))
            }
        }
    }
}
impl_tuple_from_row!(T1);
impl_tuple_from_row!(T1, T2);
impl_tuple_from_row!(T1, T2, T3);
impl_tuple_from_row!(T1, T2, T3, T4);
impl_tuple_from_row!(T1, T2, T3, T4, T5);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
impl_tuple_from_row!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
#[cfg(test)]
mod tests {
    use super::{CQLValue, FromCQLVal, FromCQLValError, FromRow, FromRowError, Row};
    use crate as scylla;
    use crate::macros::FromRow;
    use std::net::{IpAddr, Ipv4Addr};
    #[test]
    fn i32_from_cql() {
        assert_eq!(Ok(1234), i32::from_cql(CQLValue::Int(1234)));
    }
    #[test]
    fn i64_from_cql() {
        assert_eq!(Ok(1234), i64::from_cql(CQLValue::BigInt(1234)));
    }
    #[test]
    fn string_from_cql() {
        assert_eq!(
            Ok("ascii_test".to_string()),
            String::from_cql(CQLValue::Ascii("ascii_test".to_string()))
        );
        assert_eq!(
            Ok("text_test".to_string()),
            String::from_cql(CQLValue::Text("text_test".to_string()))
        );
    }
    #[test]
    fn ip_addr_from_cql() {
        let ip_addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
        assert_eq!(Ok(ip_addr), IpAddr::from_cql(CQLValue::Inet(ip_addr)));
    }
    #[test]
    fn vec_from_cql() {
        let cql_val = CQLValue::Set(vec![CQLValue::Int(1), CQLValue::Int(2), CQLValue::Int(3)]);
        assert_eq!(Ok(vec![1, 2, 3]), Vec::<i32>::from_cql(cql_val));
    }
    #[test]
    fn tuple_from_row() {
        let row = Row {
            columns: vec![
                Some(CQLValue::Int(1)),
                Some(CQLValue::Text("some_text".to_string())),
                None,
            ],
        };
        let (a, b, c) = <(i32, Option<String>, Option<i64>)>::from_row(row).unwrap();
        assert_eq!(a, 1);
        assert_eq!(b, Some("some_text".to_string()));
        assert_eq!(c, None);
        let row2 = Row {
            columns: vec![Some(CQLValue::Int(1)), Some(CQLValue::Int(2))],
        };
        let (d,) = <(i32,)>::from_row(row2).unwrap();
        assert_eq!(d, 1);
    }
    #[test]
    fn from_cql_null() {
        assert_eq!(i32::from_cql(None), Err(FromCQLValError::ValIsNull));
    }
    #[test]
    fn from_cql_wrong_type() {
        assert_eq!(
            i32::from_cql(CQLValue::BigInt(1234)),
            Err(FromCQLValError::BadCQLType)
        );
    }
    #[test]
    fn from_row_null() {
        let row = Row {
            columns: vec![None],
        };
        assert_eq!(
            <(i32,)>::from_row(row),
            Err(FromRowError::BadCQLVal(FromCQLValError::ValIsNull))
        );
    }
    #[test]
    fn from_row_wrong_type() {
        let row = Row {
            columns: vec![Some(CQLValue::Int(1234))],
        };
        assert_eq!(
            <(String,)>::from_row(row),
            Err(FromRowError::BadCQLVal(FromCQLValError::BadCQLType))
        );
    }
    #[test]
    fn from_row_too_short() {
        let row = Row {
            columns: vec![Some(CQLValue::Int(1234))],
        };
        assert_eq!(<(i32, i32)>::from_row(row), Err(FromRowError::RowTooShort));
    }
    #[test]
    fn struct_from_row() {
        #[derive(FromRow)]
        struct MyRow {
            a: i32,
            b: Option<String>,
            c: Option<Vec<i32>>,
        }
        let row = Row {
            columns: vec![
                Some(CQLValue::Int(16)),
                None,
                Some(CQLValue::Set(vec![CQLValue::Int(1), CQLValue::Int(2)])),
            ],
        };
        let my_row: MyRow = MyRow::from_row(row).unwrap();
        assert_eq!(my_row.a, 16);
        assert_eq!(my_row.b, None);
        assert_eq!(my_row.c, Some(vec![1, 2]));
    }
}