@Command(
      aliases = {"/copy"},
      flags = "e",
      desc = "Копирует выделенную территорию в буфер обмена",
      help =
          "Копирует выделенную территорию в буфер обмен\n"
              + "Флаги:\n"
              + "  -e определяет, будут ли объекты копироваться в буфер обмена\n"
              + "ПРЕДУПРЕЖДЕНИЕ: Вставленные объекты не могут быть отменены!",
      min = 0,
      max = 0)
  @CommandPermissions("worldedit.clipboard.copy")
  public void copy(
      CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession)
      throws WorldEditException {

    Region region = session.getSelection(player.getWorld());
    Vector min = region.getMinimumPoint();
    Vector max = region.getMaximumPoint();
    Vector pos = session.getPlacementPosition(player);

    CuboidClipboard clipboard =
        new CuboidClipboard(max.subtract(min).add(new Vector(1, 1, 1)), min, min.subtract(pos));
    clipboard.copy(editSession);
    if (args.hasFlag('e')) {
      for (LocalEntity entity : player.getWorld().getEntities(region)) {
        clipboard.storeEntity(entity);
      }
    }
    session.setClipboard(clipboard);

    player.print("Блок(и) скопирован(ы).");
  }
  @Command(
      aliases = {"/set"},
      usage = "<block>",
      desc = "Set all the blocks inside the selection to a block",
      min = 1,
      max = 1)
  @CommandPermissions("worldedit.region.set")
  @Logging(REGION)
  public static void set(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Pattern pattern = we.getBlockPattern(player, args.getString(0));

    int affected;

    if (pattern instanceof SingleBlockPattern) {
      affected =
          editSession.setBlocks(
              session.getSelection(player.getWorld()), ((SingleBlockPattern) pattern).getBlock());
    } else {
      affected = editSession.setBlocks(session.getSelection(player.getWorld()), pattern);
    }

    player.print(affected + " block(s) have been changed.");
  }
  @Command(
      aliases = {"/faces", "/outline"},
      usage = "<block>",
      desc = "Build the walls, ceiling, and floor of a selection",
      min = 1,
      max = 1)
  @CommandPermissions("worldedit.region.faces")
  @Logging(REGION)
  public static void faces(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Pattern pattern = we.getBlockPattern(player, args.getString(0));
    int affected;
    if (pattern instanceof SingleBlockPattern) {
      affected =
          editSession.makeCuboidFaces(
              session.getSelection(player.getWorld()), ((SingleBlockPattern) pattern).getBlock());
    } else {
      affected = editSession.makeCuboidFaces(session.getSelection(player.getWorld()), pattern);
    }

    player.print(affected + " block(s) have been changed.");
  }
  @Command(
      aliases = {"/paste"},
      usage = "",
      flags = "ao",
      desc = "Вставляет содержимое буфера обмена",
      help =
          "Вставить содержимое буфера обмена.\n"
              + "Флаги:\n"
              + "  -a пропускает блоки воздуха\n"
              + "  -o вставляет на позициях, которые были скопированы/вырезаны",
      min = 0,
      max = 0)
  @CommandPermissions("worldedit.clipboard.paste")
  @Logging(PLACEMENT)
  public void paste(
      CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession)
      throws WorldEditException {

    boolean atOrigin = args.hasFlag('o');
    boolean pasteNoAir = args.hasFlag('a');

    if (atOrigin) {
      Vector pos = session.getClipboard().getOrigin();
      session.getClipboard().place(editSession, pos, pasteNoAir);
      session.getClipboard().pasteEntities(pos);
      player.findFreePosition();
      player.print("Вставлено. Для отмены напишите //undo");
    } else {
      Vector pos = session.getPlacementPosition(player);
      session.getClipboard().paste(editSession, pos, pasteNoAir, true);
      player.findFreePosition();
      player.print("Вставлено. Для отмены напишите //undo");
    }
  }
  @Command(
      aliases = {"/cut"},
      usage = "[leave-id]",
      desc = "Вырезает выделенную территорию в буфер обмена",
      help =
          "Вырезает выделенную территорию в буфер обмена\n"
              + "Флаги:\n"
              + "  -e controls определяет, будут ли объекты копироваться в буфер обмена\n"
              + "ПРЕДУПРЕЖДЕНИЕ: Вырезанные и вставленные объекты не могут быть отменены!",
      flags = "e",
      min = 0,
      max = 1)
  @CommandPermissions("worldedit.clipboard.cut")
  @Logging(REGION)
  public void cut(
      CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession)
      throws WorldEditException {

    BaseBlock block = new BaseBlock(BlockID.AIR);
    LocalWorld world = player.getWorld();

    if (args.argsLength() > 0) {
      block = we.getBlock(player, args.getString(0));
    }

    Region region = session.getSelection(world);
    Vector min = region.getMinimumPoint();
    Vector max = region.getMaximumPoint();
    Vector pos = session.getPlacementPosition(player);

    CuboidClipboard clipboard =
        new CuboidClipboard(max.subtract(min).add(new Vector(1, 1, 1)), min, min.subtract(pos));
    clipboard.copy(editSession);
    if (args.hasFlag('e')) {
      LocalEntity[] entities = world.getEntities(region);
      for (LocalEntity entity : entities) {
        clipboard.storeEntity(entity);
      }
      world.killEntities(entities);
    }
    session.setClipboard(clipboard);

    int affected = editSession.setBlocks(session.getSelection(world), block);
    player.print(
        affected
            + " "
            + StringUtil.plural(affected, "блок вырезан", "блока вырезано", "блоков вырезано")
            + ".");
  }
  @Command(
      aliases = {"/move"},
      usage = "[count] [direction] [leave-id]",
      flags = "s",
      desc = "Move the contents of the selection",
      min = 0,
      max = 3)
  @CommandPermissions("worldedit.region.move")
  @Logging(ORIENTATION_REGION)
  public static void move(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    int count = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 1;
    Vector dir =
        we.getDirection(player, args.argsLength() > 1 ? args.getString(1).toLowerCase() : "me");
    BaseBlock replace;

    // Replacement block argument
    if (args.argsLength() > 2) {
      replace = we.getBlock(player, args.getString(2));
    } else {
      replace = new BaseBlock(BlockID.AIR);
    }

    int affected =
        editSession.moveCuboidRegion(
            session.getSelection(player.getWorld()), dir, count, true, replace);

    if (args.hasFlag('s')) {
      try {
        Region region = session.getSelection(player.getWorld());
        region.expand(dir.multiply(count));
        region.contract(dir.multiply(count));

        session.getRegionSelector().learnChanges();
        session.getRegionSelector().explainRegionAdjust(player, session);
      } catch (RegionOperationException e) {
        player.printError(e.getMessage());
      }
    }

    player.print(affected + " blocks moved.");
  }
  @Command(
      aliases = {"/overlay"},
      usage = "<block>",
      desc = "Set a block on top of blocks in the region",
      min = 1,
      max = 1)
  @CommandPermissions("worldedit.region.overlay")
  @Logging(REGION)
  public static void overlay(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Pattern pat = we.getBlockPattern(player, args.getString(0));

    Region region = session.getSelection(player.getWorld());
    int affected = 0;
    if (pat instanceof SingleBlockPattern) {
      affected = editSession.overlayCuboidBlocks(region, ((SingleBlockPattern) pat).getBlock());
    } else {
      affected = editSession.overlayCuboidBlocks(region, pat);
    }
    player.print(affected + " block(s) have been overlayed.");
  }
  @Command(
      aliases = {"/hsphere"},
      usage = "<block> <radius> [raised?] ",
      desc = "Generate a hollow sphere",
      min = 2,
      max = 3)
  @CommandPermissions({"worldedit.generation.sphere"})
  @Logging(PLACEMENT)
  public static void hsphere(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Pattern block = we.getBlockPattern(player, args.getString(0));
    double radius = Math.max(1, args.getDouble(1));
    boolean raised =
        args.argsLength() > 2
            ? (args.getString(2).equalsIgnoreCase("true")
                || args.getString(2).equalsIgnoreCase("yes"))
            : false;

    Vector pos = session.getPlacementPosition(player);
    if (raised) {
      pos = pos.add(0, radius, 0);
    }

    int affected = editSession.makeSphere(pos, block, radius, false);
    player.findFreePosition();
    player.print(affected + " block(s) have been created.");
  }
  @Command(
      aliases = {"/smooth"},
      usage = "[iterations]",
      flags = "n",
      desc = "Smooth the elevation in the selection",
      min = 0,
      max = 1)
  @CommandPermissions("worldedit.region.smooth")
  @Logging(REGION)
  public static void smooth(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    int iterations = 1;
    if (args.argsLength() > 0) {
      iterations = args.getInteger(0);
    }

    HeightMap heightMap =
        new HeightMap(editSession, session.getSelection(player.getWorld()), args.hasFlag('n'));
    HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0));
    int affected = heightMap.applyFilter(filter, iterations);
    player.print("Terrain's height map smoothed. " + affected + " block(s) changed.");
  }
  @Command(
      aliases = {"/hpyramid"},
      usage = "<block> <range>",
      desc = "Generate a hollow pyramid",
      min = 2,
      max = 2)
  @CommandPermissions({"worldedit.generation.pyramid"})
  @Logging(PLACEMENT)
  public static void hpyramid(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Pattern block = we.getBlockPattern(player, args.getString(0));
    int size = Math.max(1, args.getInteger(1));
    Vector pos = session.getPlacementPosition(player);

    int affected = editSession.makePyramid(pos, block, size, false);

    player.findFreePosition();
    player.print(affected + " block(s) have been created.");
  }
  @Command(
      aliases = {"/stack"},
      usage = "[count] [direction]",
      flags = "sa",
      desc = "Repeat the contents of the selection",
      min = 0,
      max = 2)
  @CommandPermissions("worldedit.region.stack")
  @Logging(ORIENTATION_REGION)
  public static void stack(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    int count = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 1;
    Vector dir =
        we.getDiagonalDirection(
            player, args.argsLength() > 1 ? args.getString(1).toLowerCase() : "me");

    int affected =
        editSession.stackCuboidRegion(
            session.getSelection(player.getWorld()), dir, count, !args.hasFlag('a'));

    if (args.hasFlag('s')) {
      try {
        Region region = session.getSelection(player.getWorld());
        region.expand(dir.multiply(count));
        region.contract(dir.multiply(count));

        session.getRegionSelector().learnChanges();
        session.getRegionSelector().explainRegionAdjust(player, session);
      } catch (RegionOperationException e) {
        player.printError(e.getMessage());
      }
    }

    player.print(affected + " blocks changed. Undo with //undo");
  }
  @Command(
      aliases = {"/replace"},
      usage = "[from-block] <to-block>",
      desc = "Replace all blocks in the selection with another",
      flags = "f",
      min = 1,
      max = 2)
  @CommandPermissions("worldedit.region.replace")
  @Logging(REGION)
  public static void replace(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Set<BaseBlock> from;
    Pattern to;
    if (args.argsLength() == 1) {
      from = null;
      to = we.getBlockPattern(player, args.getString(0));
    } else {
      from = we.getBlocks(player, args.getString(0), true, !args.hasFlag('f'));
      to = we.getBlockPattern(player, args.getString(1));
    }

    int affected = 0;
    if (to instanceof SingleBlockPattern) {
      affected =
          editSession.replaceBlocks(
              session.getSelection(player.getWorld()), from, ((SingleBlockPattern) to).getBlock());
    } else {
      affected = editSession.replaceBlocks(session.getSelection(player.getWorld()), from, to);
    }

    player.print(affected + " block(s) have been replaced.");
  }
  @Command(
      aliases = {"/regen"},
      usage = "",
      desc = "Regenerates the contents of the selection",
      min = 0,
      max = 0)
  @CommandPermissions("worldedit.regen")
  @Logging(REGION)
  public static void regenerateChunk(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Region region = session.getSelection(player.getWorld());
    Mask mask = session.getMask();
    session.setMask(null);
    player.getWorld().regenerate(region, editSession);
    session.setMask(mask);
    player.print("Region regenerated.");
  }
  @Command(
      aliases = {"clearclipboard"},
      usage = "",
      desc = "Очищает Ваш буфер обмена",
      min = 0,
      max = 0)
  @CommandPermissions("worldedit.clipboard.clear")
  public void clearClipboard(
      CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession)
      throws WorldEditException {

    session.setClipboard(null);
    player.print("Буфер обмена очищен.");
  }
  @Command(
      aliases = {"/naturalize"},
      usage = "",
      desc = "3 layers of dirt on top then rock below",
      min = 0,
      max = 0)
  @CommandPermissions("worldedit.region.naturalize")
  @Logging(REGION)
  public static void naturalize(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Region region = session.getSelection(player.getWorld());
    int affected = editSession.naturalizeCuboidBlocks(region);
    player.print(affected + " block(s) have been naturalized.");
  }
  @Command(
      aliases = {"/rotate"},
      usage = "<angle-in-degrees>",
      desc = "Поворот содержимого буфера обмена",
      min = 1,
      max = 1)
  @CommandPermissions("worldedit.clipboard.rotate")
  public void rotate(
      CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession)
      throws WorldEditException {

    int angle = args.getInteger(0);

    if (angle % 90 == 0) {
      CuboidClipboard clipboard = session.getClipboard();
      clipboard.rotate2D(angle);
      player.print("Содержимое буфера обмена повернуто на " + angle + " градусов.");
    } else {
      player.printError("Углы должны делиться на 90 градусов.");
    }
  }
  @Command(
      aliases = {"/flip"},
      usage = "[dir]",
      flags = "p",
      desc = "Переворачивает содержимое буфера обмена.",
      help =
          "Переворачивает содержимое буфера обмена.\n"
              + "Флаг -p переворачивает выделенную территорию вокруг игрока,\n"
              + "а не центра выделения.",
      min = 0,
      max = 1)
  @CommandPermissions("worldedit.clipboard.flip")
  public void flip(
      CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession)
      throws WorldEditException {

    CuboidClipboard.FlipDirection dir =
        we.getFlipDirection(player, args.argsLength() > 0 ? args.getString(0).toLowerCase() : "me");

    CuboidClipboard clipboard = session.getClipboard();
    clipboard.flip(dir, args.hasFlag('p'));
    player.print("Содержимое буфера обмена перевернуто.");
  }
  @Command(
      aliases = {"/cyl"},
      usage = "<block> <radius> [height] ",
      desc = "Generate a cylinder",
      min = 2,
      max = 3)
  @CommandPermissions({"worldedit.generation.cylinder"})
  @Logging(PLACEMENT)
  public static void cyl(
      CommandContext args,
      WorldEdit we,
      LocalSession session,
      LocalPlayer player,
      EditSession editSession)
      throws WorldEditException {

    Pattern block = we.getBlockPattern(player, args.getString(0));
    double radius = Math.max(1, args.getDouble(1));
    int height = args.argsLength() > 2 ? args.getInteger(2) : 1;

    Vector pos = session.getPlacementPosition(player);
    int affected = editSession.makeCylinder(pos, block, radius, height);
    player.print(affected + " block(s) have been created.");
  }