// SPDX-FileCopyrightText: 2023 Gleb Smirnov <glebsmirnov0708@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later

//! This module contains [`Parameters`] trait that allows to
//! statically validate parameter types.

use textpieces_foundations::{ParameterType, ParameterValue, ParameterValueType};

/// Trait for tuples of values convertable from parameter values.
pub trait ParametersTuple<const N_PARAMS: usize> {
    /// Converts slice of parameter values to a tuple of typed value.
    ///
    /// # Panics
    ///
    /// Should panic if parameter has invalid type.
    /// Since it's supposed to be used after type validation,
    /// panics here is something practically unreachable.
    fn from_parameters(parameters: &[ParameterValue]) -> Self;

    /// Returns list of parameter types corresponding to the tuple type.
    fn types_() -> [ParameterType; N_PARAMS];
}

/// Returns constant literal in form of `1 + 1 + 1 + ... + 1`
/// that represents number of passed identifier arguments.
macro_rules! count_args {
    ($arg:ident) => { 1 };
    (
        $first_arg:ident $(, $arg:ident)+
    ) => { count_args!($($arg),+) + 1 };
}

/// Implements [`Parameters`] trait for generic tuple of given generic types.
macro_rules! impl_for_tuples {
    ($first_arg:ident $(,$generic_arg:ident)*) => {
        impl<$first_arg: ParameterValueType $(, $generic_arg: ParameterValueType)*>
            ParametersTuple<{count_args!($first_arg$(, $generic_arg)*)}>
            for ($first_arg, $($generic_arg),*)
        {
            fn from_parameters(parameters: &[ParameterValue]) -> Self {
                #[allow(non_snake_case)]
                let [$first_arg $(,$generic_arg)*] = &parameters[..] else {
                    panic!("Invalid number of parameters passed");
                };

                (
                    $first_arg::from_parameter_value($first_arg).unwrap(),
                    $($generic_arg::from_parameter_value($generic_arg).unwrap()),*
                )
            }

            fn types_() -> [ParameterType; {count_args!($first_arg$(, $generic_arg)*)}] {
                [
                    $first_arg::TYPE
                    $(, $generic_arg::TYPE)*
                ]
            }
        }

        impl_for_tuples!($($generic_arg),*);
    };
    () => {};
}

impl_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);

impl ParametersTuple<0> for () {
    fn from_parameters(parameters: &[ParameterValue]) -> Self {
        assert!(parameters.is_empty(), "Invalid number of parameters passed");
    }

    fn types_() -> [ParameterType; 0] {
        []
    }
}
