use {
    r3vi::{
        view::{
            View, ViewPort,
            InnerViewPort, Observer, OuterViewPort,
            ObserverBroadcast,
            sequence::*,
            list::*
        },
        buffer::{vec::*}
    },
    crate::{
        editors::integer::{
            PositionalUInt
        },
        repr_tree::{ReprTree, ReprLeaf}
    },
    std::sync::{Arc, RwLock},
};

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

pub trait PosIntProjections {
    fn transform_radix(&self, dst_radix: u64) -> OuterViewPort<dyn SequenceView<Item = u64>>;
//    fn to_digits(&self) -> OuterViewPort<dyn SequenceView<Item = usize>>;
}

impl PosIntProjections for OuterViewPort<dyn PositionalUInt> {
    fn transform_radix(&self, dst_radix: u64) -> OuterViewPort<dyn SequenceView<Item = u64>> {
        let port = ViewPort::<dyn SequenceView<Item = u64>>::new();
        port.add_update_hook(Arc::new(self.0.clone()));

//        let mut vec_port = ViewPort::new();
        let proj = Arc::new(RwLock::new(RadixProjection {
            src: None,
            dst_radix,
            dst_digits: VecBuffer::new(),
            cast: port.inner().get_broadcast()
        }));

        self.add_observer(proj.clone());
        port.set_view(Some(proj as Arc<dyn SequenceView<Item = u64>>));
        port.into_outer()
    }
/*
    fn to_digits(&self) -> OuterViewPort<dyn SequenceView<Item = usize>> {
        let port = ViewPort::new();
        port.add_update_hook(Arc::new(self.0.clone()));
        let proj = Arc::new(RwLock::new(PosUIntToDigits {
            src: None,
            cast: port.inner().get_broadcast()
        }));
        self.add_observer(proj.clone());
        port.inner().set_view(Some(proj));
        port.into_outer()
    }
    */
}

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

pub struct RadixProjection {
    src: Option<Arc<dyn PositionalUInt>>,
    dst_radix: u64,
    dst_digits: VecBuffer<u64>,
    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = u64>>>>
}

impl View for RadixProjection {
    type Msg = usize;
}

impl SequenceView for RadixProjection {
    type Item = u64;

    fn get(&self, idx: &usize) -> Option<u64> {
        if *idx < self.dst_digits.len() {
            Some(self.dst_digits.get(*idx))
        } else {
            None
        }
    }

    fn len(&self) -> Option<usize> {
        Some(self.dst_digits.len())
    }
}

impl PositionalUInt for RadixProjection {
    fn get_radix(&self) -> u64 {
        self.dst_radix
    }
}

impl Observer< dyn PositionalUInt > for RadixProjection {
    fn reset(&mut self, view: Option<Arc<dyn PositionalUInt>>) {
        self.src = view;
        self.update();
    }

    fn notify(&mut self, idx: &usize) {
        self.update();
        // self.update_digit(idx)
    }
}

impl RadixProjection {
    /// recalculate everything
    fn update(&mut self) {
//       let mut dst = self.dst_digits;
        let old_len = self.dst_digits.len();
        self.dst_digits.clear();

        if let Some(src) = self.src.as_ref() {
            let mut val = src.get_value();
            while val > 0 {
                self.dst_digits.push(val % self.dst_radix);
                val /= self.dst_radix;
            }
        }

        let new_len = self.dst_digits.len();
        for i in 0 .. usize::max(old_len, new_len) {
             self.cast.write().unwrap().notify(&i);
        }
    }

    fn _update_dst_digit(&mut self, _idx: usize) {
        /*
                let v = 0; // calculate new digit value

                // which src-digits are responsible?

                if idx < self.dst_digits.len() {
                    self.dst_digits.get_mut(idx) = v;
                } else if idx == self.dst_digits.len() {
                    self.dst_digits.push(v);
                } else {
                    // error
                }
        */
    }
}