use super::Map;
use super::NestedValue;
use super::Record;
use super::Value;

pub fn merge_seqs<N: NestedValue>(mut a: Vec<N>, mut b: Vec<N>) -> Option<Vec<N>> {
    if a.len() > b.len() {
        std::mem::swap(&mut a, &mut b);
    }
    let mut r = vec![];
    let mut bi = b.into_iter();
    for av in a.into_iter() {
        r.push(merge2(av, bi.next().unwrap())?);
    }
    r.extend(bi);
    Some(r)
}

pub fn merge2<N: NestedValue>(v: N, w: N) -> Option<N> {
    let (mut v_anns, v_val) = v.pieces();
    let (w_anns, w_val) = w.pieces();
    v_anns.modify(|anns| anns.extend(w_anns.to_vec().into_iter()));
    if v_val == w_val {
        Some(N::wrap(v_anns, v_val))
    } else {
        let maybe_merged = match v_val {
            Value::Record(rv) =>
                Some(Value::Record(Record(merge_seqs(rv.0, w_val.into_record()?.0)?))),
            Value::Sequence(vs) =>
                Some(Value::Sequence(merge_seqs(vs, w_val.into_sequence()?)?)),
            Value::Set(_vs) =>
                None, // unsure how to merge sets
            Value::Dictionary(vs) => {
                let mut ws = w_val.into_dictionary()?;
                let mut rs = Map::new();
                for (k, vv) in vs.into_iter() {
                    match ws.remove(&k) {
                        Some(wv) => { rs.insert(k, merge2(vv, wv)?); }
                        None => { rs.insert(k, vv); }
                    }
                }
                rs.extend(ws.into_iter());
                Some(Value::Dictionary(rs))
            }
            _ => None,
        };
        maybe_merged.map(|vw| N::wrap(v_anns, vw))
    }
}

pub fn merge<N: NestedValue, I: IntoIterator<Item = N>>(vs: I) -> Option<N> {
    let mut vs = vs.into_iter();
    let mut v = vs.next().expect("at least one value in merge()");
    for w in vs {
        match merge2(v, w) {
            Some(merged) => v = merged,
            None => return None,
        }
    }
    Some(v)
}
