use std::{cmp::Ordering, fmt, hash::Hasher};
use crate::{
    engine_threading::{
        DebugWithEngines, DisplayWithEngines, EqWithEngines, HashWithEngines, OrdWithEngines,
        OrdWithEnginesContext, PartialEqWithEngines, PartialEqWithEnginesContext,
    },
    language::{parsed::CodeBlock, *},
    type_system::TypeBinding,
    Engines, TypeArgument, TypeId,
};
use sway_error::handler::ErrorEmitted;
use sway_types::{ident::Ident, Span, Spanned};
mod asm;
mod match_branch;
mod method_name;
mod scrutinee;
pub(crate) use asm::*;
pub(crate) use match_branch::MatchBranch;
pub use method_name::MethodName;
pub use scrutinee::*;
use sway_ast::intrinsics::Intrinsic;
#[derive(Debug, Clone)]
pub struct Expression {
    pub kind: ExpressionKind,
    pub span: Span,
}
#[derive(Debug, Clone)]
pub struct FunctionApplicationExpression {
    pub call_path_binding: TypeBinding<CallPath>,
    pub arguments: Vec<Expression>,
}
#[derive(Debug, Clone)]
pub struct LazyOperatorExpression {
    pub op: LazyOp,
    pub lhs: Box<Expression>,
    pub rhs: Box<Expression>,
}
#[derive(Debug, Clone)]
pub struct TupleIndexExpression {
    pub prefix: Box<Expression>,
    pub index: usize,
    pub index_span: Span,
}
#[derive(Debug, Clone)]
pub struct ArrayExpression {
    pub contents: Vec<Expression>,
    pub length_span: Option<Span>,
}
#[derive(Debug, Clone)]
pub struct StructExpression {
    pub call_path_binding: TypeBinding<CallPath>,
    pub fields: Vec<StructExpressionField>,
}
#[derive(Debug, Clone)]
pub struct IfExpression {
    pub condition: Box<Expression>,
    pub then: Box<Expression>,
    pub r#else: Option<Box<Expression>>,
}
#[derive(Debug, Clone)]
pub struct MatchExpression {
    pub value: Box<Expression>,
    pub branches: Vec<MatchBranch>,
}
#[derive(Debug, Clone)]
pub struct MethodApplicationExpression {
    pub method_name_binding: TypeBinding<MethodName>,
    pub contract_call_params: Vec<StructExpressionField>,
    pub arguments: Vec<Expression>,
}
#[derive(Debug, Clone)]
pub struct SubfieldExpression {
    pub prefix: Box<Expression>,
    pub field_to_access: Ident,
}
#[derive(Debug, Clone)]
pub struct AmbiguousSuffix {
    pub before: Option<TypeBinding<Ident>>,
    pub suffix: Ident,
}
impl Spanned for AmbiguousSuffix {
    fn span(&self) -> Span {
        if let Some(before) = &self.before {
            Span::join(before.span(), &self.suffix.span())
        } else {
            self.suffix.span()
        }
    }
}
#[derive(Debug, Clone)]
pub struct QualifiedPathType {
    pub ty: TypeArgument,
    pub as_trait: TypeId,
    pub as_trait_span: Span,
}
impl HashWithEngines for QualifiedPathType {
    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
        let QualifiedPathType {
            ty,
            as_trait,
            as_trait_span: _,
        } = self;
        ty.hash(state, engines);
        engines.te().get(*as_trait).hash(state, engines);
    }
}
impl EqWithEngines for QualifiedPathType {}
impl PartialEqWithEngines for QualifiedPathType {
    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
        let QualifiedPathType {
            ty,
            as_trait,
            as_trait_span: _,
        } = self;
        ty.eq(&other.ty, ctx)
            && ctx
                .engines()
                .te()
                .get(*as_trait)
                .eq(&ctx.engines().te().get(other.as_trait), ctx)
    }
}
impl OrdWithEngines for QualifiedPathType {
    fn cmp(&self, other: &Self, ctx: &OrdWithEnginesContext) -> Ordering {
        let QualifiedPathType {
            ty: l_ty,
            as_trait: l_as_trait,
            as_trait_span: _,
        } = self;
        let QualifiedPathType {
            ty: r_ty,
            as_trait: r_as_trait,
            as_trait_span: _,
        } = other;
        l_ty.cmp(r_ty, ctx).then_with(|| {
            ctx.engines()
                .te()
                .get(*l_as_trait)
                .cmp(&ctx.engines().te().get(*r_as_trait), ctx)
        })
    }
}
impl DisplayWithEngines for QualifiedPathType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
        write!(
            f,
            "<{} as {}>",
            engines.help_out(self.ty.clone()),
            engines.help_out(self.as_trait)
        )
    }
}
impl DebugWithEngines for QualifiedPathType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
        write!(f, "{}", engines.help_out(self),)
    }
}
#[derive(Debug, Clone)]
pub struct AmbiguousPathExpression {
    pub qualified_path_root: Option<QualifiedPathType>,
    pub call_path_binding: TypeBinding<CallPath<AmbiguousSuffix>>,
    pub args: Vec<Expression>,
}
#[derive(Debug, Clone)]
pub struct DelineatedPathExpression {
    pub call_path_binding: TypeBinding<QualifiedCallPath>,
    pub args: Option<Vec<Expression>>,
}
#[derive(Debug, Clone)]
pub struct AbiCastExpression {
    pub abi_name: CallPath,
    pub address: Box<Expression>,
}
#[derive(Debug, Clone)]
pub struct ArrayIndexExpression {
    pub prefix: Box<Expression>,
    pub index: Box<Expression>,
}
#[derive(Debug, Clone)]
pub struct StorageAccessExpression {
    pub field_names: Vec<Ident>,
    pub storage_keyword_span: Span,
}
#[derive(Debug, Clone)]
pub struct IntrinsicFunctionExpression {
    pub name: Ident,
    pub kind_binding: TypeBinding<Intrinsic>,
    pub arguments: Vec<Expression>,
}
#[derive(Debug, Clone)]
pub struct WhileLoopExpression {
    pub condition: Box<Expression>,
    pub body: CodeBlock,
}
#[derive(Debug, Clone)]
pub struct ForLoopExpression {
    pub desugared: Box<Expression>,
}
#[derive(Debug, Clone)]
pub struct ReassignmentExpression {
    pub lhs: ReassignmentTarget,
    pub rhs: Box<Expression>,
}
#[derive(Debug, Clone)]
pub enum ExpressionKind {
    Error(Box<[Span]>, ErrorEmitted),
    Literal(Literal),
    AmbiguousPathExpression(Box<AmbiguousPathExpression>),
    FunctionApplication(Box<FunctionApplicationExpression>),
    LazyOperator(LazyOperatorExpression),
    AmbiguousVariableExpression(Ident),
    Variable(Ident),
    Tuple(Vec<Expression>),
    TupleIndex(TupleIndexExpression),
    Array(ArrayExpression),
    Struct(Box<StructExpression>),
    CodeBlock(CodeBlock),
    If(IfExpression),
    Match(MatchExpression),
    Asm(Box<AsmExpression>),
    MethodApplication(Box<MethodApplicationExpression>),
    Subfield(SubfieldExpression),
    DelineatedPath(Box<DelineatedPathExpression>),
    AbiCast(Box<AbiCastExpression>),
    ArrayIndex(ArrayIndexExpression),
    StorageAccess(StorageAccessExpression),
    IntrinsicFunction(IntrinsicFunctionExpression),
    WhileLoop(WhileLoopExpression),
    ForLoop(ForLoopExpression),
    Break,
    Continue,
    Reassignment(ReassignmentExpression),
    ImplicitReturn(Box<Expression>),
    Return(Box<Expression>),
    Ref(RefExpression),
    Deref(Box<Expression>),
}
#[derive(Debug, Clone)]
pub struct RefExpression {
    pub to_mutable_value: bool,
    pub value: Box<Expression>,
}
#[derive(Debug, Clone)]
pub enum ReassignmentTarget {
    ElementAccess(Box<Expression>),
    Deref(Box<Expression>),
}
#[derive(Debug, Clone)]
pub struct StructExpressionField {
    pub name: Ident,
    pub value: Expression,
}
impl Spanned for Expression {
    fn span(&self) -> Span {
        self.span.clone()
    }
}
#[derive(Debug)]
pub(crate) struct Op {
    pub span: Span,
    pub op_variant: OpVariant,
}
impl Op {
    pub fn to_var_name(&self) -> Ident {
        Ident::new_with_override(self.op_variant.as_str().to_string(), self.span.clone())
    }
}
#[derive(Debug)]
pub enum OpVariant {
    Add,
    Subtract,
    Divide,
    Multiply,
    Modulo,
    Or,
    And,
    Equals,
    NotEquals,
    Xor,
    BinaryOr,
    BinaryAnd,
    GreaterThan,
    LessThan,
    GreaterThanOrEqualTo,
    LessThanOrEqualTo,
}
impl OpVariant {
    fn as_str(&self) -> &'static str {
        use OpVariant::*;
        match self {
            Add => "add",
            Subtract => "subtract",
            Divide => "divide",
            Multiply => "multiply",
            Modulo => "modulo",
            Or => "$or$",
            And => "$and$",
            Equals => "eq",
            NotEquals => "neq",
            Xor => "xor",
            BinaryOr => "binary_or",
            BinaryAnd => "binary_and",
            GreaterThan => "gt",
            LessThan => "lt",
            LessThanOrEqualTo => "le",
            GreaterThanOrEqualTo => "ge",
        }
    }
}