use crate::*;
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum PlutusScriptSourceEnum {
    Script(PlutusScript),
    RefInput(TransactionInput, ScriptHash, Option<Language>),
}
impl PlutusScriptSourceEnum {
    pub fn script_hash(&self) -> ScriptHash {
        match self {
            PlutusScriptSourceEnum::Script(script) => script.hash(),
            PlutusScriptSourceEnum::RefInput(_, script_hash, _) => script_hash.clone(),
        }
    }
    pub fn language(&self) -> Option<Language> {
        match self {
            PlutusScriptSourceEnum::Script(script) => Some(script.language_version()),
            PlutusScriptSourceEnum::RefInput(_, _, language) => language.clone(),
        }
    }
}
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PlutusScriptSource(pub(crate) PlutusScriptSourceEnum);
#[wasm_bindgen]
impl PlutusScriptSource {
    pub fn new(script: &PlutusScript) -> Self {
        Self(PlutusScriptSourceEnum::Script(script.clone()))
    }
    #[deprecated(
        since = "11.3.0",
        note = "This constructor has missed information about plutus script language vesrion. That can affect the script data hash calculation. Use `.new_ref_input_with_lang_ver` instead."
    )]
    pub fn new_ref_input(script_hash: &ScriptHash, input: &TransactionInput) -> Self {
        Self(PlutusScriptSourceEnum::RefInput(
            input.clone(),
            script_hash.clone(),
            None,
        ))
    }
    pub fn new_ref_input_with_lang_ver(
        script_hash: &ScriptHash,
        input: &TransactionInput,
        lang_ver: &Language,
    ) -> Self {
        Self(PlutusScriptSourceEnum::RefInput(
            input.clone(),
            script_hash.clone(),
            Some(lang_ver.clone()),
        ))
    }
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum DatumSourceEnum {
    Datum(PlutusData),
    RefInput(TransactionInput),
}
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct DatumSource(DatumSourceEnum);
#[wasm_bindgen]
impl DatumSource {
    pub fn new(datum: &PlutusData) -> Self {
        Self(DatumSourceEnum::Datum(datum.clone()))
    }
    pub fn new_ref_input(input: &TransactionInput) -> Self {
        Self(DatumSourceEnum::RefInput(input.clone()))
    }
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) enum NativeScriptSourceEnum {
    NativeScript(NativeScript),
    RefInput(TransactionInput, ScriptHash, RequiredSigners),
}
impl NativeScriptSourceEnum {
    pub fn script_hash(&self) -> ScriptHash {
        match self {
            NativeScriptSourceEnum::NativeScript(script) => script.hash(),
            NativeScriptSourceEnum::RefInput(_, script_hash, _) => script_hash.clone(),
        }
    }
    pub fn required_signers(&self) -> RequiredSignersSet {
        match self {
            NativeScriptSourceEnum::NativeScript(script) => RequiredSignersSet::from(script),
            NativeScriptSourceEnum::RefInput(_, _, required_signers) => required_signers.into(),
        }
    }
}
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct NativeScriptSource(pub(crate) NativeScriptSourceEnum);
#[wasm_bindgen]
impl NativeScriptSource {
    pub fn new(script: &NativeScript) -> Self {
        Self(NativeScriptSourceEnum::NativeScript(script.clone()))
    }
    pub fn new_ref_input(
        script_hash: &ScriptHash,
        input: &TransactionInput,
        required_signers: &RequiredSigners,
    ) -> Self {
        Self(NativeScriptSourceEnum::RefInput(
            input.clone(),
            script_hash.clone(),
            required_signers.clone(),
        ))
    }
}
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PlutusWitness {
    pub(crate) script: PlutusScriptSourceEnum,
    pub(crate) datum: Option<DatumSourceEnum>,
    pub(crate) redeemer: Redeemer,
}
#[wasm_bindgen]
impl PlutusWitness {
    pub fn new(script: &PlutusScript, datum: &PlutusData, redeemer: &Redeemer) -> Self {
        Self {
            script: PlutusScriptSourceEnum::Script(script.clone()),
            datum: Some(DatumSourceEnum::Datum(datum.clone())),
            redeemer: redeemer.clone(),
        }
    }
    pub fn new_with_ref(
        script: &PlutusScriptSource,
        datum: &DatumSource,
        redeemer: &Redeemer,
    ) -> Self {
        Self {
            script: script.0.clone(),
            datum: Some(datum.0.clone()),
            redeemer: redeemer.clone(),
        }
    }
    pub fn new_without_datum(script: &PlutusScript, redeemer: &Redeemer) -> Self {
        Self {
            script: PlutusScriptSourceEnum::Script(script.clone()),
            datum: None,
            redeemer: redeemer.clone(),
        }
    }
    pub fn new_with_ref_without_datum(script: &PlutusScriptSource, redeemer: &Redeemer) -> Self {
        Self {
            script: script.0.clone(),
            datum: None,
            redeemer: redeemer.clone(),
        }
    }
    pub fn script(&self) -> Option<PlutusScript> {
        match &self.script {
            PlutusScriptSourceEnum::Script(script) => Some(script.clone()),
            _ => None,
        }
    }
    pub fn datum(&self) -> Option<PlutusData> {
        match &self.datum {
            Some(DatumSourceEnum::Datum(datum)) => Some(datum.clone()),
            _ => None,
        }
    }
    pub fn redeemer(&self) -> Redeemer {
        self.redeemer.clone()
    }
    #[allow(dead_code)]
    pub(crate) fn clone_with_redeemer_index(&self, index: &BigNum) -> Self {
        Self {
            script: self.script.clone(),
            datum: self.datum.clone(),
            redeemer: self.redeemer.clone_with_index(index),
        }
    }
    pub(crate) fn clone_with_redeemer_index_and_tag(
        &self,
        index: &BigNum,
        tag: &RedeemerTag,
    ) -> Self {
        Self {
            script: self.script.clone(),
            datum: self.datum.clone(),
            redeemer: self.redeemer.clone_with_index_and_tag(index, tag),
        }
    }
}
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PlutusWitnesses(pub(crate) Vec<PlutusWitness>);
#[wasm_bindgen]
impl PlutusWitnesses {
    pub fn new() -> Self {
        Self(Vec::new())
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn get(&self, index: usize) -> PlutusWitness {
        self.0[index].clone()
    }
    pub fn add(&mut self, elem: &PlutusWitness) {
        self.0.push(elem.clone());
    }
    pub(crate) fn collect(&self) -> (PlutusScripts, Option<PlutusList>, Redeemers) {
        let mut used_scripts = BTreeSet::new();
        let mut used_datums = BTreeSet::new();
        let mut used_redeemers = BTreeSet::new();
        let mut s = PlutusScripts::new();
        let mut d: Option<PlutusList> = None;
        let mut r = Redeemers::new();
        self.0.iter().for_each(|w| {
            if let PlutusScriptSourceEnum::Script(script) = &w.script {
                if used_scripts.insert(script.clone()) {
                    s.add(script);
                }
            }
            if let Some(DatumSourceEnum::Datum(datum)) = &w.datum {
                if used_datums.insert(datum) {
                    match d {
                        Some(ref mut d) => d.add(datum),
                        None => {
                            d = {
                                let mut initial_list = PlutusList::new();
                                initial_list.add(datum);
                                Some(initial_list)
                            }
                        }
                    }
                }
            }
            if used_redeemers.insert(w.redeemer.clone()) {
                r.add(&w.redeemer);
            }
        });
        (s, d, r)
    }
}
impl From<Vec<PlutusWitness>> for PlutusWitnesses {
    fn from(scripts: Vec<PlutusWitness>) -> Self {
        Self(scripts)
    }
}
#[derive(Clone, Debug)]
pub(crate) enum ScriptWitnessType {
    NativeScriptWitness(NativeScriptSourceEnum),
    PlutusScriptWitness(PlutusWitness),
}
impl ScriptWitnessType {
    pub fn script_hash(&self) -> ScriptHash {
        match self {
            ScriptWitnessType::NativeScriptWitness(script) => script.script_hash(),
            ScriptWitnessType::PlutusScriptWitness(script) => script.script.script_hash(),
        }
    }
}