use crate::{interpret_trait::InterpreterContext, serde_raw::ValueSubTree};

use super::{
    file_loader::{load_file, MxscFileJson},
    functions::*,
    parse_num::*,
    prefixes::*,
};

pub fn interpret_subtree(vst: &ValueSubTree, context: &InterpreterContext) -> Vec<u8> {
    match vst {
        ValueSubTree::Str(s) => interpret_string(s, context),
        ValueSubTree::List(l) => {
            let mut concat = Vec::<u8>::new();
            for item in l.iter() {
                concat.extend_from_slice(interpret_subtree(item, context).as_slice());
            }
            concat
        },
        ValueSubTree::Map(m) => {
            let mut concat = Vec::<u8>::new();
            for (_, value) in m.iter() {
                concat.extend_from_slice(interpret_subtree(value, context).as_slice());
            }
            concat
        },
    }
}

pub fn interpret_string(s: &str, context: &InterpreterContext) -> Vec<u8> {
    if s.is_empty() {
        return Vec::new();
    }

    // concatenate values of different formats
    let split_parts: Vec<_> = s.split('|').collect();
    if split_parts.len() > 1 {
        let mut result = Vec::<u8>::new();
        for part in split_parts.iter() {
            result.extend_from_slice(interpret_string(part, context).as_slice());
        }
        return result;
    }

    if s == "true" {
        return [1u8].to_vec();
    }

    if s == "false" {
        return Vec::new();
    }

    for str_prefix in STR_PREFIXES.iter() {
        if let Some(stripped) = s.strip_prefix(str_prefix) {
            return stripped.as_bytes().to_vec();
        }
    }

    if let Some(stripped) = s.strip_prefix(ADDR_PREFIX) {
        return address_expression(stripped);
    }

    if let Some(stripped) = s.strip_prefix(SC_ADDR_PREFIX) {
        return sc_address_expression(stripped, &context.vm_type);
    }

    if let Some(stripped) = s.strip_prefix(FILE_PREFIX) {
        return load_file(stripped, context, |c| c);
    }

    if let Some(stripped) = s.strip_prefix(MXSC_PREFIX) {
        return load_file(stripped, context, |content| {
            let mxsc_json: MxscFileJson = serde_json::from_slice(&content).unwrap();
            hex::decode(mxsc_json.code).expect("Could not decode contract code")
        });
    }

    if let Some(stripped) = s.strip_prefix(KECCAK256_PREFIX) {
        let arg = interpret_string(stripped, context);
        return keccak256(arg.as_slice());
    }

    if let Some(stripped) = s.strip_prefix(BECH32_PREFIX) {
        return bech32(stripped);
    }

    if let Some(stripped) = s.strip_prefix(NESTED_PREFIX) {
        return parse_nested(stripped, context);
    }

    if let Some(fixed_width) = try_parse_fixed_width(s) {
        return fixed_width;
    }

    parse_num(s)
}

fn parse_nested(s: &str, context: &InterpreterContext) -> Vec<u8> {
    let parsed = interpret_string(s, context);
    let encoded_length = (parsed.len() as u32).to_be_bytes();
    [&encoded_length[..], &parsed[..]].concat()
}
