//! Serialization/deserialization support (java.io.Serializable).
//!
//! Binary format for ObjectInputStream/ObjectOutputStream compatibility.

use crate::class_file::ClassFile;
use crate::memory::{HeapObject, Value};
use std::io::{Cursor, Read, Write};

const MAGIC: &[u8; 4] = b"JVRS";
const VERSION: u8 = 1;

/// Type tags for Value serialization
const TAG_NULL: u8 = 0;
const TAG_INT: u8 = 1;
const TAG_LONG: u8 = 2;
const TAG_FLOAT: u8 = 3;
const TAG_DOUBLE: u8 = 4;
const TAG_REFERENCE: u8 = 5;
const TAG_ARRAY_REF: u8 = 6;
const TAG_BOOLEAN: u8 = 7;
const TAG_BYTE: u8 = 8;
const TAG_CHAR: u8 = 9;
const TAG_SHORT: u8 = 10;

/// Check if a class is serializable (implements java/io/Serializable interface)
pub fn is_serializable(_class_name: &str) -> bool {
    false
}

/// Check if a loaded class implements java/io/Serializable
pub fn is_serializable_class(class: &ClassFile) -> bool {
    for &iface_index in &class.interfaces {
        if let Some(name) = class.get_class_name_from_index(iface_index) {
            if name == "java/io/Serializable" {
                return true;
            }
        }
    }
    if let Some(super_name) = class.get_super_class_name() {
        if super_name == "java/lang/Object" {
            return false;
        }
    }
    false
}

fn write_len_prefixed_string<W: Write>(w: &mut W, s: &str) -> std::io::Result<()> {
    let bytes = s.as_bytes();
    w.write_all(&(bytes.len() as u32).to_be_bytes())?;
    w.write_all(bytes)?;
    Ok(())
}

fn read_len_prefixed_string<R: Read>(r: &mut R) -> std::io::Result<String> {
    let mut len_buf = [0u8; 4];
    r.read_exact(&mut len_buf)?;
    let len = u32::from_be_bytes(len_buf) as usize;
    let mut buf = vec![0u8; len];
    r.read_exact(&mut buf)?;
    String::from_utf8(buf).map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid UTF-8"))
}

fn write_value<W: Write>(w: &mut W, v: &Value) -> std::io::Result<()> {
    match v {
        Value::Null => w.write_all(&[TAG_NULL])?,
        Value::Int(x) => {
            w.write_all(&[TAG_INT])?;
            w.write_all(&x.to_be_bytes())?;
        }
        Value::Long(x) => {
            w.write_all(&[TAG_LONG])?;
            w.write_all(&x.to_be_bytes())?;
        }
        Value::Float(x) => {
            w.write_all(&[TAG_FLOAT])?;
            w.write_all(&x.to_bits().to_be_bytes())?;
        }
        Value::Double(x) => {
            w.write_all(&[TAG_DOUBLE])?;
            w.write_all(&x.to_bits().to_be_bytes())?;
        }
        Value::Reference(addr) => {
            w.write_all(&[TAG_REFERENCE])?;
            w.write_all(&addr.to_be_bytes())?;
        }
        Value::ArrayRef(addr) => {
            w.write_all(&[TAG_ARRAY_REF])?;
            w.write_all(&addr.to_be_bytes())?;
        }
        Value::Boolean(b) => {
            w.write_all(&[TAG_BOOLEAN])?;
            w.write_all(&[*b as u8])?;
        }
        Value::Byte(x) => {
            w.write_all(&[TAG_BYTE])?;
            w.write_all(&[*x as u8])?;
        }
        Value::Char(c) => {
            w.write_all(&[TAG_CHAR])?;
            w.write_all(&c.to_be_bytes())?;
        }
        Value::Short(x) => {
            w.write_all(&[TAG_SHORT])?;
            w.write_all(&x.to_be_bytes())?;
        }
        Value::ReturnAddress(_) => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Cannot serialize return address",
            ));
        }
    }
    Ok(())
}

fn read_value<R: Read>(r: &mut R) -> std::io::Result<Value> {
    let mut tag = [0u8];
    r.read_exact(&mut tag)?;
    match tag[0] {
        TAG_NULL => Ok(Value::Null),
        TAG_INT => {
            let mut buf = [0u8; 4];
            r.read_exact(&mut buf)?;
            Ok(Value::Int(i32::from_be_bytes(buf)))
        }
        TAG_LONG => {
            let mut buf = [0u8; 8];
            r.read_exact(&mut buf)?;
            Ok(Value::Long(i64::from_be_bytes(buf)))
        }
        TAG_FLOAT => {
            let mut buf = [0u8; 4];
            r.read_exact(&mut buf)?;
            Ok(Value::Float(f32::from_bits(u32::from_be_bytes(buf))))
        }
        TAG_DOUBLE => {
            let mut buf = [0u8; 8];
            r.read_exact(&mut buf)?;
            Ok(Value::Double(f64::from_bits(u64::from_be_bytes(buf))))
        }
        TAG_REFERENCE => {
            let mut buf = [0u8; 4];
            r.read_exact(&mut buf)?;
            Ok(Value::Reference(u32::from_be_bytes(buf)))
        }
        TAG_ARRAY_REF => {
            let mut buf = [0u8; 4];
            r.read_exact(&mut buf)?;
            Ok(Value::ArrayRef(u32::from_be_bytes(buf)))
        }
        TAG_BOOLEAN => {
            let mut buf = [0u8; 1];
            r.read_exact(&mut buf)?;
            Ok(Value::Boolean(buf[0] != 0))
        }
        TAG_BYTE => {
            let mut buf = [0u8; 1];
            r.read_exact(&mut buf)?;
            Ok(Value::Byte(buf[0] as i8))
        }
        TAG_CHAR => {
            let mut buf = [0u8; 2];
            r.read_exact(&mut buf)?;
            Ok(Value::Char(u16::from_be_bytes(buf)))
        }
        TAG_SHORT => {
            let mut buf = [0u8; 2];
            r.read_exact(&mut buf)?;
            Ok(Value::Short(i16::from_be_bytes(buf)))
        }
        _ => Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            format!("Unknown value tag: {}", tag[0]),
        )),
    }
}

/// Serialize an object to bytes
pub fn serialize_object(obj: &HeapObject) -> Result<Vec<u8>, String> {
    let mut out = Vec::new();
    out.write_all(MAGIC).map_err(|e| e.to_string())?;
    out.write_all(&[VERSION]).map_err(|e| e.to_string())?;
    write_len_prefixed_string(&mut out, &obj.class_name).map_err(|e| e.to_string())?;
    let field_count = obj.fields.len() as u32;
    out.write_all(&field_count.to_be_bytes()).map_err(|e| e.to_string())?;
    for (name, value) in &obj.fields {
        write_len_prefixed_string(&mut out, name).map_err(|e| e.to_string())?;
        write_value(&mut out, value).map_err(|e| e.to_string())?;
    }
    if let Some(ref s) = obj.string_data {
        out.write_all(&[1u8]).map_err(|e| e.to_string())?;
        write_len_prefixed_string(&mut out, s).map_err(|e| e.to_string())?;
    } else {
        out.write_all(&[0u8]).map_err(|e| e.to_string())?;
    }
    Ok(out)
}

/// Deserialize bytes to object
pub fn deserialize_object(data: &[u8], _class_name: &str) -> Result<HeapObject, String> {
    let mut cursor = Cursor::new(data);
    let mut magic = [0u8; 4];
    cursor.read_exact(&mut magic).map_err(|e| e.to_string())?;
    if &magic != MAGIC {
        return Err("Invalid serialization magic".to_string());
    }
    let mut ver = [0u8; 1];
    cursor.read_exact(&mut ver).map_err(|e| e.to_string())?;
    if ver[0] != VERSION {
        return Err(format!("Unsupported serialization version: {}", ver[0]));
    }
    let class_name = read_len_prefixed_string(&mut cursor).map_err(|e| e.to_string())?;
    let mut count_buf = [0u8; 4];
    cursor.read_exact(&mut count_buf).map_err(|e| e.to_string())?;
    let field_count = u32::from_be_bytes(count_buf) as usize;
    let mut fields = std::collections::HashMap::new();
    for _ in 0..field_count {
        let name = read_len_prefixed_string(&mut cursor).map_err(|e| e.to_string())?;
        let value = read_value(&mut cursor).map_err(|e| e.to_string())?;
        fields.insert(name, value);
    }
    let mut has_string = [0u8; 1];
    cursor.read_exact(&mut has_string).map_err(|e| e.to_string())?;
    let string_data = if has_string[0] != 0 {
        Some(read_len_prefixed_string(&mut cursor).map_err(|e| e.to_string())?)
    } else {
        None
    };
    Ok(HeapObject {
        fields,
        class_name,
        marked: false,
        string_data,
        monitor: None,
    })
}

/// Serialize a Value
pub fn serialize_value(v: &Value) -> Result<Vec<u8>, String> {
    let mut out = Vec::new();
    write_value(&mut out, v).map_err(|e| e.to_string())?;
    Ok(out)
}

/// Deserialize a Value from bytes
pub fn deserialize_value(data: &[u8]) -> Result<Value, String> {
    let mut cursor = Cursor::new(data);
    read_value(&mut cursor).map_err(|e| e.to_string())
}
