use crate::{
    error::Error,
    interpreter::{
        client_types::ClientTypes,
        consts::{HandledConst, UnhandledConst},
        data::StructId,
        data_store::SimpleAddress,
        fn_data::Closure,
        function::Function,
        generic_context::GenericContext,
        interpreter_backtrace::Backtrace,
        interpreter_statics::UnhandledStruct,
        memory::Memory,
        namespace::{Namespace, NamespacedCollection, Progress, TypeAndGenerics, UnHandledType},
        scope::AllScopeInfo,
    },
    profile::{CodeStore, Profile},
    utils::Ptr,
};
use indexmap::IndexSet;
use std::collections::HashMap;

/// Interaction with the client TypeScript library.
mod client_types;
/// Constants and their handling.
mod consts;
/// The `Data` enum which is the basic `Memory` item and some structs used in its variants.
mod data;
/// A trait used by `Memory`.
mod data_store;
/// The `FnData` enum which is used in `Data::Fn(FnData)`.
mod fn_data;
/// Functions and their handling.
mod function;
/// `GenericContext` stores values of the different generics.
mod generic_context;
/// The `#[instruction]` inputs.
mod inputs;
/// Methods of `Interpreter` regarding backtrace.
mod interpreter_backtrace;
/// Methods of `Interpreter` regarding bindings (variables and their values).
mod interpreter_bindings;
/// Contains the constructor of `Interpreter`.
mod interpreter_constructor;
/// Methods of `Interpreter` regarding conversions.
mod interpreter_conversion;
/// Methods of `Interpreter` regarding generics.
mod interpreter_generics;
/// Methods of `Interpreter` regarding macros.
mod interpreter_macros;
/// Methods of `Interpreter` regarding patterns.
mod interpreter_pat;
/// Methods of `Interpreter` regarding profiling (to support `arcium profile`).
mod interpreter_profile;
/// Methods of `Interpreter` regarding using the `GenericContext` to identify types.
mod interpreter_refine;
/// Methods of `Interpreter` that should really be pub. The entry point is there.
mod interpreter_result;
/// Methods of `Interpreter` to run expressions and statements.
mod interpreter_run;
/// Methods of `Interpreter` regarding statics (Consts, Structs, Functions).
mod interpreter_statics;
/// Methods of `Interpreter` regarding types.
mod interpreter_typing;
/// Methods of `Interpreter` that did not fit elsewhere.
mod interpreter_utils;
/// Methods of `Interpreter` regarding `ArcisValue`s.
mod interpreter_values;
/// Implementation of syn::Visit for `Interpreter`
mod interpreter_visit;
/// A folder of `Interpreter`-implemented functions.
mod library;
/// `Memory`, a glorified `Vec<Data>`. The memory.
mod memory;
/// Traits regarding namespaced collections (consts, functions, modules, types).
mod namespace;
/// `#[instruction]` outputs.
mod outputs;
/// The `Pointer` enum which is used in `Data::Pointer(Pointer)`. Actually represents a reference.
mod pointer;
/// `Scope`, used to define scopes.
mod scope;
/// `SupportedType` enum, the types as understood by the `Interpreter`.
mod supported_type;
/// Builds the test code.
/// The test here is the one checking for similarity (equality except for floats) of outputs between
/// the rust code and the MPC circuit on the same valid inputs when the rust code does not panic.
mod test_code;

pub type DependencyGraph<'ast> =
    HashMap<Ptr<'ast, syn::File>, Vec<(&'ast str, Option<Ptr<'ast, syn::File>>)>>;
pub type ParsedMacroStore<'ast> = HashMap<syn::Macro, &'ast [syn::Expr]>;

#[derive(Debug, Default)]
pub struct Interpreter<'ast> {
    /// Contains the already parsed macros.
    parsed_macros: ParsedMacroStore<'ast>,
    /// A closure => closure_id hash map.
    /// This was made in order to have no lifetime on the `Data` enum,
    /// which uses these ids in `Data::Fn(FnData::Closure(id, ...))`.
    closures: IndexSet<Closure<'ast>>,
    /// Where (which namespace) the functions are. The `usize` is an index in `functions`.
    function_index: NamespacedCollection<'ast, usize>,
    /// The functions themselves.
    /// Currently, the functions are picked up straight from the AST with no transformation.
    /// Hence, the `()`.
    functions: Vec<Progress<(), Function<'ast>>>,
    /// The generics on structs.
    /// This was made in order to have no lifetime on the `ArcisStruct` struct.
    struct_generics: Vec<&'ast syn::Generics>,
    /// The structs themselves.
    /// They are either taken from the AST or already transformed into `StructId`s.
    /// They can be found from a path through `namespaces`, which gives a `types` id.
    structs: Vec<Progress<UnhandledStruct<'ast>, StructId>>,
    /// Where (which namespace) the consts are. The `usize` is an index in `consts`.
    const_index: NamespacedCollection<'ast, usize>,
    /// The consts themselves.
    /// They are either taken from the AST or already transformed into `HandledConst`s.
    consts: Vec<Progress<UnhandledConst<'ast>, HandledConst>>,
    /// The values of types.
    /// In `type T<G> = U`, contains either `G` and `U`
    /// or already transformed into  `G` and a `SupportedType` equal to `U`.
    /// Same for `impl<G> U { ... }`.
    /// Also contains the values of the type alias `Self`.
    types: Vec<Progress<UnHandledType<'ast>, TypeAndGenerics<'ast>>>,
    /// The namespaces. Either modules (`mod module {...}`) or types (`struct MyStruct`).
    namespaces: NamespacedCollection<'ast, Namespace<'ast>>,
    /// Contains some code from the AST. Used by the profiling.
    pub code_store: CodeStore,
    /// Information about profiling.
    profile: Profile,
    /// The types for the TypeScript client.
    pub client_types: ClientTypes,
    /// List of errors.
    /// Errors (unsupported stuff) found while `syn::Visit`ing the AST are put here.
    pub errors: Vec<Error>,
    /// The info about every scope, and also allows to get the current scope.
    scope_info: AllScopeInfo<'ast>,
    /// The memory. Contains all the `Data`.
    memory: Memory,
    /// The values of the generic parameters in the current execution.
    generic_context: GenericContext,
    /// The variable in scope of the current execution.
    bindings: HashMap<syn::Ident, SimpleAddress>,
    /// The backtrace of the current execution.
    backtrace: Backtrace<'ast>,
}
