package org.to2mbn.jmccc.auth.yggdrasil;

import java.io.Serializable;
import java.util.Objects;
import java.util.UUID;
import org.json.JSONObject;
import org.to2mbn.jmccc.auth.AuthInfo;
import org.to2mbn.jmccc.auth.Authenticator;
import org.to2mbn.jmccc.auth.yggdrasil.core.Agent;
import org.to2mbn.jmccc.auth.yggdrasil.core.GameProfile;
import org.to2mbn.jmccc.auth.yggdrasil.core.Session;
import org.to2mbn.jmccc.auth.yggdrasil.core.SessionService;
import org.to2mbn.jmccc.auth.yggdrasil.core.util.UUIDUtils;
import org.to2mbn.jmccc.auth.yggdrasil.core.yggdrasil.YggdrasilSessionService;
import org.to2mbn.jmccc.auth.AuthenticationException;

public class YggdrasilAuthenticator implements Authenticator, Serializable {

	public static interface PasswordProvider {

		String getUsername() throws AuthenticationException;

		String getPassword() throws AuthenticationException;

		CharacterSelector getCharacterSelector();

	}

	private static final long serialVersionUID = 1L;

	private SessionService sessionService;
	private volatile Session authResult;

	public YggdrasilAuthenticator() {
		this(new YggdrasilSessionService(UUIDUtils.unsign(UUID.randomUUID()), Agent.MINECRAFT));
	}

	public YggdrasilAuthenticator(SessionService sessionService) {
		Objects.requireNonNull(sessionService);
		this.sessionService = sessionService;
	}

	@Override
	public synchronized AuthInfo auth() throws AuthenticationException {
		GameProfile selectedProfile = session().getSelectedProfile();
		if (selectedProfile == null) {
			throw new AuthenticationException("no profile is available");
		}
		return new AuthInfo(selectedProfile.getName(), authResult.getAccessToken(), UUIDUtils.unsign(selectedProfile.getUUID()), new JSONObject(authResult.getProperties()).toString(), authResult.getUserType().getName());
	}

	public synchronized Session session() throws AuthenticationException {
		if (authResult == null || !sessionService.validate(authResult.getAccessToken())) {
			refresh();
		}
		if (authResult == null) {
			throw new AuthenticationException("no authentication is available");
		}
		return authResult;
	}

	public synchronized void refresh() throws AuthenticationException {
		if (authResult == null) {
			// refresh operation is not available
			PasswordProvider passwordProvider = tryPasswordLogin();
			if (passwordProvider == null) {
				throw new AuthenticationException("no more authentication methods to try");
			}
			refreshWithPassword(passwordProvider);
		} else {
			try {
				refreshWithToken(authResult.getAccessToken());
			} catch (AuthenticationException e) {
				// token login failed
				PasswordProvider passwordProvider = tryPasswordLogin();
				if (passwordProvider == null) {
					throw e;
				}

				try {
					refreshWithPassword(passwordProvider);
				} catch (AuthenticationException e1) {
					e1.addSuppressed(e);
					throw e1;
				}
			}
		}
	}

	public synchronized void refreshWithPassword(PasswordProvider passwordProvider) throws AuthenticationException {
		Objects.requireNonNull(passwordProvider);
		authResult = sessionService.login(passwordProvider.getUsername(), passwordProvider.getPassword());
		if (authResult.getSelectedProfile() == null) {
			// no profile is selected
			// let's select one
			CharacterSelector selector = passwordProvider.getCharacterSelector();
			if (selector == null) {
				selector = new DefaultCharacterSelector();
			}
			GameProfile[] profiles = authResult.getProfiles();
			if (profiles == null || profiles.length == 0) {
				throw new AuthenticationException("no profile is available");
			}
			GameProfile selectedProfile = selector.select(profiles);
			authResult = sessionService.selectProfile(authResult.getAccessToken(), selectedProfile.getUUID());
		}
	}

	public synchronized void refreshWithToken(String accessToken) throws AuthenticationException {
		Objects.requireNonNull(accessToken);
		authResult = sessionService.refresh(accessToken);
	}

	public synchronized void clearToken() {
		authResult = null;
	}

	protected PasswordProvider tryPasswordLogin() throws AuthenticationException {
		return null;
	}

}

@@ -1099,36 +1099,8 @@
 his(
-new YggdrasilSessionService(
 UUID
@@ -1130,16 +1130,122 @@
 mUUID())
+);%0A%09%7D%0A%0A%09public YggdrasilAuthenticator(String clientToken) %7B%0A%09%09this(new YggdrasilSessionService(clientToken
 , Agent.
@@ -4067,16 +4067,188 @@
 ll;%0A%09%7D%0A%0A
+%09public synchronized Session getCurrentSession() %7B%0A%09%09return authResult;%0A%09%7D%0A%0A%09public synchronized void setCurrentSession(Session session) %7B%0A%09%09this.authResult = session;%0A%09%7D%0A%0A
 %09protect

add getter and setter for current session