///|
/// Helper function to determine if a type represents a simple anonymous parameter
/// that should be printed without "_ :" prefix
fn is_simple_anonymous_type(ty : @syntax.Type) -> Bool {
  match ty {
    // Simple built-in types are likely anonymous parameters
    Name(constr_id~, ..) =>
      match constr_id.id {
        Ident(name~) =>
          // Common simple types that are typically anonymous
          name == "String" ||
          name == "Int" ||
          name == "Bool" ||
          name == "Double" ||
          name == "Float" ||
          name == "Char" ||
          name == "Unit" ||
          name == "Bytes"
        _ => false
      }
    // Complex types (Self, generic types, etc.) should use explicit "_ :"
    _ => false
  }
}

///|
/// Function and Type Declaration printing
fn print_parameter(param : @syntax.Parameter, logger : StringBuilder) -> Unit {
  match param {
    DiscardPositional(ty~, ..) =>
      // For discarded positional parameters
      // Heuristic: Simple type names (like String, Int) in anonymous contexts should omit "_ :"
      // Complex types (like Self) or when no type provided should include "_ :"
      match ty {
        Some(t) =>
          // Check if this looks like a simple anonymous parameter
          if is_simple_anonymous_type(t) {
            // Print just the type for anonymous parameters
            print_type(t, logger)
          } else {
            // Print "_ : Type" for explicit discard parameters  
            logger.write_string("_")
            logger.write_string(" : ")
            print_type(t, logger)
          }
        None => logger.write_string("_")
      }
    Positional(binder~, ty~) => {
      print_binder(binder, logger)
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
    }
    Labelled(binder~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("~")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
    }
    Optional(binder~, default~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("?")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
      logger.write_string(" = ")
      print_expr(default, logger)
    }
    QuestionOptional(binder~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("?")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
    }
  }
}

///|
/// Function and Type Declaration printing
fn print_impl_parameter(
  param : @syntax.Parameter,
  logger : StringBuilder,
) -> Unit {
  match param {
    DiscardPositional(ty~, ..) =>
      // For discarded positional parameters, always show "_" or "_ : Type"
      match ty {
        Some(t) => {
          logger.write_string("_")
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => logger.write_string("_")
      }
    Positional(binder~, ty~) =>
      // If binder is "_", always show "_" or "_ : Ty", don't just show the type
      if binder.name == "_" {
        logger.write_string("_")
        match ty {
          Some(t) => {
            logger.write_string(" : ")
            print_type(t, logger)
          }
          None => ()
        }
      } else {
        print_binder(binder, logger)
        match ty {
          Some(t) => {
            logger.write_string(" : ")
            print_type(t, logger)
          }
          None => ()
        }
      }
    Labelled(binder~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("~")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
    }
    Optional(binder~, default~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("?")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
      logger.write_string(" = ")
      print_expr(default, logger)
    }
    QuestionOptional(binder~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("?")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
    }
  }
}

///|
/// Function and Type Declaration printing
fn print_trait_method_parameter(
  param : @syntax.Parameter,
  logger : StringBuilder,
) -> Unit {
  match param {
    DiscardPositional(ty~, ..) =>
      // For discarded positional parameters, always print just the type
      match ty {
        Some(t) => print_type(t, logger)
        None => logger.write_string("_")
      }
    Positional(binder~, ty~) =>
      // If binder is "_", just print the typename without "_ : Ty"
      if binder.name == "_" {
        match ty {
          Some(t) => print_type(t, logger)
          None => logger.write_string("_")
        }
      } else {
        print_binder(binder, logger)
        match ty {
          Some(t) => {
            logger.write_string(" : ")
            print_type(t, logger)
          }
          None => ()
        }
      }
    Labelled(binder~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("~")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
    }
    Optional(binder~, default~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("?")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
      logger.write_string(" = ")
      print_expr(default, logger)
    }
    QuestionOptional(binder~, ty~) => {
      print_binder(binder, logger)
      logger.write_string("?")
      match ty {
        Some(t) => {
          logger.write_string(" : ")
          print_type(t, logger)
        }
        None => ()
      }
    }
  }
}

///|
fn print_fun_decl(fun_decl : @syntax.FunDecl, logger : StringBuilder) -> Unit {
  print_visibility(fun_decl.vis, logger)
  logger.write_string("fn")

  // fn[A] id(x : A) -> A { ... }
  //   ^^
  // quantifiers
  if not(fun_decl.quantifiers.is_empty()) {
    logger.write_string("[")
    print_list(fun_decl.quantifiers, print_type_var_binder, ", ", logger)
    logger.write_string("]")
  }
  match fun_decl.type_name {
    Some(type_name) => {
      logger.write_string(" ")
      print_type_name(type_name, logger)
      logger.write_string("::")
    }
    None => logger.write_string(" ")
  }
  print_binder(fun_decl.name, logger)
  logger.write_string("(")
  match fun_decl.decl_params {
    Some(params) => print_list(params, print_parameter, ", ", logger)
    None => ()
  }
  logger.write_string(")")
  match fun_decl.return_type {
    Some(ret_ty) => {
      logger.write_string(" -> ")
      print_type(ret_ty, logger)
    }
    None => ()
  }
  print_error_type(fun_decl.error_type, logger)
}

///|
fn print_decl_body(body : @syntax.DeclBody, logger : StringBuilder) -> Unit {
  match body {
    DeclBody(expr~, ..) => {
      logger.write_string("{ ")
      print_expr(expr, logger)
      logger.write_string(" }")
    }
    DeclStubs(stubs) =>
      match stubs {
        Import(module_name~, func_name~) => {
          logger.write_string(" = \"")
          logger.write_string(module_name)
          logger.write_string("\" \"")
          logger.write_string(func_name)
          logger.write_string("\"")
        }
        Embedded(..) => logger.write_string(" = { /* embedded code */ }")
      }
  }
}

///|
fn print_type_decl(
  type_decl : @syntax.TypeDecl,
  logger : StringBuilder,
) -> Unit {
  print_visibility(type_decl.type_vis, logger)
  match type_decl.components {
    Record(fields) => {
      logger.write_string("struct ")
      logger.write_string(type_decl.tycon)
      logger.write_string(" { ")
      for field in fields {
        print_visibility(field.vis, logger)
        if field.mut_ {
          logger.write_string("mut ")
        }
        logger.write_string(field.name.label)
        logger.write_string(" : ")
        print_type(field.ty, logger)
        logger.write_string("; ")
      }
      logger.write_string("}")
    }
    Variant(constructors) => {
      logger.write_string("enum ")
      logger.write_string(type_decl.tycon)
      logger.write_string(" { ")
      for constr in constructors {
        logger.write_string(constr.name.name)
        match constr.args {
          Some(args) => {
            logger.write_string("(")
            let mut first = true
            for arg in args.iter() {
              if not(first) {
                logger.write_string(", ")
              }
              first = false
              print_type(arg.ty, logger)
            }
            logger.write_string(")")
          }
          None => ()
        }
        logger.write_string("; ")
      }
      logger.write_string("}")
    }
    Abstract => {
      // FIXME
      logger.write_string("type ")
      logger.write_string(type_decl.tycon)
    }
    Extern => {
      // FIXME 
      logger.write_string("extern type")
      logger.write_string(type_decl.tycon)
    }
    Newtype(ty) => {
      // FIXME: need add type param
      logger.write_string("type ")
      logger.write_string(type_decl.tycon)
      logger.write_string(" ")
      print_type(ty, logger)
    }
    Error(exception_decl) => {
      logger.write_string("suberror ")
      logger.write_string(type_decl.tycon)
      match exception_decl {
        NoPayload => ()
        SinglePayload(ty) => {
          logger.write_string("(")
          print_type(ty, logger)
          logger.write_string(")")
        }
        EnumPayload(constructors) => {
          logger.write_string(" { ")
          for constr in constructors {
            logger.write_string(constr.name.name)
            match constr.args {
              Some(args) => {
                logger.write_string("(")
                let mut first = true
                for arg in args.iter() {
                  if not(first) {
                    logger.write_string(", ")
                  }
                  first = false
                  print_type(arg.ty, logger)
                }
                logger.write_string(")")
              }
              None => ()
            }
            logger.write_string("; ")
          }
          logger.write_string("}")
        }
      }
    }
    TupleStruct(types) => {
      logger.write_string("struct ")
      logger.write_string(type_decl.tycon)
      logger.write_string("(")
      let mut first = true
      for ty in types {
        if not(first) {
          logger.write_string(", ")
        }
        first = false
        print_type(ty, logger)
      }
      logger.write_string(")")
    }
    Alias(ty) => {
      logger.write_string("typealias ")
      print_type(ty, logger)
      logger.write_string(" as ")
      logger.write_string(type_decl.tycon)
    }
  }
}