// Path: app/src/main/java/io/kaif/mobile/config/ApiConfiguration.java
// public class ApiConfiguration {
// 
//   private String endPoint;
// 
//   private String clientId;
// 
//   private String redirectUri;
// 
//   private String clientSecret;
// 
//   public ApiConfiguration(String endPoint,
//       String clientId,
//       String clientSecret,
//       String redirectUri) {
//     this.endPoint = endPoint;
//     this.clientId = clientId;
//     this.clientSecret = clientSecret;
//     this.redirectUri = redirectUri;
//   }
// 
//   public String getEndPoint() {
//     return endPoint;
//   }
// 
//   public String getClientId() {
//     return clientId;
//   }
// 
//   public String getRedirectUri() {
//     return redirectUri;
//   }
// 
//   public String getClientSecret() {
//     return clientSecret;
//   }
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/EventPublishSubject.java
// public class EventPublishSubject<T> {
// 
//   private final PublishSubject<T> subject;
// 
//   public EventPublishSubject() {
//     this.subject = PublishSubject.create();
//   }
// 
//   public Observable<T> getSubject(Class<?>... classes) {
//     return subject.asObservable().filter(event -> {
//       for (Class<?> clazz : classes) {
//         if (clazz.isInstance(event)) {
//           return true;
//         }
//       }
//       return false;
//     }).observeOn(AndroidSchedulers.mainThread());
//   }
// 
//   public <E extends T> Observable<E> getSubject(Class<E> clazz) {
//     return subject.asObservable()
//         .filter(clazz::isInstance)
//         .cast(clazz)
//         .observeOn(AndroidSchedulers.mainThread());
//   }
// 
//   public <E extends T> void onNext(E event) {
//     subject.onNext(event);
//   }
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/account/AccountEvent.java
// public class AccountEvent {
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/account/SignInSuccessEvent.java
// public class SignInSuccessEvent extends AccountEvent {
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/account/SignOutEvent.java
// public class SignOutEvent extends AccountEvent {
// }
// 
// Path: app/src/main/java/io/kaif/mobile/model/oauth/AccessTokenManager.java
// @Singleton
// public class AccessTokenManager {
// 
//   public static final String ACCESS_TOKEN_KEY = "ACCESS_TOKEN";
// 
//   SharedPreferences preference;
// 
//   Gson gson;
// 
//   @Inject
//   public AccessTokenManager(SharedPreferences preference, Gson gson) {
//     this.gson = gson;
//     this.preference = preference;
//   }
// 
//   public boolean hasAccount() {
//     return preference.contains(ACCESS_TOKEN_KEY);
//   }
// 
//   public void signOut() {
//     preference.edit().remove(ACCESS_TOKEN_KEY).apply();
//   }
// 
//   public void saveAccount(AccessTokenInfo accessTokenInfo) {
//     preference.edit().putString(ACCESS_TOKEN_KEY, gson.toJson(accessTokenInfo)).apply();
//   }
// 
//   public AccessTokenInfo findAccount() {
//     return gson.fromJson(preference.getString(ACCESS_TOKEN_KEY, null), AccessTokenInfo.class);
//   }
// }
// 
// Path: app/src/main/java/io/kaif/mobile/service/OauthService.java
// public interface OauthService {
// 
//   @FormUrlEncoded
//   @POST("/oauth/access-token")
//   Observable<AccessTokenInfo> getAccessToken(@Field("client_id") String clientId,
//                                              @Field("client_secret") String clientSecret,
//                                              @Field("code") String code,
//                                              @Field("redirect_uri") String redirectUri,
//                                              @Field("grant_type") String grantType);
// 
// }


import java.util.UUID;
import javax.inject.Inject;
import javax.inject.Singleton;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import io.kaif.mobile.config.ApiConfiguration;
import io.kaif.mobile.event.EventPublishSubject;
import io.kaif.mobile.event.account.AccountEvent;
import io.kaif.mobile.event.account.SignInSuccessEvent;
import io.kaif.mobile.event.account.SignOutEvent;
import io.kaif.mobile.model.oauth.AccessTokenManager;
import io.kaif.mobile.service.OauthService;
import rx.Observable;
        .appendQueryParameter("client_id", apiConfiguration.getClientId())
        .appendQueryParameter("response_type", "code")
        .appendQueryParameter("scope", "public feed article vote user debate")
        .appendQueryParameter("state", state)
        .appendQueryParameter("redirect_uri", apiConfiguration.getRedirectUri())
        .build();
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(uri);
    return intent;
  }

  public boolean hasAccount() {
    return accessTokenManager.hasAccount();
  }

  public void signOut() {
    accessTokenManager.signOut();
    subject.onNext(new SignOutEvent());
  }

  public Observable<Void> accessToken(String code, String state) {
    if (!TextUtils.equals(this.state, state)) {
      return Observable.<Void>error(new IllegalStateException("receive a malicious response"));
    }
    this.state = null;
    return oauthService.getAccessToken(apiConfiguration.getClientId(),
        apiConfiguration.getClientSecret(),
        code,
        apiConfiguration.getRedirectUri(),
        "authorization_code").<Void>map(accessTokenInfo -> {
// Path: app/src/main/java/io/kaif/mobile/config/ApiConfiguration.java
// public class ApiConfiguration {
// 
//   private String endPoint;
// 
//   private String clientId;
// 
//   private String redirectUri;
// 
//   private String clientSecret;
// 
//   public ApiConfiguration(String endPoint,
//       String clientId,
//       String clientSecret,
//       String redirectUri) {
//     this.endPoint = endPoint;
//     this.clientId = clientId;
//     this.clientSecret = clientSecret;
//     this.redirectUri = redirectUri;
//   }
// 
//   public String getEndPoint() {
//     return endPoint;
//   }
// 
//   public String getClientId() {
//     return clientId;
//   }
// 
//   public String getRedirectUri() {
//     return redirectUri;
//   }
// 
//   public String getClientSecret() {
//     return clientSecret;
//   }
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/EventPublishSubject.java
// public class EventPublishSubject<T> {
// 
//   private final PublishSubject<T> subject;
// 
//   public EventPublishSubject() {
//     this.subject = PublishSubject.create();
//   }
// 
//   public Observable<T> getSubject(Class<?>... classes) {
//     return subject.asObservable().filter(event -> {
//       for (Class<?> clazz : classes) {
//         if (clazz.isInstance(event)) {
//           return true;
//         }
//       }
//       return false;
//     }).observeOn(AndroidSchedulers.mainThread());
//   }
// 
//   public <E extends T> Observable<E> getSubject(Class<E> clazz) {
//     return subject.asObservable()
//         .filter(clazz::isInstance)
//         .cast(clazz)
//         .observeOn(AndroidSchedulers.mainThread());
//   }
// 
//   public <E extends T> void onNext(E event) {
//     subject.onNext(event);
//   }
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/account/AccountEvent.java
// public class AccountEvent {
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/account/SignInSuccessEvent.java
// public class SignInSuccessEvent extends AccountEvent {
// }
// 
// Path: app/src/main/java/io/kaif/mobile/event/account/SignOutEvent.java
// public class SignOutEvent extends AccountEvent {
// }
// 
// Path: app/src/main/java/io/kaif/mobile/model/oauth/AccessTokenManager.java
// @Singleton
// public class AccessTokenManager {
// 
//   public static final String ACCESS_TOKEN_KEY = "ACCESS_TOKEN";
// 
//   SharedPreferences preference;
// 
//   Gson gson;
// 
//   @Inject
//   public AccessTokenManager(SharedPreferences preference, Gson gson) {
//     this.gson = gson;
//     this.preference = preference;
//   }
// 
//   public boolean hasAccount() {
//     return preference.contains(ACCESS_TOKEN_KEY);
//   }
// 
//   public void signOut() {
//     preference.edit().remove(ACCESS_TOKEN_KEY).apply();
//   }
// 
//   public void saveAccount(AccessTokenInfo accessTokenInfo) {
//     preference.edit().putString(ACCESS_TOKEN_KEY, gson.toJson(accessTokenInfo)).apply();
//   }
// 
//   public AccessTokenInfo findAccount() {
//     return gson.fromJson(preference.getString(ACCESS_TOKEN_KEY, null), AccessTokenInfo.class);
//   }
// }
// 
// Path: app/src/main/java/io/kaif/mobile/service/OauthService.java
// public interface OauthService {
// 
//   @FormUrlEncoded
//   @POST("/oauth/access-token")
//   Observable<AccessTokenInfo> getAccessToken(@Field("client_id") String clientId,
//                                              @Field("client_secret") String clientSecret,
//                                              @Field("code") String code,
//                                              @Field("redirect_uri") String redirectUri,
//                                              @Field("grant_type") String grantType);
// 
// }


// Path: app/src/main/java/io/kaif/mobile/view/daemon/AccountDaemon.java
import java.util.UUID;
import javax.inject.Inject;
import javax.inject.Singleton;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import io.kaif.mobile.config.ApiConfiguration;
import io.kaif.mobile.event.EventPublishSubject;
import io.kaif.mobile.event.account.AccountEvent;
import io.kaif.mobile.event.account.SignInSuccessEvent;
import io.kaif.mobile.event.account.SignOutEvent;
import io.kaif.mobile.model.oauth.AccessTokenManager;
import io.kaif.mobile.service.OauthService;
import rx.Observable;

        .appendQueryParameter("client_id", apiConfiguration.getClientId())
        .appendQueryParameter("response_type", "code")
        .appendQueryParameter("scope", "public feed article vote user debate")
        .appendQueryParameter("state", state)
        .appendQueryParameter("redirect_uri", apiConfiguration.getRedirectUri())
        .build();
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(uri);
    return intent;
  }

  public boolean hasAccount() {
    return accessTokenManager.hasAccount();
  }

  public void signOut() {
    accessTokenManager.signOut();
    subject.onNext(new SignOutEvent());
  }

  public Observable<Void> accessToken(String code, String state) {
    if (!TextUtils.equals(this.state, state)) {
      return Observable.<Void>error(new IllegalStateException("receive a malicious response"));
    }
    this.state = null;
    return oauthService.getAccessToken(apiConfiguration.getClientId(),
        apiConfiguration.getClientSecret(),
        code,
        apiConfiguration.getRedirectUri(),
        "authorization_code").<Void>map(accessTokenInfo -> {
      accessTokenManager.saveAccount(accessTokenInfo);