// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.invalidation;

import android.os.Bundle;
import android.util.Base64;

import com.google.protobuf.nano.MessageNano;

import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.components.invalidation.SerializedInvalidation.Invalidation;

import java.io.IOException;

import javax.annotation.Nullable;

/**
 * A container class that stores the received invalidations.
 * It plays the role of abstracting conversions to and from other storage types like String
 * (storing in prefStore) and Bundle (ContentProvider).
 */
public class PendingInvalidation {
    private static final String TAG = Log.makeTag("invalidation");

    private static final String INVALIDATION_OBJECT_SOURCE_KEY = "objectSource";
    private static final String INVALIDATION_OBJECT_ID_KEY = "objectId";
    private static final String INVALIDATION_VERSION_KEY = "version";
    private static final String INVALIDATION_PAYLOAD_KEY = "payload";

    public final String mObjectId;
    public final int mObjectSource;
    public final long mVersion;
    public final String mPayload;

    /**
     * A constructor used in tests only.
     */
    @VisibleForTesting
    public PendingInvalidation(String id, int source, long version, String payload) {
        mObjectId = id;
        mObjectSource = source;
        mVersion = version;
        mPayload = payload;
    }

    /**
     * Parse a PendingInvalidation from a bundle.
     */
    public PendingInvalidation(Bundle bundle) {
        // bundle.get* methods helpfully give compatible fallback values so we don't handle that.
        mObjectId = bundle.getString(INVALIDATION_OBJECT_ID_KEY);
        mObjectSource = bundle.getInt(INVALIDATION_OBJECT_SOURCE_KEY);
        mVersion = bundle.getLong(INVALIDATION_VERSION_KEY);
        mPayload = bundle.getString(INVALIDATION_PAYLOAD_KEY);
    }

    /**
     * Encode an invalidation into a bundle.
     */
    public static Bundle createBundle(String id, int source, long version, String payload) {
        Bundle bundle = new Bundle();
        bundle.putString(INVALIDATION_OBJECT_ID_KEY, id);
        bundle.putInt(INVALIDATION_OBJECT_SOURCE_KEY, source);
        bundle.putLong(INVALIDATION_VERSION_KEY, version);
        bundle.putString(INVALIDATION_PAYLOAD_KEY, payload);
        return bundle;
    }

    /**
     * Encode an invalidation into a String.
     * The invalidation is first encoded as {@link SerializedInvalidation.Invalidation}, which is
     * further encoded into a String of Base64 encoding.
     * Do not call for mObjectSource == 0, which is an invalidation for all types and is handled
     * specially.
     */
    public String encodeToString() {
        assert mObjectSource != 0;
        Invalidation invalidation = new Invalidation();
        invalidation.objectSource = mObjectSource;
        invalidation.objectId = mObjectId;
        invalidation.version = mVersion;
        invalidation.payload = mPayload;
        return Base64.encodeToString(MessageNano.toByteArray(invalidation), Base64.DEFAULT);
    }

    /**
     * Decode the invalidation encoded as a String into a Bundle.
     * Return value is {@code null} if the string could not be parsed or is an invalidation for all.
     */
    @Nullable
    public static Bundle decodeToBundle(String encoded) {
        assert encoded != null;
        byte[] decoded = Base64.decode(encoded, Base64.DEFAULT);
        Invalidation invalidation;
        try {
            invalidation = MessageNano.mergeFrom(new Invalidation(), decoded);
        } catch (IOException e) {
            Log.e(TAG, "Could not parse the serialized invalidations.", e);
            return null;
        }
        assert invalidation != null;
        if (invalidation.objectSource == null || invalidation.objectSource == 0) return null;
        return createBundle(invalidation.objectId, invalidation.objectSource,
                invalidation.version != null ? invalidation.version : 0L, invalidation.payload);
    }

    @VisibleForTesting
    @Override
    public boolean equals(Object other) {
        if (other == null) return false;
        if (other == this) return true;
        if (!(other instanceof PendingInvalidation)) return false;
        PendingInvalidation otherInvalidation = (PendingInvalidation) other;
        if (mObjectSource != otherInvalidation.mObjectSource) return false;
        if (mObjectSource == 0) return true;
        if (!mObjectId.equals(otherInvalidation.mObjectId)) return false;
        if (!mPayload.equals(otherInvalidation.mPayload)) return false;
        return mVersion == otherInvalidation.mVersion;
    }

    @Override
    public int hashCode() {
        return mObjectId == null ? mObjectSource : mObjectSource ^ mObjectId.hashCode()
                        ^ Long.valueOf(mVersion).hashCode();
    }

    @Override
    public String toString() {
        return String.format("objectSrc:%d,objectId:%s,version:%d,payload:%s", mObjectSource,
                mObjectId, mVersion, mPayload);
    }
}

@@ -486,24 +486,49 @@
 IOException;
+%0Aimport java.util.Locale;
 %0A%0Aimport jav
@@ -5111,16 +5111,27 @@
 .format(
+Locale.US, 
 %22objectS
@@ -5175,23 +5175,8 @@
 %25s%22,
- mObjectSource,
 %0A   
@@ -5183,24 +5183,39 @@
             
+ mObjectSource,
  mObjectId, 

Fix Lint Error: Explicitly use US Locale.