use crate::chaindef::Transaction;
#[cfg(nexa)]
use crate::nexa::hash_types::TxIdem;
use bitcoin_hashes::Hash;
use bitcoincash::consensus::encode::MAX_VEC_SIZE;
use bitcoincash::consensus::Decodable;
use bitcoincash::consensus::Encodable;
#[cfg(bch)]
use bitcoincash::Txid;
use bitcoincash::VarInt;

use crate::chaindef::OutPoint;
use crate::chaindef::OutPointHash;

#[cfg(bch)]
pub fn compute_outpoint_hash(txid: &Txid, vout: u32) -> OutPointHash {
    let mut e = OutPointHash::engine();
    txid.consensus_encode(&mut e)
        .expect("failed to encode input txid");
    vout.consensus_encode(&mut e)
        .expect("failed to encode input index");
    OutPointHash::from_engine(e)
}

#[cfg(nexa)]
pub fn compute_outpoint_hash(txidem: &TxIdem, n: u32) -> OutPointHash {
    let mut e = OutPointHash::engine();
    txidem
        .consensus_encode(&mut e)
        .expect("failed to encode input txid");
    n.consensus_encode(&mut e)
        .expect("failed to encode input index");
    OutPointHash::from_engine(e)
}

#[cfg(nexa)]
pub fn compute_outpoint_hash_from_tx(tx: &Transaction, out_n: u32) -> OutPointHash {
    compute_outpoint_hash(&tx.txidem(), out_n)
}

#[cfg(bch)]
pub fn compute_outpoint_hash_from_tx(tx: &Transaction, out_n: u32) -> OutPointHash {
    compute_outpoint_hash(&tx.txid(), out_n)
}

#[cfg(nexa)]
pub fn outpoint_hash(previous_outpoint: &OutPoint) -> OutPointHash {
    previous_outpoint.hash
}

#[cfg(bch)]
pub fn outpoint_hash(previous_output: &OutPoint) -> OutPointHash {
    compute_outpoint_hash(&previous_output.txid, previous_output.vout)
}

pub fn decode_vector<R: std::io::Read + ?Sized, V: Decodable>(
    r: &mut R,
) -> Result<Vec<V>, bitcoincash::consensus::encode::Error> {
    let vector_len = VarInt::consensus_decode(r)?.0;
    let vector_bytes = (vector_len as usize)
        .checked_mul(std::mem::size_of::<V>())
        .ok_or(bitcoincash::consensus::encode::Error::ParseFailed(
            "Invalid length",
        ))?;
    if vector_bytes > MAX_VEC_SIZE {
        return Err(
            bitcoincash::consensus::encode::Error::OversizedVectorAllocation {
                requested: vector_bytes,
                max: MAX_VEC_SIZE,
            },
        );
    }
    let mut vector: Vec<V> = Vec::with_capacity(vector_len as usize);
    for _ in 0..vector_len {
        vector.push(Decodable::consensus_decode(r)?);
    }
    Ok(vector)
}

pub fn encode_vector<W: std::io::Write + ?Sized, V: Encodable>(
    w: &mut W,
    v: &[V],
) -> Result<usize, std::io::Error> {
    let mut len = 0;
    len += VarInt(v.len() as u64).consensus_encode(w)?;
    for i in v {
        len += i.consensus_encode(w)?;
    }
    Ok(len)
}
