use {
    r3vi::{
        view::{
            InnerViewPort, OuterViewPort,
            sequence::*,
        },
        buffer::{
            vec::*
        },
        projection::projection_helper::*,
    },
    std::sync::{Arc, RwLock},
};

fn posint_add(
    radix: usize,
    a: impl SequenceView<Item = usize>,
    b: impl SequenceView<Item = usize>,
) -> Vec<usize> {
    let mut carry = 0;
    let mut result = Vec::new();

    for digit_idx in 0..std::cmp::max(a.len().unwrap_or(0), b.len().unwrap_or(0)) {
        let sum = a.get(&digit_idx).unwrap_or(0) + b.get(&digit_idx).unwrap_or(0) + carry;

        result.push(sum % radix);

        carry = if sum > radix { sum - radix } else { 0 };
    }

    if carry > 0 {
        result.push(carry);
    }

    result
}

pub struct Add {
    radix: usize,
    a: Arc<dyn SequenceView<Item = usize>>, // PosInt, Little Endian
    b: Arc<dyn SequenceView<Item = usize>>, // PosInt, Little Endian
    c: VecBuffer<usize>,
    _proj_helper: ProjectionHelper<usize, Self>,
}

impl Add {
    pub fn new(
        radix: usize,
        a: OuterViewPort<dyn SequenceView<Item = usize>>,
        b: OuterViewPort<dyn SequenceView<Item = usize>>,
        c: InnerViewPort<RwLock<Vec<usize>>>, //<dyn SequenceView<Item = usize>>
    ) -> Arc<RwLock<Self>> {
        let mut proj_helper = ProjectionHelper::new(c.0.update_hooks.clone());
        let add = Arc::new(RwLock::new(Add {
            radix,
            a: proj_helper.new_sequence_arg(0, a, |s: &mut Self, _digit_idx| s.update()),
            b: proj_helper.new_sequence_arg(1, b, |s: &mut Self, _digit_idx| s.update()),
            c: VecBuffer::with_port(c),
            _proj_helper: proj_helper,
        }));
        add
    }

    fn update(&mut self) {
        self.c.clear();
        for digit in posint_add(self.radix, self.a.clone(), self.b.clone()) {
            self.c.push(digit);
        }
    }
}