/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jetbrains.python.psi;

import com.google.common.collect.ImmutableList;
import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author yole
 */
public enum LanguageLevel {

  /**
   * @apiNote This level is not supported since 2018.1.
   */
  PYTHON24(24),
  /**
   * @apiNote This level is not supported since 2018.1.
   */
  PYTHON25(25),
  /**
   * @apiNote This level is not supported since 2019.1.
   */
  PYTHON26(26),
  PYTHON27(27),
  /**
   * @apiNote This level is not supported since 2018.1.
   * Use it only to distinguish Python 2 and Python 3.
   * Consider using {@link LanguageLevel#isPython2()}.
   * Replace {@code level.isOlderThan(PYTHON30)} with {@code level.isPython2()}
   * and {@code level.isAtLeast(PYTHON30)} with {@code !level.isPython2()}.
   */
  PYTHON30(300),
  /**
   * @apiNote This level is not supported since 2018.1.
   */
  PYTHON31(301),
  /**
   * @apiNote This level is not supported since 2018.1.
   */
  PYTHON32(302),
  /**
   * @apiNote This level is not supported since 2018.1.
   */
  PYTHON33(303),
  /**
   * @apiNote This level is not supported since 2019.1.
   */
  PYTHON34(304),
  /**
   * @apiNote This level is not supported since 2020.3.
   */
  PYTHON35(305),
  PYTHON36(306),
  PYTHON37(307),
  PYTHON38(308),
  PYTHON39(309),
  PYTHON310(310);

  /**
   * This value is mostly bound to the compatibility of our debugger and helpers.
   * You're free to gradually drop support of versions not mentioned here if they present too much hassle to maintain.
   */
  public static final List<LanguageLevel> SUPPORTED_LEVELS =
    ImmutableList.copyOf(
      Stream
        .of(values())
        .filter(v -> v.myVersion > 305 || v.myVersion == 27)
        .collect(Collectors.toList())
    );

  private static final LanguageLevel DEFAULT2 = PYTHON27;
  private static final LanguageLevel DEFAULT3 = PYTHON310;

  @ApiStatus.Internal
  public static LanguageLevel FORCE_LANGUAGE_LEVEL = null;

  @NotNull
  public static LanguageLevel getDefault() {
    return getLatest();
  }

  private final int myVersion;

  LanguageLevel(int version) {
    myVersion = version;
  }

  /**
   * @return an int where major and minor version are represented decimally: "version 2.5" is 25.
   * @deprecated please migrate to {@link LanguageLevel#getMajorVersion()} and {@link LanguageLevel#getMinorVersion()}
   * since this method disclose {@link LanguageLevel} internals.
   */
  @Deprecated
  @ApiStatus.ScheduledForRemoval(inVersion = "2021.1")
  public int getVersion() {
    return myVersion;
  }

  public int getMajorVersion() {
    return myVersion < 100 ? myVersion / 10 : myVersion / 100;
  }

  public int getMinorVersion() {
    return myVersion < 100 ? myVersion % 10 : myVersion % 100;
  }

  public boolean hasPrintStatement() {
    return isPython2();
  }

  public boolean isPython2() {
    return getMajorVersion() == 2;
  }

  public boolean isPy3K() {
    return getMajorVersion() == 3;
  }

  public boolean isOlderThan(@NotNull LanguageLevel other) {
    return myVersion < other.myVersion;
  }

  public boolean isAtLeast(@NotNull LanguageLevel other) {
    return myVersion >= other.myVersion;
  }

  @Nullable
  @Contract("null->null;!null->!null")
  public static LanguageLevel fromPythonVersion(@Nullable String pythonVersion) {
    if (pythonVersion == null) return null;

    if (pythonVersion.startsWith("2")) {
      if (pythonVersion.startsWith("2.4")) {
        return PYTHON24;
      }
      if (pythonVersion.startsWith("2.5")) {
        return PYTHON25;
      }
      if (pythonVersion.startsWith("2.6")) {
        return PYTHON26;
      }
      if (pythonVersion.startsWith("2.7")) {
        return PYTHON27;
      }
      return DEFAULT2;
    }
    if (pythonVersion.startsWith("3")) {
      if (pythonVersion.startsWith("3.0")) {
        return PYTHON30;
      }
      if (pythonVersion.startsWith("3.1.") || pythonVersion.equals("3.1")) {
        return PYTHON31;
      }
      if (pythonVersion.startsWith("3.2")) {
        return PYTHON32;
      }
      if (pythonVersion.startsWith("3.3")) {
        return PYTHON33;
      }
      if (pythonVersion.startsWith("3.4")) {
        return PYTHON34;
      }
      if (pythonVersion.startsWith("3.5")) {
        return PYTHON35;
      }
      if (pythonVersion.startsWith("3.6")) {
        return PYTHON36;
      }
      if (pythonVersion.startsWith("3.7")) {
        return PYTHON37;
      }
      if (pythonVersion.startsWith("3.8")) {
        return PYTHON38;
      }
      if (pythonVersion.startsWith("3.9")) {
        return PYTHON39;
      }
      if (pythonVersion.startsWith("3.10")) {
        return PYTHON310;
      }
      return DEFAULT3;
    }
    return getDefault();
  }

  @Nullable
  @Contract("null->null;!null->!null")
  public static String toPythonVersion(@Nullable LanguageLevel level) {
    return level == null ? null : level.getMajorVersion() + "." + level.getMinorVersion();
  }

  @NotNull
  public static LanguageLevel forElement(@NotNull PsiElement element) {
    return PyPsiFacade.getInstance(element.getProject()).getLanguageLevel(element);
  }

  @NotNull
  public static LanguageLevel getLatest() {
    return ArrayUtil.getLastElement(values());
  }

  @Override
  public String toString() {
    return toPythonVersion(this);
  }
}

@@ -1147,16 +1147,17 @@
 THON24(2
+0
 4),%0A  /*
@@ -1232,16 +1232,17 @@
 THON25(2
+0
 5),%0A  /*
@@ -1317,16 +1317,17 @@
 THON26(2
+0
 6),%0A  PY
@@ -1334,16 +1334,17 @@
 THON27(2
+0
 7),%0A  /*
@@ -2564,42 +2564,42 @@
 %3E v.
-myVersion %3E 305 %7C%7C v.myVersion == 
+isAtLeast(PYTHON36) %7C%7C v == PYTHON
 27)%0A
@@ -3487,43 +3487,8 @@
 turn
- myVersion %3C 100 ? myVersion / 10 :
  myV
@@ -3553,43 +3553,8 @@
 turn
- myVersion %3C 100 ? myVersion %25 10 :
  myV

Align inner representation of python 2 versions with python 3