mod macros;
mod misc;
pub use misc::*;
pub mod util;
pub mod visitor;
pub(crate) mod lowfidelity;
pub use lowfidelity::{Ast, Node, NodeType, SourceLocation as LowFidelitySourceLocation};
pub mod yul;
use crate::serde_helpers;
use core::fmt;
use macros::{ast_node, expr_node, node_group, stmt_node};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use yul::YulBlock;
ast_node!(
    struct SourceUnit {
        #[serde(rename = "absolutePath")]
        absolute_path: String,
        #[serde(default, rename = "exportedSymbols")]
        exported_symbols: BTreeMap<String, Vec<usize>>,
        #[serde(default)]
        license: Option<String>,
        #[serde(default, skip_serializing_if = "Vec::is_empty")]
        nodes: Vec<SourceUnitPart>,
    }
);
node_group! {
    SourceUnitPart;
    PragmaDirective,
    ImportDirective,
    UsingForDirective,
    VariableDeclaration,
    EnumDefinition,
    ErrorDefinition,
    FunctionDefinition,
    StructDefinition,
    UserDefinedValueTypeDefinition,
    ContractDefinition,
}
node_group! {
    Expression;
    Assignment,
    BinaryOperation,
    Conditional,
    ElementaryTypeNameExpression,
    FunctionCall,
    FunctionCallOptions,
    Identifier,
    IndexAccess,
    IndexRangeAccess,
    Literal,
    MemberAccess,
    NewExpression,
    TupleExpression,
    UnaryOperation,
}
node_group! {
    Statement;
    Block,
    Break,
    Continue,
    DoWhileStatement,
    EmitStatement,
    ExpressionStatement,
    ForStatement,
    IfStatement,
    InlineAssembly,
    PlaceholderStatement,
    Return,
    RevertStatement,
    TryStatement,
    UncheckedBlock,
    VariableDeclarationStatement,
    WhileStatement,
}
node_group! {
    ContractDefinitionPart;
    EnumDefinition,
    ErrorDefinition,
    EventDefinition,
    FunctionDefinition,
    ModifierDefinition,
    StructDefinition,
    UserDefinedValueTypeDefinition,
    UsingForDirective,
    VariableDeclaration,
}
node_group! {
    TypeName;
    ArrayTypeName,
    ElementaryTypeName,
    FunctionTypeName,
    Mapping,
    UserDefinedTypeName,
}
node_group! {
    UserDefinedTypeNameOrIdentifierPath;
    UserDefinedTypeName,
    IdentifierPath,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BlockOrStatement {
    Statement(Statement),
    Block(Block),
}
node_group! {
    ExpressionOrVariableDeclarationStatement;
    ExpressionStatement,
    VariableDeclarationStatement
}
node_group! {
    IdentifierOrIdentifierPath;
    Identifier,
    IdentifierPath
}
ast_node!(
    struct ContractDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        #[serde(default, rename = "abstract")]
        is_abstract: bool,
        base_contracts: Vec<InheritanceSpecifier>,
        canonical_name: Option<String>,
        contract_dependencies: Vec<usize>,
        #[serde(rename = "contractKind")]
        kind: ContractKind,
        documentation: Option<StructuredDocumentation>,
        fully_implemented: bool,
        linearized_base_contracts: Vec<usize>,
        nodes: Vec<ContractDefinitionPart>,
        scope: usize,
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        used_errors: Vec<usize>,
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        used_events: Vec<usize>,
        #[serde(default, rename = "internalFunctionIDs")]
        internal_function_ids: BTreeMap<String, usize>,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ContractKind {
    Contract,
    Interface,
    Library,
}
ast_node!(
    struct InheritanceSpecifier {
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        arguments: Vec<Expression>,
        base_name: UserDefinedTypeNameOrIdentifierPath,
    }
);
expr_node!(
    struct Assignment {
        #[serde(rename = "leftHandSide")]
        lhs: Expression,
        operator: AssignmentOperator,
        #[serde(rename = "rightHandSide")]
        rhs: Expression,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AssignmentOperator {
    #[serde(rename = "=")]
    Assign,
    #[serde(rename = "+=")]
    AddAssign,
    #[serde(rename = "-=")]
    SubAssign,
    #[serde(rename = "*=")]
    MulAssign,
    #[serde(rename = "/=")]
    DivAssign,
    #[serde(rename = "%=")]
    ModAssign,
    #[serde(rename = "|=")]
    OrAssign,
    #[serde(rename = "&=")]
    AndAssign,
    #[serde(rename = "^=")]
    XorAssign,
    #[serde(rename = ">>=")]
    ShrAssign,
    #[serde(rename = "<<=")]
    ShlAssign,
}
ast_node!(
    struct BinaryOperation {
        common_type: TypeDescriptions,
        #[serde(rename = "leftExpression")]
        lhs: Expression,
        operator: BinaryOperator,
        #[serde(rename = "rightExpression")]
        rhs: Expression,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum BinaryOperator {
    #[serde(rename = "+")]
    Add,
    #[serde(rename = "-")]
    Sub,
    #[serde(rename = "*")]
    Mul,
    #[serde(rename = "/")]
    Div,
    #[serde(rename = "%")]
    Mod,
    #[serde(rename = "**")]
    Pow,
    #[serde(rename = "&&")]
    And,
    #[serde(rename = "||")]
    Or,
    #[serde(rename = "!=")]
    NotEqual,
    #[serde(rename = "==")]
    Equal,
    #[serde(rename = "<")]
    LessThan,
    #[serde(rename = "<=")]
    LessThanOrEqual,
    #[serde(rename = ">")]
    GreaterThan,
    #[serde(rename = ">=")]
    GreaterThanOrEqual,
    #[serde(rename = "^")]
    Xor,
    #[serde(rename = "~")]
    BitNot,
    #[serde(rename = "&")]
    BitAnd,
    #[serde(rename = "|")]
    BitOr,
    #[serde(rename = "<<")]
    Shl,
    #[serde(rename = ">>")]
    Shr,
}
expr_node!(
    struct Conditional {
        condition: Expression,
        false_expression: Expression,
        true_expression: Expression,
    }
);
expr_node!(
    struct ElementaryTypeNameExpression {
        type_name: ElementaryOrRawTypeName,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ElementaryOrRawTypeName {
    ElementaryTypeName(ElementaryTypeName),
    Raw(String),
}
ast_node!(
    struct ElementaryTypeName {
        type_descriptions: TypeDescriptions,
        name: String,
        state_mutability: Option<StateMutability>,
    }
);
expr_node!(
    struct FunctionCall {
        arguments: Vec<Expression>,
        expression: Expression,
        kind: FunctionCallKind,
        names: Vec<String>,
        #[serde(default)]
        try_call: bool,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum FunctionCallKind {
    FunctionCall,
    TypeConversion,
    StructConstructorCall,
}
expr_node!(
    struct FunctionCallOptions {
        expression: Expression,
        names: Vec<String>,
        options: Vec<Expression>,
    }
);
ast_node!(
    struct Identifier {
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        argument_types: Vec<TypeDescriptions>,
        name: String,
        overloaded_declarations: Vec<isize>,
        referenced_declaration: Option<isize>,
        type_descriptions: TypeDescriptions,
    }
);
expr_node!(
    struct IndexAccess {
        base_expression: Expression,
        index_expression: Option<Expression>,
    }
);
expr_node!(
    struct IndexRangeAccess {
        base_expression: Expression,
        start_expression: Option<Expression>,
        end_expression: Option<Expression>,
    }
);
expr_node!(
    struct Literal {
        hex_value: String,
        kind: LiteralKind,
        subdenomination: Option<String>, value: Option<String>,           }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum LiteralKind {
    Bool,
    Number,
    String,
    HexString,
    UnicodeString,
}
expr_node!(
    struct MemberAccess {
        expression: Expression,
        member_name: String,
        referenced_declaration: Option<isize>,
    }
);
expr_node!(
    struct NewExpression {
        type_name: TypeName,
    }
);
ast_node!(
    struct ArrayTypeName {
        type_descriptions: TypeDescriptions,
        base_type: TypeName,
        length: Option<Expression>,
    }
);
ast_node!(
    struct FunctionTypeName {
        type_descriptions: TypeDescriptions,
        parameter_types: ParameterList,
        return_parameter_types: ParameterList,
        state_mutability: StateMutability,
        visibility: Visibility,
    }
);
ast_node!(
    struct ParameterList {
        parameters: Vec<VariableDeclaration>,
    }
);
ast_node!(
    struct VariableDeclaration {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        base_functions: Vec<usize>,
        #[serde(default)]
        constant: bool,
        #[serde(default)]
        state_variable: bool,
        documentation: Option<StructuredDocumentation>,
        function_selector: Option<String>, #[serde(default)]
        indexed: bool,
        #[serde(default)]
        mutability: Option<Mutability>,
        overrides: Option<OverrideSpecifier>,
        scope: usize,
        storage_location: StorageLocation,
        type_descriptions: TypeDescriptions,
        type_name: Option<TypeName>,
        value: Option<Expression>,
        visibility: Visibility,
    }
);
impl VariableDeclaration {
    pub fn mutability(&self) -> &Mutability {
        if let Some(mutability) = &self.mutability {
            mutability
        } else if self.constant {
            &Mutability::Constant
        } else if self.state_variable {
            &Mutability::Mutable
        } else {
            unreachable!()
        }
    }
}
ast_node!(
    struct StructuredDocumentation {
        text: String,
    }
);
ast_node!(
    struct OverrideSpecifier {
        overrides: Vec<UserDefinedTypeNameOrIdentifierPath>,
    }
);
ast_node!(
    struct UserDefinedTypeName {
        type_descriptions: TypeDescriptions,
        contract_scope: Option<String>, name: Option<String>,
        path_node: Option<IdentifierPath>,
        referenced_declaration: isize,
    }
);
ast_node!(
    struct IdentifierPath {
        name: String,
        referenced_declaration: isize,
    }
);
ast_node!(
    struct Mapping {
        type_descriptions: TypeDescriptions,
        key_type: TypeName,
        value_type: TypeName,
    }
);
expr_node!(
    struct TupleExpression {
        components: Vec<Option<Expression>>,
        is_inline_array: bool,
    }
);
expr_node!(
    struct UnaryOperation {
        operator: UnaryOperator,
        prefix: bool,
        sub_expression: Expression,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum UnaryOperator {
    #[serde(rename = "++")]
    Increment,
    #[serde(rename = "--")]
    Decrement,
    #[serde(rename = "-")]
    Negate,
    #[serde(rename = "!")]
    Not,
    #[serde(rename = "~")]
    BitNot,
    #[serde(rename = "delete")]
    Delete,
}
ast_node!(
    struct EnumDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        canonical_name: String,
        members: Vec<EnumValue>,
    }
);
ast_node!(
    struct EnumValue {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
    }
);
ast_node!(
    struct ErrorDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        documentation: Option<StructuredDocumentation>,
        error_selector: Option<String>, parameters: ParameterList,
    }
);
ast_node!(
    struct EventDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        anonymous: bool,
        event_selector: Option<String>, documentation: Option<StructuredDocumentation>,
        parameters: ParameterList,
    }
);
ast_node!(
    struct FunctionDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        base_functions: Vec<usize>,
        body: Option<Block>,
        documentation: Option<StructuredDocumentation>,
        function_selector: Option<String>, implemented: bool,
        modifiers: Vec<ModifierInvocation>,
        overrides: Option<OverrideSpecifier>,
        parameters: ParameterList,
        return_parameters: ParameterList,
        scope: usize,
        visibility: Visibility,
        kind: Option<FunctionKind>,
        #[serde(default)]
        state_mutability: Option<StateMutability>,
        #[serde(default, rename = "virtual")]
        is_virtual: bool,
        #[serde(default)]
        is_constructor: bool,
        #[serde(default)]
        is_declared_const: bool,
        #[serde(default)]
        is_payable: bool,
    }
);
impl FunctionDefinition {
    pub fn kind(&self) -> &FunctionKind {
        if let Some(kind) = &self.kind {
            kind
        } else if self.is_constructor {
            &FunctionKind::Constructor
        } else {
            &FunctionKind::Function
        }
    }
    pub fn state_mutability(&self) -> &StateMutability {
        if let Some(state_mutability) = &self.state_mutability {
            state_mutability
        } else if self.is_declared_const {
            &StateMutability::View
        } else if self.is_payable {
            &StateMutability::Payable
        } else {
            &StateMutability::Nonpayable
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum FunctionKind {
    Function,
    Receive,
    Constructor,
    Fallback,
    FreeFunction,
}
ast_node!(
    struct Block {
        documentation: Option<String>,
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        statements: Vec<Statement>,
    }
);
stmt_node!(
    struct Break {}
);
stmt_node!(
    struct Continue {}
);
stmt_node!(
    struct DoWhileStatement {
        body: Block,
        condition: Expression,
    }
);
stmt_node!(
    struct EmitStatement {
        event_call: FunctionCall,
    }
);
stmt_node!(
    struct ExpressionStatement {
        expression: Expression,
    }
);
stmt_node!(
    struct ForStatement {
        body: BlockOrStatement,
        condition: Option<Expression>,
        initialization_expression: Option<ExpressionOrVariableDeclarationStatement>,
        loop_expression: Option<ExpressionStatement>,
    }
);
stmt_node!(
    struct VariableDeclarationStatement {
        assignments: Vec<Option<usize>>,
        declarations: Vec<Option<VariableDeclaration>>,
        initial_value: Option<Expression>,
    }
);
stmt_node!(
    struct IfStatement {
        condition: Expression,
        false_body: Option<BlockOrStatement>,
        true_body: BlockOrStatement,
    }
);
ast_node!(
    struct InlineAssembly {
        documentation: Option<String>,
        #[serde(rename = "AST")]
        ast: YulBlock,
        external_references: Vec<ExternalInlineAssemblyReference>,
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        flags: Vec<InlineAssemblyFlag>,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalInlineAssemblyReference {
    #[serde(with = "serde_helpers::display_from_str")]
    pub src: SourceLocation,
    pub declaration: usize,
    #[serde(default)]
    pub offset: bool,
    #[serde(default)]
    pub slot: bool,
    #[serde(default)]
    pub length: bool,
    pub value_size: usize,
    pub suffix: Option<AssemblyReferenceSuffix>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AssemblyReferenceSuffix {
    Slot,
    Offset,
    Length,
}
impl fmt::Display for AssemblyReferenceSuffix {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Slot => f.write_str("slot"),
            Self::Offset => f.write_str("offset"),
            Self::Length => f.write_str("length"),
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum InlineAssemblyFlag {
    #[serde(rename = "memory-safe")]
    MemorySafe,
}
stmt_node!(
    struct PlaceholderStatement {}
);
stmt_node!(
    struct Return {
        expression: Option<Expression>,
        function_return_parameters: usize,
    }
);
stmt_node!(
    struct RevertStatement {
        error_call: FunctionCall,
    }
);
stmt_node!(
    struct TryStatement {
        clauses: Vec<TryCatchClause>,
        external_call: FunctionCall,
    }
);
ast_node!(
    struct TryCatchClause {
        block: Block,
        error_name: String,
        parameters: Option<ParameterList>,
    }
);
stmt_node!(
    struct UncheckedBlock {
        statements: Vec<Statement>,
    }
);
stmt_node!(
    struct WhileStatement {
        body: BlockOrStatement,
        condition: Expression,
    }
);
ast_node!(
    struct ModifierInvocation {
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        arguments: Vec<Expression>,
        kind: Option<ModifierInvocationKind>,
        modifier_name: IdentifierOrIdentifierPath,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ModifierInvocationKind {
    ModifierInvocation,
    BaseConstructorSpecifier,
}
ast_node!(
    struct ModifierDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        base_modifiers: Vec<usize>,
        body: Block,
        documentation: Option<StructuredDocumentation>,
        overrides: Option<OverrideSpecifier>,
        parameters: ParameterList,
        #[serde(default, rename = "virtual")]
        is_virtual: bool,
        visibility: Visibility,
    }
);
ast_node!(
    struct StructDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        canonical_name: String,
        members: Vec<VariableDeclaration>,
        scope: usize,
        visibility: Visibility,
    }
);
ast_node!(
    struct UserDefinedValueTypeDefinition {
        name: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        canonical_name: Option<String>,
        underlying_type: TypeName,
    }
);
ast_node!(
    struct UsingForDirective {
        #[serde(default, deserialize_with = "serde_helpers::default_for_null")]
        function_list: Vec<UsingForFunctionItem>,
        #[serde(default)]
        global: bool,
        library_name: Option<UserDefinedTypeNameOrIdentifierPath>,
        type_name: Option<TypeName>,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UsingForFunctionItem {
    Function(FunctionIdentifierPath),
    OverloadedOperator(OverloadedOperator),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FunctionIdentifierPath {
    pub function: IdentifierPath,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OverloadedOperator {
    pub definition: IdentifierPath,
    pub operator: String,
}
ast_node!(
    struct ImportDirective {
        absolute_path: String,
        file: String,
        #[serde(default, with = "serde_helpers::display_from_str_opt")]
        name_location: Option<SourceLocation>,
        scope: usize,
        source_unit: usize,
        symbol_aliases: Vec<SymbolAlias>,
        unit_alias: String,
    }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SymbolAlias {
    pub foreign: Identifier,
    pub local: Option<String>,
    #[serde(default, with = "serde_helpers::display_from_str_opt")]
    pub name_location: Option<SourceLocation>,
}
ast_node!(
    struct PragmaDirective {
        literals: Vec<String>,
    }
);
#[cfg(test)]
mod tests {
    use super::*;
    use std::{fs, path::PathBuf};
    #[test]
    fn can_parse_ast() {
        fs::read_dir(
            PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../test-data").join("ast"),
        )
        .unwrap()
        .for_each(|path| {
            let path = path.unwrap().path();
            let path_str = path.to_string_lossy();
            let input = fs::read_to_string(&path).unwrap();
            let deserializer = &mut serde_json::Deserializer::from_str(&input);
            let result: Result<SourceUnit, _> = serde_path_to_error::deserialize(deserializer);
            match result {
                Err(e) => {
                    println!("... {path_str} fail: {e}");
                    panic!();
                }
                Ok(_) => {
                    println!("... {path_str} ok");
                }
            }
        })
    }
}