/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.sync;

import android.support.annotation.CheckResult;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.sync.Utils;

/**
 * Data class representing a Firefox Account.
 *
 * This implementation is independent from Firefox for Android's because we want to be able to quickly access read-only
 * Sync data in a standalone library but AndroidFxAccount is heavily dependent on Android Accounts. To use Android
 * Accounts properly, we should have all Firefox Account apps accessing the same Account APK from Google Play, which is
 * beyond the scope of this implementation, considering we want to have it running quickly.
 */
public class FirefoxAccount {

    private static final String LOGTAG = FirefoxAccountShared.LOGTAG;

    public final String email;
    public final String uid;

    public final State accountState;
    public final FirefoxAccountEndpointConfig endpointConfig;

    public FirefoxAccount(final String email, final String uid, final State state,
            final FirefoxAccountEndpointConfig endpointConfig) {
        this.email = email;
        this.uid = uid;
        this.accountState = state;
        this.endpointConfig = endpointConfig;
    }

    /**
     * Gets a new account instance with the updated state - does *not*
     * modify the existing account instance.
     *
     * @return the account with the given state.
     */
    @CheckResult
    public FirefoxAccount withNewState(final State newState) {
        return new FirefoxAccount(email, uid, newState, endpointConfig);
    }

    /**
     * Returns a Firefox Account from the web sign in flow.
     *
     * @param jsonData The stringified JSON of the "event.details.data" object.
     * @return the account associated with the JSON or null if an error occurs.
     */
    public static FirefoxAccount fromWebFlow(final FirefoxAccountEndpointConfig endpointConfig, final String jsonData) {
        if (TextUtils.isEmpty(jsonData) || jsonData.equals("undefined")) {
            Log.e(LOGTAG, "fromWebFlow: input empty.");
            return null;
        }

        final JSONObject data;
        try {
            data = new JSONObject(jsonData);
        } catch (final JSONException e) {
            Log.e(LOGTAG, "fromWebFlow: unable to parse json."); // Don't log exception to avoid leaking user data.
            return null;
        }

        try {
            final String uid = data.getString("uid");
            final String email = data.getString("email");
            final boolean verified = data.optBoolean("verified", false);
            final byte[] keyFetchToken = Utils.hex2Byte(data.getString("keyFetchToken"));
            final byte[] sessionToken = Utils.hex2Byte(data.getString("sessionToken"));
            final byte[] unwrapBKey = Utils.hex2Byte(data.getString("unwrapBKey"));

            final State accountState = new Engaged(email, uid, verified, unwrapBKey, sessionToken, keyFetchToken);
            return new FirefoxAccount(email, uid, accountState, endpointConfig);
        } catch (final JSONException e) {
            Log.d(LOGTAG, "fromWebFlow: unable to gather all necessary fields from json."); // Don't log exception to avoid leaking user data.
            return null;
        }
    }
}

@@ -226,16 +226,72 @@
 .sync;%0A%0A
+import android.os.Parcel;%0Aimport android.os.Parcelable;%0A
 import a
@@ -559,18 +559,298 @@
 cko.
-sync.Utils
+fxa.login.StateFactory;%0Aimport org.mozilla.gecko.sync.ExtendedJSONObject;%0Aimport org.mozilla.gecko.sync.NonObjectJSONException;%0Aimport org.mozilla.gecko.sync.Utils;%0A%0Aimport java.io.IOException;%0Aimport java.security.NoSuchAlgorithmException;%0Aimport java.security.spec.InvalidKeySpecException
 ;%0A%0A/
@@ -1380,16 +1380,38 @@
 Account 
+implements Parcelable 
 %7B%0A%0A    p
@@ -3971,11 +3971,1675 @@
  %7D%0A    %7D
+%0A%0A    // --- START PARCELABLE --- //%0A    @Override public int describeContents() %7B return 0; %7D%0A%0A    @Override%0A    public void writeToParcel(final Parcel dest, final int flags) %7B%0A        for (final String parcelString : new String%5B%5D %7B%0A                email,%0A                uid,%0A                accountState.getStateLabel().name(),%0A                accountState.toJSONObject().toJSONString(),%0A        %7D) %7B%0A            dest.writeString(parcelString);%0A        %7D%0A%0A        endpointConfig.writeToParcel(dest, flags);%0A    %7D%0A%0A    public static final Parcelable.Creator%3CFirefoxAccount%3E CREATOR = new Parcelable.Creator%3CFirefoxAccount%3E() %7B%0A        @Override%0A        public FirefoxAccount createFromParcel(final Parcel source) %7B%0A            final String email = source.readString();%0A            final String uid = source.readString();%0A            final State.StateLabel stateLabel = State.StateLabel.valueOf(source.readString());%0A            final State state;%0A            try %7B%0A                state = StateFactory.fromJSONObject(stateLabel, new ExtendedJSONObject(source.readString()));%0A            %7D catch (final InvalidKeySpecException %7C NoSuchAlgorithmException %7C NonObjectJSONException %7C IOException e) %7B%0A                throw new IllegalStateException(%22Parcelled state JSON should be retrievable%22);%0A            %7D%0A            final FirefoxAccountEndpointConfig endpointConfig = FirefoxAccountEndpointConfig.CREATOR.createFromParcel(source);%0A%0A            return new FirefoxAccount(email, uid, state, endpointConfig);%0A        %7D%0A%0A        @Override public FirefoxAccount%5B%5D newArray(final int size) %7B return new FirefoxAccount%5Bsize%5D; %7D%0A    %7D;%0A    // --- END PARCELABLE --- //
 %0A%7D%0A

Make FirefoxAccount implements Parcellable.