//! Contains definitions for variables and constant values.

use crate::ast::{FloatTy, IntTy, UIntTy};
use core::hash::Hash;
use derive_generic_visitor::{Drive, DriveMut};
use macros::{EnumAsGetters, EnumIsA, VariantIndexArity, VariantName};
use serde::{Deserialize, Serialize};
use serde_state::{DeserializeState, SerializeState};

// We need to manipulate a lot of indices for the types, variables, definitions,
// etc. In order not to confuse them, we define an index type for every one of
// them (which is just a struct with a unique usize field), together with some
// utilities like a fresh index generator. Those structures and utilities are
// generated by using macros.
// TODO: move
generate_index_type!(LocalId, "");

/// A primitive value.
///
/// Those are for instance used for the constant operands [crate::expressions::Operand::Const]
#[derive(
    Debug,
    PartialEq,
    Eq,
    Clone,
    VariantName,
    EnumIsA,
    EnumAsGetters,
    Serialize,
    Deserialize,
    SerializeState,
    DeserializeState,
    Drive,
    DriveMut,
    Hash,
    PartialOrd,
    Ord,
)]
#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("V"))]
#[serde_state(stateless)]
pub enum Literal {
    Scalar(ScalarValue),
    Float(FloatValue),
    #[drive(skip)]
    Bool(bool),
    #[drive(skip)]
    Char(char),
    #[drive(skip)]
    ByteStr(Vec<u8>),
    #[drive(skip)]
    Str(String),
}

/// A scalar value.
#[derive(
    Debug,
    PartialEq,
    Eq,
    Copy,
    Clone,
    EnumIsA,
    EnumAsGetters,
    VariantName,
    VariantIndexArity,
    Hash,
    PartialOrd,
    Ord,
    Serialize,
    Deserialize,
    Drive,
    DriveMut,
)]
#[drive(skip)]
#[cfg_attr(feature = "charon_on_charon", charon::variants_suffix("Scalar"))]
pub enum ScalarValue {
    Unsigned(
        UIntTy,
        #[serde(with = "crate::ast::values_utils::scalar_value_ser_de")] u128,
    ),

    Signed(
        IntTy,
        #[serde(with = "crate::ast::values_utils::scalar_value_ser_de")] i128,
    ),
}

/// This is simlar to the Scalar value above. However, instead of storing
/// the float value itself, we store its String representation. This allows
/// to derive the Eq and Ord traits, which are not implemented for floats
#[derive(
    Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash, PartialOrd, Ord, Drive, DriveMut,
)]
pub struct FloatValue {
    #[cfg_attr(feature = "charon_on_charon", charon::rename("float_value"))]
    #[drive(skip)]
    pub value: String,
    #[cfg_attr(feature = "charon_on_charon", charon::rename("float_ty"))]
    pub ty: FloatTy,
}
