use encodings::hex::ToHex;
use extended_primitives::Hash;
use handshake_types::Compact;
use serde::{ser::SerializeSeq, Serialize, Serializer};
use serde_tuple::*;
use stratum_types::traits::Notify as NotifyTrait;

//Note: We only support deserializing with the non-classic version of stratum. This is entirely
//working as intended. We do not plan to support deserializing of the non-classic version at any
//point in the future.
#[derive(Clone, Debug, Default, Deserialize_tuple)]
pub struct Notify {
    pub classic: bool,
    pub job_id: String,
    pub prev_block: Hash,
    pub merkle_root: Hash,
    pub witness_root: Hash,
    pub tree_root: Hash,
    pub reserved_root: Hash,
    pub mask_hash: Option<Hash>,
    pub version: u32,
    pub bits: u32,
    pub time: u64,
}

impl NotifyTrait for Notify {
    fn get_difficulty(&self) -> f64 {
        Compact::from(self.bits).to_f64()
    }
}

impl Serialize for Notify {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        if self.classic {
            let mut seq = serializer.serialize_seq(Some(9))?;
            seq.serialize_element(&self.job_id)?;
            seq.serialize_element(&self.prev_block)?;
            seq.serialize_element(&self.merkle_root)?;
            seq.serialize_element(&self.witness_root)?;
            seq.serialize_element(&self.tree_root)?;
            seq.serialize_element(&self.reserved_root)?;

            //treat version and bits as hex.
            seq.serialize_element(&self.version.to_be_bytes().to_hex())?;
            seq.serialize_element(&self.bits.to_be_bytes().to_hex())?;

            //Treat time as a u32, and hex.
            let time = self.time as u32;
            seq.serialize_element(&time.to_be_bytes().to_hex())?;

            seq.end()
        } else {
            let mut seq = serializer.serialize_seq(Some(10))?;
            seq.serialize_element(&self.job_id)?;
            seq.serialize_element(&self.prev_block)?;
            seq.serialize_element(&self.merkle_root)?;
            seq.serialize_element(&self.witness_root)?;
            seq.serialize_element(&self.tree_root)?;
            seq.serialize_element(&self.reserved_root)?;
            //@todo figure out a way to unwrap this smarter
            seq.serialize_element(&self.mask_hash.unwrap())?;
            seq.serialize_element(&self.version)?;
            seq.serialize_element(&self.bits)?;
            seq.serialize_element(&self.time)?;
            seq.end()
        }
    }
}
