use bytes::BytesMut;
use crate::OBJECT_PATH_REGEX;
mod encode;
pub use encode::{encode_algin, EncodeError};
mod decode;
pub use decode::{decode_algin, DecodeError};
fn get_next_signature<'a>(signature: &'a str, signature_offset: &mut usize)
    -> Result<&'a str, DecodeError> {
    let start = *signature_offset;
    loop {
        let mut end = *signature_offset + 1;
        match signature.get(*signature_offset..end) {
            Some(s) => {
                match s {
                    "y" | "b" | "n" | "q" | "i" | "u" | "x" | "t" | "d" | "s" |
                    "o" | "g" | "v" => {
                        return Ok(&signature[start..end]);
                    }
                    "a" => {}
                    "(" => {
                        let mut parentheses_depth: usize = 0;
                        loop {
                            *signature_offset = end;
                            end = *signature_offset + 1;
                            if let Some(s) = signature.get(
                                *signature_offset..end) {
                                if s == ")" {
                                    if parentheses_depth == 0 {
                                        return Ok(&signature[start..end]);
                                    } else {
                                        parentheses_depth -= 1;
                                    }
                                } else if s == "(" {
                                    parentheses_depth += 1;
                                }
                            } else {
                                return Err(DecodeError::Signature);
                            }
                        }
                    }
                    "{" => {
                        let mut bracket_depth: usize = 0;
                        loop {
                            *signature_offset = end;
                            end = *signature_offset + 1;
                            if let Some(s) = signature.get(
                                *signature_offset..end) {
                                if s == "}" {
                                    if bracket_depth == 0 {
                                        return Ok(&signature[start..end]);
                                    } else {
                                        bracket_depth -= 1;
                                    }
                                } else if s == "{" {
                                    bracket_depth += 1;
                                }
                            } else {
                                return Err(DecodeError::Signature);
                            }
                        }
                    }
                    _ => return Err(DecodeError::Signature)
                }
            }
            None => return Err(DecodeError::Signature)
        }
        *signature_offset = end;
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
    Byte(u8),
    Boolean(bool),
    Int16(i16),
    Uint16(u16),
    Int32(i32),
    Uint32(u32),
    Int64(i64),
    Uint64(u64),
    Double(f64),
    String(String),
    ObjectPath(String),
    Signature(String),
    Array(Vec<Value>, String),
    Struct(Vec<Value>),
    DictEntry(Box<(Value, Value)>),
    Variant(Vec<Value>),
    UnixFD(u32),
}
impl Value {
    
    pub fn decode(buf: &BytesMut, offset: &mut usize, is_le: bool,
                  array_recursion: u8, struct_recursion: u8, signature: &str)
        -> Result<Vec<Value>, DecodeError> {
        
        if 256 <= signature.len() {
            return Err(DecodeError::SignatureTooBig);
        }
        
        if 32 <= array_recursion {
            return Err(DecodeError::ArrayRecursion);
        }
        
        if 32 <= struct_recursion {
            return Err(DecodeError::StructRecursion);
        }
        let mut signature_offset: usize = 0;
        let mut r = Vec::new();
        let mut end = signature_offset + 1;
        
        while let Some(s) = signature.get(signature_offset..end) {
            let v = match s {
                "y" => decode::byte(buf, offset),
                "b" => {
                    decode::decode_algin(buf, offset, 4)?;
                    decode::boolean(buf, offset, is_le)
                }
                "n" => {
                    decode::decode_algin(buf, offset, 2)?;
                    decode::int16(buf, offset, is_le)
                }
                "q" => {
                    decode::decode_algin(buf, offset, 2)?;
                    decode::uint16(buf, offset, is_le)
                }
                "i" => {
                    decode::decode_algin(buf, offset, 4)?;
                    decode::int32(buf, offset, is_le)
                }
                "u" => {
                    decode::decode_algin(buf, offset, 4)?;
                    decode::uint32(buf, offset, is_le)
                }
                "x" => {
                    decode::decode_algin(buf, offset, 8)?;
                    decode::int64(buf, offset, is_le)
                }
                "t" => {
                    decode::decode_algin(buf, offset, 8)?;
                    decode::uint64(buf, offset, is_le)
                }
                "d" => {
                    decode::decode_algin(buf, offset, 8)?;
                    decode::double(buf, offset, is_le)
                }
                "s" => {
                    decode::decode_algin(buf, offset, 4)?;
                    decode::string(buf, offset, is_le)
                }
                "o" => {
                    decode::decode_algin(buf, offset, 4)?;
                    decode::path(buf, offset, is_le)
                }
                "g" => decode::signature(buf, offset),
                "a" => {
                    decode::decode_algin(buf, offset, 4)?;
                    signature_offset += 1;
                    decode::array(buf, offset, is_le, array_recursion,
                                  struct_recursion,
                                  get_next_signature(signature,
                                                     &mut signature_offset)?)
                }
                "(" => {
                    decode::decode_algin(buf, offset, 8)?;
                    
                    
                    signature_offset += 1;
                    let mut bracket_depth = 0;
                    let start = signature_offset;
                    loop {
                        let end = signature_offset + 1;
                        
                        
                        if signature.len() <= signature_offset {
                            return Err(DecodeError::Signature);
                        }
                        
                        if let Some(s) = signature.get(signature_offset..end) {
                            
                            if s == ")" {
                                
                                
                                if bracket_depth == 0 {
                                    
                                    
                                    if signature_offset - start == 0 {
                                        return Err(DecodeError::Signature);
                                    }
                                    
                                    break decode::decode_struct(buf, offset,
                                                                is_le,
                                                                array_recursion,
                                                                struct_recursion,
                                                                &signature[start..signature_offset]);
                                } else {
                                    bracket_depth -= 1;
                                }
                            } else if s == "(" {
                                bracket_depth += 1;
                            }
                        } else {
                            return Err(DecodeError::Signature);
                        }
                        signature_offset = end;
                    }
                }
                "{" => {
                    decode::decode_algin(buf, offset, 8)?;
                    
                    
                    
                    
                    signature_offset += 1;
                    let signature_key =
                        get_next_signature(signature, &mut signature_offset)?;
                    
                    
                    signature_offset += 1;
                    let signature_value =
                        get_next_signature(signature, &mut signature_offset)?;
                    signature_offset += 1;
                    end = signature_offset + 1;
                    
                    if let Some(s) = signature.get(signature_offset..end) {
                        
                        if s == "}" {
                            decode::dict_entry(buf, offset, is_le,
                                               array_recursion,
                                               struct_recursion,
                                               signature_key,
                                               signature_value)
                        } else {
                            return Err(DecodeError::Signature);
                        }
                    } else {
                        return Err(DecodeError::Signature);
                    }
                }
                "v" => decode::variant(buf, offset, is_le),
                _ => return Err(DecodeError::Signature)
            }?;
            signature_offset += 1;
            end = signature_offset + 1;
            r.push(v);
        }
        Ok(r)
    }
    
    pub fn encode(&self, buf: &mut BytesMut, is_le: bool)
                  -> Result<(), EncodeError> {
        match self {
            Value::Byte(b) => encode::byte(buf, *b),
            Value::Boolean(b) => {
                encode::encode_algin(buf, 4);
                encode::boolean(buf, *b, is_le)
            }
            Value::Int16(i) => {
                encode::encode_algin(buf, 2);
                encode::i16(buf, *i, is_le)
            }
            Value::Uint16(u) => {
                encode::encode_algin(buf, 2);
                encode::u16(buf, *u, is_le)
            }
            Value::Int32(i) => {
                encode::encode_algin(buf, 4);
                encode::i32(buf, *i, is_le)
            }
            Value::Uint32(u) => {
                encode::encode_algin(buf, 4);
                encode::u32(buf, *u, is_le)
            }
            Value::Int64(i) => {
                encode::encode_algin(buf, 8);
                encode::i64(buf, *i, is_le)
            }
            Value::Uint64(u) => {
                encode::encode_algin(buf, 8);
                encode::u64(buf, *u, is_le)
            }
            Value::Double(f) => {
                encode::encode_algin(buf, 8);
                encode::f64(buf, *f, is_le)
            }
            Value::ObjectPath(s) => {
                if !OBJECT_PATH_REGEX.is_match(s) {
                    return Err(EncodeError::ObjectPathInvalid(s.clone()));
                }
                encode::encode_algin(buf, 4);
                encode::str(buf, s, is_le)
            }
            Value::String(s) => {
                encode::encode_algin(buf, 4);
                encode::str(buf, s, is_le)
            }
            Value::Signature(s) => encode::sig(buf, s),
            Value::Array(vec, sig) => {
                encode::encode_algin(buf, 4);
                encode::array(buf, vec, sig, is_le)
            }
            Value::Struct(vec) => {
                encode::encode_algin(buf, 8);
                encode::encode_struct(buf, vec, is_le)
            }
            Value::DictEntry(b) => {
                encode::encode_algin(buf, 8);
                encode::dict_entry(buf, b, is_le)
            }
            Value::Variant(vec) => encode::variant(buf, vec, is_le),
            Value::UnixFD(pos) => {
                encode::encode_algin(buf, 4);
                encode::u32(buf, *pos, is_le)
            }
        }
    }
    
    pub fn get_signature(&self, s: &mut String) {
        match self {
            Value::Byte(_) => s.push('y'),
            Value::Boolean(_) => s.push('b'),
            Value::Int16(_) => s.push('n'),
            Value::Uint16(_) => s.push('q'),
            Value::Int32(_) => s.push('i'),
            Value::Uint32(_) => s.push('u'),
            Value::Int64(_) => s.push('x'),
            Value::Uint64(_) => s.push('t'),
            Value::Double(_) => s.push('d'),
            Value::String(_) => s.push('s'),
            Value::ObjectPath(_) => s.push('o'),
            Value::Signature(_) => s.push('g'),
            Value::Array(_, sig) => {
                s.push('a');
                s.push_str(sig);
            }
            Value::Struct(vec) => {
                s.push('(');
                for v in vec {
                    v.get_signature(s);
                }
                s.push(')');
            }
            Value::DictEntry(b) => {
                s.push('{');
                let (key, value) = &**b;
                key.get_signature(s);
                value.get_signature(s);
                s.push('}');
            }
            Value::Variant(_) => s.push('v'),
            Value::UnixFD(_) => s.push('h')
        }
    }
}