use crate::high_level_ir::type_resolver::arena::ResolverArena;
use crate::high_level_ir::type_resolver::declaration::{DeclarationItem, DeclarationItemKind};
use crate::middle_level_ir::context::HLIR2MLIRContext;
use core::result;
use std::collections::HashMap;
use std::error::Error;
use wiz_constants as constants;
use wiz_constants::annotation::{BUILTIN, NO_MANGLE};
use wiz_hir::typed_annotation::TypedAnnotations;
use wiz_hir::typed_decl::{
    TypedArgDef, TypedDecl, TypedDeclKind, TypedExtension, TypedFun, TypedFunBody,
    TypedMemberFunction, TypedProtocol, TypedStruct, TypedVar,
};
use wiz_hir::typed_expr::{
    TypedArray, TypedBinOp, TypedBinaryOperator, TypedCall, TypedCallArg, TypedExprKind, TypedIf,
    TypedInstanceMember, TypedLiteralKind, TypedName, TypedPrefixUnaryOperator, TypedReturn,
    TypedSubscript, TypedTypeCast, TypedUnaryOp,
};
use wiz_hir::typed_file::{TypedFile, TypedSourceSet};
use wiz_hir::typed_stmt::{
    TypedAssignmentAndOperator, TypedAssignmentStmt, TypedBlock, TypedLoopStmt, TypedStmt,
};
use wiz_hir::typed_type::{
    Package, TypedFunctionType, TypedPackage, TypedType, TypedTypeParam, TypedValueType,
};
use wiz_mir::builder::{FunBuilder, MLIRModule};
use wiz_mir::expr::{
    MLArray, MLBinOp, MLBinOpKind, MLBlock, MLCall, MLCallArg, MLExpr, MLIf, MLLiteral,
    MLLiteralKind, MLMember, MLName, MLSubscript, MLTypeCast, MLUnaryOp, MLUnaryOpKind,
};
use wiz_mir::ml_decl::{MLArgDef, MLDecl, MLField, MLFun, MLFunBody, MLStruct, MLVar};
use wiz_mir::ml_file::MLFile;
use wiz_mir::ml_type::{MLFunctionType, MLPrimitiveType, MLType, MLValueType};
use wiz_mir::statement::{MLAssignmentStmt, MLLoopStmt, MLReturn, MLStmt};

mod context;
#[cfg(test)]
mod tests;

pub type Result<T> = result::Result<T, Box<dyn Error>>;

pub fn hlir2mlir<'arena>(
    target: TypedSourceSet,
    dependencies: &'arena [MLFile],
    arena: &'arena ResolverArena,
) -> Result<MLFile> {
    let mut converter = HLIR2MLIR::new(arena);
    converter.load_dependencies(dependencies)?;
    Ok(converter.convert_from_source_set(target))
}

#[derive(Debug)]
pub struct HLIR2MLIR<'arena> {
    arena: &'arena ResolverArena,
    context: HLIR2MLIRContext,
    module: MLIRModule,
}

impl<'arena> HLIR2MLIR<'arena> {
    pub fn new(arena: &'arena ResolverArena) -> Self {
        Self {
            arena,
            context: Default::default(),
            module: Default::default(),
        }
    }

    pub fn load_dependencies(&mut self, dependencies: &[MLFile]) -> Result<()> {
        for dependency in dependencies {
            self.load_dependencies_file(dependency)?;
        }
        Ok(())
    }

    fn load_dependencies_file(&mut self, f: &MLFile) -> Result<()> {
        f.body
            .iter()
            .try_for_each(|d| self.load_dependencies_decl(d))
    }

    fn load_dependencies_decl(&mut self, d: &MLDecl) -> Result<()> {
        match d {
            MLDecl::Var(v) => self.load_dependencies_var(v),
            MLDecl::Fun(f) => self.load_dependencies_function(f),
            MLDecl::Struct(s) => self.load_dependencies_struct(s),
        }
    }

    fn load_dependencies_var(&mut self, v: &MLVar) -> Result<()> {
        self.module.add_global_var(v.clone());
        Ok(())
    }

    fn load_dependencies_struct(&mut self, s: &MLStruct) -> Result<()> {
        self.module.add_struct(s.clone());
        self.context
            .add_struct(MLValueType::Struct(s.name.clone()), s.clone());
        Ok(())
    }

    fn load_dependencies_function(&mut self, f: &MLFun) -> Result<()> {
        if f.body.is_none() {
            self.module._add_function(FunBuilder::from(f.clone()));
        };
        Ok(())
    }

    pub fn convert_from_source_set(&mut self, s: TypedSourceSet) -> MLFile {
        self.source_set(s).unwrap()
    }

    fn type_(&self, t: TypedType) -> MLType {
        match t {
            TypedType::Value(t) => MLType::Value(self.value_type(t)),
            TypedType::Function(f) => MLType::Function(self.function_type(*f)),
            _ => panic!("Invalid Type convert  {:?}", t),
        }
    }

    fn value_type(&self, t: TypedValueType) -> MLValueType {
        match t {
            TypedValueType::Value(t) => {
                let mut pkg = t.package.clone().into_resolved().names;
                if pkg.is_empty() {
                    match &*t.name {
                        constants::NOTING => MLValueType::Primitive(MLPrimitiveType::Noting),
                        constants::UNIT => MLValueType::Primitive(MLPrimitiveType::Unit),
                        constants::INT8 => MLValueType::Primitive(MLPrimitiveType::Int8),
                        constants::UINT8 => MLValueType::Primitive(MLPrimitiveType::UInt8),
                        constants::INT16 => MLValueType::Primitive(MLPrimitiveType::Int16),
                        constants::UINT16 => MLValueType::Primitive(MLPrimitiveType::UInt16),
                        constants::INT32 => MLValueType::Primitive(MLPrimitiveType::Int32),
                        constants::UINT32 => MLValueType::Primitive(MLPrimitiveType::UInt32),
                        constants::INT64 => MLValueType::Primitive(MLPrimitiveType::Int64),
                        constants::UINT64 => MLValueType::Primitive(MLPrimitiveType::UInt64),
                        constants::INT128 => MLValueType::Primitive(MLPrimitiveType::Int128),
                        constants::UINT128 => MLValueType::Primitive(MLPrimitiveType::UInt128),
                        constants::SIZE => MLValueType::Primitive(MLPrimitiveType::Size),
                        constants::USIZE => MLValueType::Primitive(MLPrimitiveType::USize),
                        constants::BOOL => MLValueType::Primitive(MLPrimitiveType::Bool),
                        constants::F32 => MLValueType::Primitive(MLPrimitiveType::Float),
                        constants::F64 => MLValueType::Primitive(MLPrimitiveType::Double),
                        constants::STRING => MLValueType::Primitive(MLPrimitiveType::String),
                        other => {
                            pkg.push(String::from(other));
                            MLValueType::Struct(pkg.join("::"))
                        }
                    }
                } else {
                    pkg.push(t.name);
                    MLValueType::Struct(pkg.join("::"))
                }
            }
            TypedValueType::Array(t, len) => {
                MLValueType::Array(Box::new(self.type_(*t).into_value_type()), len)
            }
            TypedValueType::Tuple(_) => {
                todo!()
            }
            TypedValueType::Pointer(t) => MLValueType::Pointer(Box::new(self.type_(*t))),
            TypedValueType::Reference(t) => MLValueType::Reference(Box::new(self.type_(*t))),
        }
    }

    fn function_type(&self, t: TypedFunctionType) -> MLFunctionType {
        MLFunctionType {
            arguments: t
                .arguments
                .into_iter()
                .map(|a| match self.type_(a.typ) {
                    MLType::Value(v) => v,
                    MLType::Function(f) => todo!("{:?}", f),
                })
                .collect(),
            return_type: match self.type_(t.return_type) {
                MLType::Value(v) => v,
                MLType::Function(f) => todo!("{:?}", f),
            },
        }
    }

    fn source_set(&mut self, s: TypedSourceSet) -> Result<MLFile> {
        let name = match s {
            TypedSourceSet::File(f) => {
                let name = f.name.clone();
                self.file(f)?;
                name
            }
            TypedSourceSet::Dir { name, mut items } => {
                items.sort();
                let _: Vec<_> = items
                    .into_iter()
                    .map(|i| self.source_set(i))
                    .collect::<Result<Vec<_>>>()?
                    .into_iter()
                    .flat_map(|i| i.body)
                    .collect();
                name
            }
        };
        Ok(self.module.to_mlir_file(name))
    }

    fn file(&mut self, f: TypedFile) -> Result<()> {
        f.body.into_iter().try_for_each(|d| self.decl(d))
    }

    fn stmt(&mut self, s: TypedStmt) -> Vec<MLStmt> {
        match s {
            TypedStmt::Expr(e) => vec![MLStmt::Expr(self.expr(e))],
            TypedStmt::Decl(d) => match d.kind {
                TypedDeclKind::Var(v) => {
                    vec![MLStmt::Var(self.var(v))]
                }
                TypedDeclKind::Fun(_) => todo!("local function"),
                TypedDeclKind::Struct(_) => todo!("local struct"),
                TypedDeclKind::Class => {
                    todo!()
                }
                TypedDeclKind::Enum => {
                    todo!()
                }
                TypedDeclKind::Protocol(_) => todo!("local protocol"),
                TypedDeclKind::Extension(_) => todo!("local extension"),
            },
            TypedStmt::Assignment(a) => vec![MLStmt::Assignment(self.assignment(a))],
            TypedStmt::Loop(l) => vec![MLStmt::Loop(self.loop_stmt(l))],
        }
    }

    fn assignment(&mut self, a: TypedAssignmentStmt) -> MLAssignmentStmt {
        match a {
            TypedAssignmentStmt::Assignment(a) => MLAssignmentStmt {
                target: self.expr(a.target),
                value: self.expr(a.value),
            },
            TypedAssignmentStmt::AssignmentAndOperation(a) => {
                let target = self.expr(a.target.clone());
                let value = TypedExprKind::BinOp(TypedBinOp {
                    left: Box::new(a.target.clone()),
                    operator: match a.operator {
                        TypedAssignmentAndOperator::Add => TypedBinaryOperator::Add,
                        TypedAssignmentAndOperator::Sub => TypedBinaryOperator::Sub,
                        TypedAssignmentAndOperator::Mul => TypedBinaryOperator::Mul,
                        TypedAssignmentAndOperator::Div => TypedBinaryOperator::Div,
                        TypedAssignmentAndOperator::Mod => TypedBinaryOperator::Mod,
                    },
                    right: Box::new(a.value),
                    type_: a.target.type_(),
                });
                MLAssignmentStmt {
                    target,
                    value: self.expr(value),
                }
            }
        }
    }

    fn loop_stmt(&mut self, l: TypedLoopStmt) -> MLLoopStmt {
        match l {
            TypedLoopStmt::While(w) => MLLoopStmt {
                condition: self.expr(w.condition),
                block: self.block(w.block),
            },
            TypedLoopStmt::For(_) => todo!(),
        }
    }

    fn decl(&mut self, d: TypedDecl) -> Result<()> {
        let TypedDecl {
            annotations,
            package,
            modifiers,
            kind,
        } = d;
        match kind {
            TypedDeclKind::Var(v) => {
                let v = self.global_var(v, &package);
                self.module.add_global_var(v);
            }
            TypedDeclKind::Fun(f) => {
                if !f.is_generic() {
                    let f = FunBuilder::from(self.fun(f, annotations, package, None));
                    self.module._add_function(f);
                }
            }
            TypedDeclKind::Struct(s) => {
                let (st, fns) = self.struct_(s, package);
                self.module.add_struct(st);
                for f in fns {
                    self.module._add_function(FunBuilder::from(f));
                }
            }
            TypedDeclKind::Class => todo!(),
            TypedDeclKind::Enum => todo!(),
            TypedDeclKind::Protocol(p) => {
                let functions = self.protocol(p);
                for f in functions {
                    self.module._add_function(FunBuilder::from(f));
                }
            }
            TypedDeclKind::Extension(e) => {
                let functions = self.extension(e);
                for f in functions {
                    self.module._add_function(FunBuilder::from(f));
                }
            }
        };
        Ok(())
    }

    fn global_var(&mut self, v: TypedVar, package: &Package) -> MLVar {
        let expr = self.expr(v.value);
        MLVar {
            is_mute: v.is_mut,
            name: self.package_name_mangling_(package, &v.name),
            type_: self.type_(v.type_.unwrap()),
            value: expr,
        }
    }

    fn var(&mut self, v: TypedVar) -> MLVar {
        let expr = self.expr(v.value);
        MLVar {
            is_mute: v.is_mut,
            name: v.name,
            type_: self.type_(v.type_.unwrap()),
            value: expr,
        }
    }

    fn fun(
        &mut self,
        f: TypedFun,
        annotations: TypedAnnotations,
        package: Package,
        type_arguments: Option<HashMap<TypedTypeParam, TypedType>>,
    ) -> MLFun {
        let TypedFun {
            name,
            type_params,
            type_constraints: _,
            arg_defs,
            body,
            return_type,
        } = f;
        let package_mangled_name = self.package_name_mangling_(&package, &name);
        let mangled_name = if annotations.has_annotate(NO_MANGLE) {
            name
        } else {
            let fun_arg_label_type_mangled_name = self.fun_arg_label_type_name_mangling(&arg_defs);
            if fun_arg_label_type_mangled_name.is_empty() {
                package_mangled_name
            } else {
                package_mangled_name + "##" + &*fun_arg_label_type_mangled_name
            }
        };
        let args = arg_defs.into_iter().map(|a| self.arg_def(a)).collect();
        MLFun {
            name: mangled_name,
            arg_defs: args,
            return_type: self.type_(return_type.unwrap()).into_value_type(),
            body: body.map(|b| self.fun_body(b)),
        }
    }

    fn struct_(&mut self, s: TypedStruct, package: Package) -> (MLStruct, Vec<MLFun>) {
        let TypedStruct {
            name,
            type_params,
            stored_properties,
            computed_properties,
            member_functions,
        } = s;
        let struct_ = MLStruct {
            name: self.package_name_mangling_(&package, &name),
            fields: stored_properties
                .into_iter()
                .map(|p| MLField {
                    name: p.name,
                    type_: self.type_(p.type_).into_value_type(),
                })
                .collect(),
        };
        let value_type = MLValueType::Struct(struct_.name.clone());
        self.context.add_struct(value_type.clone(), struct_.clone());

        let members: Vec<MLFun> = member_functions
            .into_iter()
            .map(|mf| {
                let TypedMemberFunction {
                    name: fname,
                    arg_defs: args,
                    type_params,
                    body,
                    return_type,
                } = mf;
                let fun_arg_label_type_mangled_name = self.fun_arg_label_type_name_mangling(&args);
                let args = args.into_iter().map(|a| self.arg_def(a)).collect();
                MLFun {
                    name: self.package_name_mangling_(&package, &name)
                        + "::"
                        + &fname
                        + &*if fun_arg_label_type_mangled_name.is_empty() {
                            String::new()
                        } else {
                            String::from("##") + &*fun_arg_label_type_mangled_name
                        },
                    arg_defs: args,
                    return_type: self.type_(return_type.unwrap()).into_value_type(),
                    body: body.map(|body| self.fun_body(body)),
                }
            })
            .collect();
        (struct_, members)
    }

    fn extension(&mut self, e: TypedExtension) -> Vec<MLFun> {
        let TypedExtension {
            name,
            protocol,
            computed_properties,
            member_functions,
        } = e;
        member_functions
            .into_iter()
            .map(|mf| {
                let TypedMemberFunction {
                    name: fname,
                    arg_defs: args,
                    type_params,
                    body,
                    return_type,
                } = mf;
                let fun_arg_label_type_mangled_name = self.fun_arg_label_type_name_mangling(&args);
                let args = args.into_iter().map(|a| self.arg_def(a)).collect();
                MLFun {
                    name: self.package_name_mangling(&name.package(), &name.name())
                        + "::"
                        + &fname
                        + &*if fun_arg_label_type_mangled_name.is_empty() {
                            String::new()
                        } else {
                            String::from("##") + &*fun_arg_label_type_mangled_name
                        },
                    arg_defs: args,
                    return_type: self.type_(return_type.unwrap()).into_value_type(),
                    body: body.map(|body| self.fun_body(body)),
                }
            })
            .collect()
    }

    fn protocol(&mut self, p: TypedProtocol) -> Vec<MLFun> {
        vec![]
    }

    fn expr(&mut self, e: TypedExprKind) -> MLExpr {
        match e {
            TypedExprKind::Name(name) => self.name(name),
            TypedExprKind::Literal(l, t) => MLExpr::Literal(self.literal(l, t)),
            TypedExprKind::BinOp(b) => MLExpr::PrimitiveBinOp(self.binop(b)),
            TypedExprKind::UnaryOp(u) => MLExpr::PrimitiveUnaryOp(self.unary_op(u)),
            TypedExprKind::Subscript(s) => self.subscript(s),
            TypedExprKind::Member(m) => self.member(m),
            TypedExprKind::Array(a) => MLExpr::Array(self.array(a)),
            TypedExprKind::Tuple => todo!(),
            TypedExprKind::Dict => todo!(),
            TypedExprKind::StringBuilder => todo!(),
            TypedExprKind::Call(c) => self.call(c),
            TypedExprKind::If(i) => MLExpr::If(self.if_expr(i)),
            TypedExprKind::When => todo!(),
            TypedExprKind::Lambda(l) => todo!(),
            TypedExprKind::Return(r) => MLExpr::Return(self.return_expr(r)),
            TypedExprKind::TypeCast(t) => MLExpr::PrimitiveTypeCast(self.type_cast(t)),
        }
    }

    fn name(&self, n: TypedName) -> MLExpr {
        if let TypedType::Type(t) = n.type_.as_ref().unwrap() {
            let package = t.package().into_resolved();
            let name = t.name();
            let has_no_mangle = if let Some(i) = self.arena.get(&package.names, &name) {
                i.has_annotation(NO_MANGLE)
            } else {
                false
            };
            let mut mangled_name = if has_no_mangle {
                name
            } else {
                self.package_name_mangling_(&package, &name)
            };
            if let Some(type_arguments) = n.type_arguments {
                mangled_name += format!(
                    "<{}>",
                    type_arguments
                        .into_iter()
                        .map(|t| t.to_string())
                        .collect::<Vec<_>>()
                        .join(",")
                )
                .as_str()
            };
            MLExpr::Literal(MLLiteral {
                kind: MLLiteralKind::Struct(vec![]),
                type_: MLValueType::Struct(mangled_name),
            })
        } else {
            let package = n.package.clone().into_resolved();
            let has_no_mangle = if let Some(i) = self.arena.get(&package.names, &n.name) {
                i.has_annotation(NO_MANGLE)
            } else {
                false
            };
            let mut mangled_name = if has_no_mangle {
                n.name
            } else {
                self.package_name_mangling_(&package, &*n.name)
            };
            if let Some(type_arguments) = n.type_arguments {
                mangled_name += format!(
                    "<{}>",
                    type_arguments
                        .into_iter()
                        .map(|t| t.to_string())
                        .collect::<Vec<_>>()
                        .join(",")
                )
                .as_str()
            };
            if !has_no_mangle {
                if let Some(TypedType::Function(fun_type)) = &n.type_ {
                    if !fun_type.arguments.is_empty() {
                        mangled_name += "##";
                        mangled_name += &*self.fun_arg_label_type_name_mangling(
                            &fun_type
                                .arguments
                                .iter()
                                .map(|a| TypedArgDef {
                                    label: a.label.to_string(),
                                    name: "".to_string(),
                                    type_: a.typ.clone(),
                                })
                                .collect(),
                        )
                    }
                }
            }
            MLExpr::Name(MLName {
                name: mangled_name,
                type_: self.type_(n.type_.unwrap()),
            })
        }
    }

    fn literal(&self, l: TypedLiteralKind, type_: Option<TypedType>) -> MLLiteral {
        let (kind, type_) = match l {
            TypedLiteralKind::Integer(value) => (
                MLLiteralKind::Integer(value),
                self.type_(type_.unwrap()).into_value_type(),
            ),
            TypedLiteralKind::FloatingPoint(value) => (
                MLLiteralKind::FloatingPoint(value),
                self.type_(type_.unwrap()).into_value_type(),
            ),
            TypedLiteralKind::String(value) => (
                MLLiteralKind::String(value),
                self.type_(type_.unwrap()).into_value_type(),
            ),
            TypedLiteralKind::Boolean(value) => (
                MLLiteralKind::Boolean(value),
                self.type_(type_.unwrap()).into_value_type(),
            ),
            TypedLiteralKind::NullLiteral => (
                MLLiteralKind::Null,
                self.type_(type_.unwrap()).into_value_type(),
            ),
        };
        MLLiteral { kind, type_ }
    }

    fn binop(&mut self, b: TypedBinOp) -> MLBinOp {
        let TypedBinOp {
            left,
            operator: kind,
            right,
            type_,
        } = b;
        MLBinOp {
            left: Box::new(self.expr(*left)),
            kind: match kind {
                TypedBinaryOperator::Add => MLBinOpKind::Plus,
                TypedBinaryOperator::Sub => MLBinOpKind::Minus,
                TypedBinaryOperator::Mul => MLBinOpKind::Mul,
                TypedBinaryOperator::Div => MLBinOpKind::Div,
                TypedBinaryOperator::Mod => MLBinOpKind::Mod,
                TypedBinaryOperator::Equal => MLBinOpKind::Equal,
                TypedBinaryOperator::GrateThanEqual => MLBinOpKind::GrateThanEqual,
                TypedBinaryOperator::GrateThan => MLBinOpKind::GrateThan,
                TypedBinaryOperator::LessThanEqual => MLBinOpKind::LessThanEqual,
                TypedBinaryOperator::LessThan => MLBinOpKind::LessThan,
                TypedBinaryOperator::NotEqual => MLBinOpKind::NotEqual,
                TypedBinaryOperator::InfixFunctionCall(call) => {
                    todo!("infix function call {:?}", call)
                }
            },
            right: Box::new(self.expr(*right)),
            type_: self.type_(type_.unwrap()).into_value_type(),
        }
    }

    fn unary_op(&mut self, u: TypedUnaryOp) -> MLUnaryOp {
        match u {
            TypedUnaryOp::Prefix(p) => {
                let target = self.expr(*p.target);
                MLUnaryOp {
                    kind: match p.operator {
                        TypedPrefixUnaryOperator::Positive => MLUnaryOpKind::Positive,
                        TypedPrefixUnaryOperator::Negative => MLUnaryOpKind::Negative,
                        TypedPrefixUnaryOperator::Dereference => MLUnaryOpKind::DeRef,
                        TypedPrefixUnaryOperator::Reference => MLUnaryOpKind::Ref,
                        TypedPrefixUnaryOperator::Not => MLUnaryOpKind::Not,
                    },
                    type_: self.type_(p.type_.unwrap()).into_value_type(),
                    target: Box::new(target),
                }
            }
            TypedUnaryOp::Postfix(p) => {
                todo!()
            }
        }
    }

    fn subscript(&mut self, s: TypedSubscript) -> MLExpr {
        let t = s.target.type_().unwrap();
        if t.is_pointer_type() && s.indexes.len() == 1 {
            match t {
                TypedType::Value(v) => match v {
                    TypedValueType::Value(v) => {
                        if v.type_args.as_ref().unwrap()[0].is_primitive() {
                            MLExpr::PrimitiveSubscript(MLSubscript {
                                target: Box::new(self.expr(*s.target)),
                                index: Box::new(self.expr(s.indexes[0].clone())),
                                type_: self
                                    .type_(v.type_args.unwrap()[0].clone())
                                    .into_value_type(),
                            })
                        } else {
                            self.subscript_for_user_defined(s)
                        }
                    }
                    TypedValueType::Array(_, _) => {
                        todo!()
                    }
                    TypedValueType::Tuple(_) => {
                        todo!()
                    }
                    TypedValueType::Pointer(p) => MLExpr::PrimitiveSubscript(MLSubscript {
                        target: Box::new(self.expr(*s.target)),
                        index: Box::new(self.expr(s.indexes[0].clone())),
                        type_: self.type_(*p).into_value_type(),
                    }),
                    TypedValueType::Reference(_) => {
                        todo!()
                    }
                },
                t => panic!("function pointer detected. {:?}", t),
            }
        } else if t.is_string() || t.is_string_ref() {
            MLExpr::PrimitiveSubscript(MLSubscript {
                target: Box::new(self.expr(*s.target)),
                index: Box::new(self.expr(s.indexes[0].clone())),
                type_: MLValueType::Primitive(MLPrimitiveType::UInt8),
            })
        } else if t.is_array_type() {
            MLExpr::PrimitiveSubscript(MLSubscript {
                target: Box::new(self.expr(*s.target)),
                index: Box::new(self.expr(s.indexes[0].clone())),
                type_: match t {
                    TypedType::Value(TypedValueType::Array(e, _)) => {
                        self.type_(*e).into_value_type()
                    }
                    _ => unreachable!(),
                },
            })
        } else {
            self.subscript_for_user_defined(s)
        }
    }

    fn subscript_for_user_defined(&mut self, s: TypedSubscript) -> MLExpr {
        let target = self.expr(*s.target);
        match target {
            MLExpr::Name(target) => MLExpr::Call(MLCall {
                target,
                args: s
                    .indexes
                    .into_iter()
                    .map(|i| MLCallArg { arg: self.expr(i) })
                    .collect(),
                type_: self.type_(s.type_.unwrap()).into_value_type(),
            }),
            a => panic!("{:?}", a),
        }
    }

    fn member(&mut self, m: TypedInstanceMember) -> MLExpr {
        let TypedInstanceMember {
            target,
            name,
            is_safe,
            type_,
        } = m;
        let target = self.expr(*target);
        let type_ = self.type_(type_.unwrap());
        MLExpr::Member(MLMember {
            target: Box::new(target),
            name,
            type_,
        })
    }

    fn array(&mut self, a: TypedArray) -> MLArray {
        MLArray {
            elements: a.elements.into_iter().map(|e| self.expr(e)).collect(),
            type_: self.type_(a.type_.unwrap()).into_value_type(),
        }
    }

    fn call(&mut self, c: TypedCall) -> MLExpr {
        let TypedCall {
            target,
            mut args,
            type_,
        } = c;
        let target = match *target {
            TypedExprKind::Member(m) => {
                let TypedInstanceMember {
                    target,
                    name,
                    is_safe,
                    type_,
                } = m;
                match target.type_().unwrap() {
                    TypedType::Self_ => unreachable!(),
                    TypedType::Value(v) => {
                        let target_type = self.value_type(v);
                        let type_ = type_.unwrap();
                        if let TypedType::Function(fun_type) = &type_ {
                            args.insert(
                                0,
                                TypedCallArg {
                                    label: None,
                                    arg: target,
                                    is_vararg: false,
                                },
                            );
                            let mut mangled_name = target_type.name() + "::" + &*name;
                            if !fun_type.arguments.is_empty() {
                                mangled_name += "##";
                                mangled_name += &*self.fun_arg_label_type_name_mangling(
                                    &fun_type
                                        .arguments
                                        .iter()
                                        .map(|a| TypedArgDef {
                                            label: a.label.to_string(),
                                            name: "".to_string(),
                                            type_: a.typ.clone(),
                                        })
                                        .collect(),
                                )
                            }
                            MLExpr::Name(MLName {
                                name: mangled_name,
                                type_: self.type_(type_),
                            })
                        } else {
                            let is_stored = self.context.struct_has_field(&target_type, &name);
                            if is_stored {
                                let target = self.expr(*target);
                                let type_ = self.type_(type_);
                                MLExpr::Member(MLMember {
                                    target: Box::new(target),
                                    name,
                                    type_,
                                })
                            } else {
                                panic!("struct has no member {}", name)
                            }
                        }
                    }
                    TypedType::Function(_) => {
                        todo!()
                    }
                    TypedType::Type(t) => match *t {
                        TypedType::Self_ => unreachable!(),
                        TypedType::Value(t) => match t {
                            TypedValueType::Value(t) => {
                                let type_ = self.type_(type_.unwrap());
                                MLExpr::Name(MLName {
                                    name: self.package_name_mangling(&t.package, &*t.name)
                                        + "::"
                                        + &*name,
                                    type_,
                                })
                            }
                            TypedValueType::Array(_, _) => {
                                todo!()
                            }
                            TypedValueType::Tuple(_) => {
                                todo!()
                            }
                            TypedValueType::Pointer(_) => {
                                todo!()
                            }
                            TypedValueType::Reference(_) => {
                                todo!()
                            }
                        },
                        TypedType::Function(_) => {
                            todo!()
                        }
                        TypedType::Type(_) => {
                            todo!()
                        }
                    },
                }
            }
            t => self.expr(t),
        };
        let target = match target {
            MLExpr::Name(name) => name,
            MLExpr::Literal(MLLiteral {
                kind: MLLiteralKind::Struct(mut s),
                type_,
            }) => {
                for arg in args.into_iter() {
                    let v = self.expr(*arg.arg);
                    s.push((arg.label.unwrap(), v));
                }
                return MLExpr::Literal(MLLiteral {
                    kind: MLLiteralKind::Struct(s),
                    type_,
                });
            }
            a => panic!("{:?}", a),
        };
        MLExpr::Call(MLCall {
            target,
            args: args
                .into_iter()
                .map(|a| MLCallArg {
                    arg: self.expr(*a.arg),
                })
                .collect(),
            type_: self.type_(type_.unwrap()).into_value_type(),
        })
    }

    fn if_expr(&mut self, i: TypedIf) -> MLIf {
        MLIf {
            condition: Box::new(self.expr(*i.condition)),
            body: self.block(i.body),
            else_body: i.else_body.map(|b| self.block(b)),
            type_: self.type_(i.type_.unwrap()).into_value_type(),
        }
    }

    fn return_expr(&mut self, r: TypedReturn) -> MLReturn {
        MLReturn {
            value: r.value.map(|v| Box::new(self.expr(*v))),
        }
    }

    fn type_cast(&mut self, t: TypedTypeCast) -> MLTypeCast {
        MLTypeCast {
            target: Box::new(self.expr(*t.target)),
            type_: self.type_(t.type_.unwrap()).into_value_type(),
        }
    }

    fn arg_def(&self, e: TypedArgDef) -> MLArgDef {
        MLArgDef {
            name: e.name,
            type_: self.type_(e.type_).into_value_type(),
        }
    }

    fn fun_body(&mut self, b: TypedFunBody) -> MLFunBody {
        match b {
            TypedFunBody::Expr(e) => MLFunBody {
                body: vec![MLStmt::Expr(MLExpr::Return(MLReturn::new(Some(
                    self.expr(e),
                ))))],
            },
            TypedFunBody::Block(b) => MLFunBody {
                body: self.block(b).body,
            },
        }
    }

    fn block(&mut self, b: TypedBlock) -> MLBlock {
        MLBlock {
            body: b.body.into_iter().flat_map(|s| self.stmt(s)).collect(),
        }
    }

    fn package_name_mangling_(&self, package: &Package, name: &str) -> String {
        if package.is_global() || name == "main" {
            String::from(name)
        } else {
            package.to_string() + "::" + name
        }
    }

    fn package_name_mangling(&self, package: &TypedPackage, name: &str) -> String {
        if let TypedPackage::Resolved(pkg) = package {
            self.package_name_mangling_(pkg, name)
        } else {
            panic!("pkg name mangling failed => {:?}, {}", package, name)
        }
    }

    fn fun_arg_label_type_name_mangling(&self, args: &Vec<TypedArgDef>) -> String {
        args.iter()
            .map(|arg| format!("{}#{}", arg.label, arg.type_.to_string()))
            .collect::<Vec<_>>()
            .join("##")
    }
}
