use std::sync::{Arc, RwLock};

use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::{
    op::{OpPool, OpRef},
    program::Program,
    programmetadata::ProgramMetadata,
    programsignature::ProgramSignature,
};

#[derive(Serialize, Deserialize)]
struct ProgramFields {
    op_pool: OpPool,
    metadata: ProgramMetadata,
    signature: ProgramSignature,
    root: Option<OpRef>,
}

impl Serialize for Program {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        ProgramFields {
            op_pool: self
                .op_pool
                .read()
                .map_err(|_| serde::ser::Error::custom("poisoned op_pool lock"))?
                .clone(),
            metadata: self
                .metadata
                .read()
                .map_err(|_| serde::ser::Error::custom("poisoned metadata lock"))?
                .clone(),
            signature: self
                .signature
                .read()
                .map_err(|_| serde::ser::Error::custom("poisoned signature lock"))?
                .clone(),
            root: self.root,
        }
        .serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for Program {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let fields = ProgramFields::deserialize(deserializer)?;
        Ok(Self {
            op_pool: Arc::new(RwLock::new(fields.op_pool)),
            metadata: Arc::new(RwLock::new(fields.metadata)),
            signature: Arc::new(RwLock::new(fields.signature)),
            root: fields.root,
        })
    }
}
