/**
   * Request token key using Oauth2
   *
   * @param tokenUrl
   * @param clientId
   * @param clientSecret
   * @return tokenkey
   * @throws ParseException
   * @throws IOException
   * @throws OAuthSystemException
   * @throws OAuthProblemException
   */
  public static String Oauth2ClientRequestToken(
      String tokenUrl, String clientId, String clientSecret) throws ParseException, IOException {
    // configure Oauth2 and get access token
    OAuthClientRequest request = null;
    OAuthClient oAuthClient = null;
    OAuthJSONAccessTokenResponse tokenResponse = null;

    try {
      request =
          OAuthClientRequest.tokenLocation(tokenUrl)
              .setClientId(clientId)
              .setClientSecret(clientSecret)
              .setGrantType(GrantType.CLIENT_CREDENTIALS)
              .setScope("all")
              .buildBodyMessage();

      oAuthClient = new OAuthClient(new URLConnectionClient());
      tokenResponse = oAuthClient.accessToken(request, OAuthJSONAccessTokenResponse.class);
    } catch (OAuthSystemException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (OAuthProblemException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    if (tokenResponse == null) return null;

    // return token
    return tokenResponse.getAccessToken();
  }
  private OAuthClientResponse getOauthResponse(
      OAuthClient oAuthClient, OAuthClientRequest accessRequest)
      throws AuthenticationFailedException {

    OAuthClientResponse oAuthResponse = null;
    try {
      oAuthResponse = oAuthClient.accessToken(accessRequest);
    } catch (OAuthSystemException e) {
      if (log.isDebugEnabled()) {
        log.debug("Exception while requesting access token", e);
      }
      throw new AuthenticationFailedException(e.getMessage(), e);
    } catch (OAuthProblemException e) {
      if (log.isDebugEnabled()) {
        log.debug("Exception while requesting access token", e);
      }
    }
    return oAuthResponse;
  }
  private OAuthClientRequest getaccessRequest(
      String tokenEndPoint, String clientId, String code, String clientSecret, String callbackurl)
      throws AuthenticationFailedException {

    OAuthClientRequest accessRequest = null;
    try {
      accessRequest =
          OAuthClientRequest.tokenLocation(tokenEndPoint)
              .setGrantType(GrantType.AUTHORIZATION_CODE)
              .setClientId(clientId)
              .setClientSecret(clientSecret)
              .setRedirectURI(callbackurl)
              .setCode(code)
              .buildBodyMessage();

    } catch (OAuthSystemException e) {
      if (log.isDebugEnabled()) {
        log.debug("Exception while building request for request access token", e);
      }
      throw new AuthenticationFailedException(e.getMessage(), e);
    }
    return accessRequest;
  }
  @Override
  protected void log(int priority, String tag, String message, Throwable t) {
    if (priority == Log.ERROR) {
      // remove any stack trace attached by Timber
      if (message != null) {
        int newLine = message.indexOf('\n');
        if (newLine > 0) {
          message = message.substring(0, newLine);
        }
      }

      // special treatment for some exceptions
      if (t instanceof TvdbException) {
        TvdbException e = (TvdbException) t;
        Utils.trackCustomEvent(
            context, CATEGORY_THETVDB_ERROR, tag + ": " + message, e.getMessage());
        return;
      } else if (t instanceof OAuthProblemException) {
        // log trakt OAuth failures
        OAuthProblemException e = (OAuthProblemException) t;
        StringBuilder exceptionMessage = new StringBuilder();
        if (!TextUtils.isEmpty(e.getError())) {
          exceptionMessage.append(e.getError());
        }
        if (!TextUtils.isEmpty(e.getDescription())) {
          exceptionMessage.append(", ").append(e.getDescription());
        }
        if (!TextUtils.isEmpty(e.getUri())) {
          exceptionMessage.append(", ").append(e.getUri());
        }
        Utils.trackCustomEvent(
            context, "OAuth Error", tag + ": " + message, exceptionMessage.toString());
        return;
      } else if (t instanceof OAuthSystemException) {
        // log trakt OAuth failures
        OAuthSystemException e = (OAuthSystemException) t;
        Utils.trackCustomEvent(context, "OAuth Error", tag + ": " + message, e.getMessage());
        return;
      }
    }

    // drop empty messages
    if (message == null) {
      return;
    }
    // drop debug and verbose logs
    if (priority == Log.DEBUG || priority == Log.VERBOSE) {
      return;
    }

    // transform priority into string
    String level = null;
    switch (priority) {
      case Log.INFO:
        level = "INFO";
        break;
      case Log.WARN:
        level = "WARN";
        break;
      case Log.ERROR:
        level = "ERROR";
        break;
    }

    // finally log to crashlytics
    Crashlytics.log(level + "/" + tag + ": " + message);

    // track some non-fatal exceptions with crashlytics
    if (priority == Log.ERROR) {
      if (t instanceof SQLiteException) {
        Crashlytics.logException(t);
      }
    }
  }
  @Override
  protected void initiateAuthenticationRequest(
      HttpServletRequest request, HttpServletResponse response, AuthenticationContext context)
      throws AuthenticationFailedException {

    try {
      Map<String, String> authenticatorProperties = context.getAuthenticatorProperties();
      if (authenticatorProperties != null) {
        String clientId = authenticatorProperties.get(OIDCAuthenticatorConstants.CLIENT_ID);
        String authorizationEP = getAuthorizationServerEndpoint(authenticatorProperties);

        if (authorizationEP == null) {
          authorizationEP =
              authenticatorProperties.get(OIDCAuthenticatorConstants.OAUTH2_AUTHZ_URL);
        }

        String callbackurl = getCallbackUrl(authenticatorProperties);

        if (StringUtils.isBlank(callbackurl)) {
          callbackurl = IdentityUtil.getServerURL(FrameworkConstants.COMMONAUTH, true, true);
        }

        String state = context.getContextIdentifier() + "," + OIDCAuthenticatorConstants.LOGIN_TYPE;

        state = getState(state, authenticatorProperties);

        OAuthClientRequest authzRequest;

        String queryString = getQueryString(authenticatorProperties);
        Map<String, String> paramValueMap = new HashMap<>();

        if (StringUtils.isNotBlank(queryString)) {
          String[] params = queryString.split("&");
          if (params != null && params.length > 0) {
            for (String param : params) {
              String[] intParam = param.split("=");
              paramValueMap.put(intParam[0], intParam[1]);
            }
            context.setProperty("oidc:param.map", paramValueMap);
          }
        }

        String scope = paramValueMap.get("scope");

        if (scope == null) {
          scope = OIDCAuthenticatorConstants.OAUTH_OIDC_SCOPE;
        }

        scope = getScope(scope, authenticatorProperties);

        if (queryString != null
            && queryString.toLowerCase().contains("scope=")
            && queryString.toLowerCase().contains("redirect_uri=")) {
          authzRequest =
              OAuthClientRequest.authorizationLocation(authorizationEP)
                  .setClientId(clientId)
                  .setResponseType(OIDCAuthenticatorConstants.OAUTH2_GRANT_TYPE_CODE)
                  .setState(state)
                  .buildQueryMessage();
        } else if (queryString != null && queryString.toLowerCase().contains("scope=")) {
          authzRequest =
              OAuthClientRequest.authorizationLocation(authorizationEP)
                  .setClientId(clientId)
                  .setRedirectURI(callbackurl)
                  .setResponseType(OIDCAuthenticatorConstants.OAUTH2_GRANT_TYPE_CODE)
                  .setState(state)
                  .buildQueryMessage();
        } else if (queryString != null && queryString.toLowerCase().contains("redirect_uri=")) {
          authzRequest =
              OAuthClientRequest.authorizationLocation(authorizationEP)
                  .setClientId(clientId)
                  .setResponseType(OIDCAuthenticatorConstants.OAUTH2_GRANT_TYPE_CODE)
                  .setScope(OIDCAuthenticatorConstants.OAUTH_OIDC_SCOPE)
                  .setState(state)
                  .buildQueryMessage();

        } else {
          authzRequest =
              OAuthClientRequest.authorizationLocation(authorizationEP)
                  .setClientId(clientId)
                  .setRedirectURI(callbackurl)
                  .setResponseType(OIDCAuthenticatorConstants.OAUTH2_GRANT_TYPE_CODE)
                  .setScope(scope)
                  .setState(state)
                  .buildQueryMessage();
        }

        String loginPage = authzRequest.getLocationUri();
        String domain = request.getParameter("domain");

        if (domain != null) {
          loginPage = loginPage + "&fidp=" + domain;
        }

        if (queryString != null) {
          if (!queryString.startsWith("&")) {
            loginPage = loginPage + "&" + queryString;
          } else {
            loginPage = loginPage + queryString;
          }
        }
        response.sendRedirect(loginPage);
      } else {
        if (log.isDebugEnabled()) {
          log.debug("Error while retrieving properties. Authenticator Properties cannot be null");
        }
        throw new AuthenticationFailedException(
            "Error while retrieving properties. Authenticator Properties cannot be null");
      }
    } catch (IOException e) {
      log.error("Exception while sending to the login page", e);
      throw new AuthenticationFailedException(e.getMessage(), e);
    } catch (OAuthSystemException e) {
      log.error("Exception while building authorization code request", e);
      throw new AuthenticationFailedException(e.getMessage(), e);
    }
    return;
  }