use crate::deep::*;
use crate::config::*;

macro_rules! len_err{
    ($str:literal, $a:expr, $b:expr $(,)?) => {{
        if $a != $b{
            return Err(anyhow!( $str, $a, $b ))
        }

    }}
}

/*
impl ParLayer for DenseLayer{
    castable!(from ParLayer);

    fn dim(&self) -> (usize, usize){
        (self.neurons[0].w.len(), self.neurons.len())
    }

    fn predict(&self, i: &[FLOAT]) -> Result<Vec<FLOAT>>{
        len_err!(
            "Unable to predict for input vector of wrong lenth {}, expected {}",
            i.len(),
            self.neurons[0].w.len(),
        );

        Ok(
            (0..self.neurons.len()).collect::<Vec<_>>().par_iter().map(|n| {
                self.neurons[*n].predict(&mut i.iter().map(|a|*a))
            }).collect::<Vec<_>>()
        )
    }

    fn backpropagate<const I_LEN: usize, const O_LEN: usize>(&mut self, i: &[FLOAT], gradient: &[FLOAT]) -> Result<Vec<FLOAT>>{
        let lr = self.lr;
        let matrix = self.neurons.par_iter_mut().enumerate().map(|(n, neuron)|{
            neuron
                .backpropagate(&mut i.iter().map(|n|*n), gradient[n], lr)
                .context(format!("Error in neuron {}", n))
        }).collect::<Vec<_>>();

        let mut buffer: Vec<FLOAT> = vec![0.0;self.buffer.0.len()];

        for i in matrix{
            let i = i?;
            for j in 0..i.len(){
                buffer[j] += i[j]
            }
        }

        Ok(buffer)
    }
}

impl DenseLayer{
    pub fn random_par(i: usize, o: usize, lr: FLOAT) -> Box<dyn ParLayer>{
        Box::new(Self{
            buffer: (
                        vec![0.0;i].into_boxed_slice(),
                        vec![0.0;o].into_boxed_slice(),
            ),

            neurons: (0..o)
                .collect::<Vec<_>>()
                .par_iter().map(|_| Neuron::random(i))
                .collect::<Vec<_>>()
                .into_boxed_slice(),

            lr: lr
        })
    }
}

pub struct DNN(pub Vec<Box<dyn ParLayer>>);

impl Layer for DNN{
    fn dim(&self) -> (usize,usize){
        (self.0[0].dim().0, self.0[self.0.len()-1].dim().1)
    }

    fn predict(&self, i: &[FLOAT]) -> Result<Box<[FLOAT]>>{
        let mut tmp = self.0[0].predict(i).context("Error in first layer while predict")?;
        for l in 1..self.0.len(){
            tmp = self.0[l].predict(&tmp[..]).context(format!("Error in layer {} while predicting", l))?
        }

        Ok(tmp.into_boxed_slice())
    }

    fn backpropagate(&mut self, i: &[FLOAT], gradient: &[FLOAT]) -> Result<Box<[FLOAT]>>{
        let mut inputs: Vec<Vec<FLOAT>> = vec![vec![];self.0.len()];
        inputs[0] = Vec::from(i);

        for l in 1..self.0.len(){ // DOES NOT PREDICT FOR LAST LAYER, AS IT IS NOT STRICTLY NEEDED FOR BACK PROPAGATION
            inputs[l] = self.0[l-1].predict(&inputs[l-1][..]).context(format!("Error in layer {} while propagating", l))?
        }

        let end = self.0.len() - 1;
        let mut tmp = self.0[end].backpropagate(&inputs[end][..], gradient).context("Error in last layer while propagating")?;

        for l in 1..self.0.len(){
            let l = self.0.len() - l - 1;
            tmp = self.0[l].backpropagate(&inputs[l][..], &tmp[..]).context(format!("Error in layer {} while propagating", l))?
        }

        Ok(tmp.into_boxed_slice())
    }
}

impl ParLayer for DNN{
    castable!(from ParLayer);

    fn dim(&self) -> (usize,usize){
        (self.0[0].dim().0, self.0[self.0.len()-1].dim().1)
    }

    fn predict(&self, i: &[FLOAT]) -> Result<Vec<FLOAT>>{
        let mut tmp = self.0[0].predict(i).context("Error in first layer while predict")?;
        for l in 1..self.0.len(){
            tmp = self.0[l].predict(&tmp[..]).context(format!("Error in layer {} while predicting", l))?
        }

        Ok(tmp)
    }

    fn backpropagate(&mut self, i: &[FLOAT], gradient: &[FLOAT]) -> Result<Vec<FLOAT>>{
        let mut inputs: Vec<Vec<FLOAT>> = vec![vec![];self.0.len()];
        inputs[0] = Vec::from(i);

        for l in 1..self.0.len(){ // DOES NOT PREDICT FOR LAST LAYER, AS IT IS NOT STRICTLY NEEDED FOR BACK PROPAGATION
            inputs[l] = self.0[l-1].predict(&inputs[l-1][..]).context(format!("Error in layer {} while propagating", l))?
        }

        let end = self.0.len() - 1;
        let mut tmp = self.0[end].backpropagate(&inputs[end][..], gradient).context("Error in last layer while propagating")?;

        for l in 1..self.0.len(){
            let l = self.0.len() - l - 1;
            tmp = self.0[l].backpropagate(&inputs[l][..], &tmp[..]).context(format!("Error in layer {} while propagating", l))?
        }

        Ok(tmp)
    }
}*/

#[macro_export]
macro_rules! buffer_dnn{
    ($dnn: expr, $i: expr, [$($to: tt <- $l: tt),* $(,)?]) => {{
        let mut buffer = ($({
            $l as usize;
            vec![].into_boxed_slice()
        }),*);

        let mut tmp = $dnn.0[0].predict($i).context("Error in first layer while predict")?;
        $(if $l == 0{
            buffer.$to = tmp.clone();
        })*

        for l in 1..$dnn.0.len(){
            tmp = $dnn.0[l].predict(&tmp[..]).context(format!("Error in layer {} while predicting", l))?;
            $(if $l == l{
                buffer.$to = tmp.clone();
            })*
        }
        let ok: Result<_> = Ok(buffer);
        ok
    }}
}

/*
impl ParLayer for Tanh{
    castable!(from ParLayer);

    fn dim(&self) -> (usize, usize){
        (0,0)
    }
    fn predict(&self, i: &[FLOAT]) -> Result<Vec<FLOAT>>{
        Ok(i.par_iter().map(|i|i.tanh()).collect())
    }
    fn backpropagate(&mut self, i: &[FLOAT], gradient: &[FLOAT]) -> Result<Vec<FLOAT>>{
        Ok(i.par_iter().zip(gradient).map(|(d, i)| -i.tanh().powi(2)*d ).collect())
    }
}*/
