package com.stripe.android.model;

import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.stripe.android.ObjectBuilder;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

import org.json.JSONException;
import org.json.JSONObject;

import static com.stripe.android.model.StripeJsonUtils.optString;

/**
 * Model for an owner <a href="https://stripe.com/docs/api#source_object-owner-address">address</a>
 * object in the Source api.
 */
public final class Address extends StripeModel implements StripeParamsModel, Parcelable {

    @IntDef({
            RequiredBillingAddressFields.NONE,
            RequiredBillingAddressFields.ZIP,
            RequiredBillingAddressFields.FULL,
            RequiredBillingAddressFields.NAME})
    @Retention(RetentionPolicy.SOURCE)
    public @interface RequiredBillingAddressFields {
        int NONE = 0;
        int ZIP = 1;
        int FULL = 2;
        int NAME = 3;
    }

    private static final String FIELD_CITY = "city";
    /* 2 Character Country Code */
    private static final String FIELD_COUNTRY = "country";
    private static final String FIELD_LINE_1 = "line1";
    private static final String FIELD_LINE_2 = "line2";
    private static final String FIELD_POSTAL_CODE = "postal_code";
    private static final String FIELD_STATE = "state";

    @Nullable private final String mCity;
    @Nullable private final String mCountry;
    @Nullable private final String mLine1;
    @Nullable private final String mLine2;
    @Nullable private final String mPostalCode;
    @Nullable private final String mState;

    private Address(
            @Nullable String city,
            @Nullable String country,
            @Nullable String line1,
            @Nullable String line2,
            @Nullable String postalCode,
            @Nullable String state) {
        mCity = city;
        mCountry = country;
        mLine1 = line1;
        mLine2 = line2;
        mPostalCode = postalCode;
        mState = state;
    }

    private Address(@NonNull Builder addressBuilder) {
        this(addressBuilder.mCity, addressBuilder.mCountry, addressBuilder.mLine1,
                addressBuilder.mLine2, addressBuilder.mPostalCode, addressBuilder.mState);
    }

    protected Address(@NonNull Parcel in) {
        mCity = in.readString();
        mCountry = in.readString();
        mLine1 = in.readString();
        mLine2 = in.readString();
        mPostalCode = in.readString();
        mState = in.readString();
    }

    @Nullable
    public String getCity() {
        return mCity;
    }

    @Nullable
    public String getCountry() {
        return mCountry;
    }

    @Nullable
    public String getLine1() {
        return mLine1;
    }

    @Nullable
    public String getLine2() {
        return mLine2;
    }

    @Nullable
    public String getPostalCode() {
        return mPostalCode;
    }

    @Nullable
    public String getState() {
        return mState;
    }

    @NonNull
    @Override
    public Map<String, Object> toParamMap() {
        final Map<String, Object> map = new HashMap<>();
        if (mCity != null) {
            map.put(FIELD_CITY, mCity);
        }
        if (mCountry != null) {
            map.put(FIELD_COUNTRY, mCountry);
        }
        if (mLine1 != null) {
            map.put(FIELD_LINE_1, mLine1);
        }
        if (mLine2 != null) {
            map.put(FIELD_LINE_2, mLine2);
        }
        if (mPostalCode != null) {
            map.put(FIELD_POSTAL_CODE, mPostalCode);
        }
        if (mState != null) {
            map.put(FIELD_STATE, mState);
        }
        return map;
    }

    @Nullable
    public static Address fromString(@Nullable String jsonString) {
        try {
            return fromJson(new JSONObject(jsonString));
        } catch (JSONException ignored) {
            return null;
        }
    }

    @Nullable
    public static Address fromJson(@Nullable JSONObject jsonObject) {
        if (jsonObject == null) {
            return null;
        }

        final String city = optString(jsonObject, FIELD_CITY);
        final String country = optString(jsonObject, FIELD_COUNTRY);
        final String line1 = optString(jsonObject, FIELD_LINE_1);
        final String line2 = optString(jsonObject, FIELD_LINE_2);
        final String postalCode = optString(jsonObject, FIELD_POSTAL_CODE);
        final String state = optString(jsonObject, FIELD_STATE);
        return new Address(city, country, line1, line2, postalCode, state);
    }

    @Override
    public boolean equals(Object obj) {
        return this == obj || (obj instanceof Address && typedEquals((Address) obj));
    }

    private boolean typedEquals(@NonNull Address address) {
        return Objects.equals(mCity, address.mCity)
                && Objects.equals(mCountry, address.mCountry)
                && Objects.equals(mLine1, address.mLine1)
                && Objects.equals(mLine2, address.mLine2)
                && Objects.equals(mPostalCode, address.mPostalCode)
                && Objects.equals(mState, address.mState);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mCity, mCountry, mLine1, mLine2, mPostalCode, mState);
    }

    /************** Parcelable *********************/
    public static final Parcelable.Creator<Address> CREATOR
            = new Parcelable.Creator<Address>() {

        @Override
        public Address createFromParcel(Parcel in) {
            return new Address(in);
        }

        @Override
        public Address[] newArray(int size) {
            return new Address[size];
        }
    };

    @Override
    public void writeToParcel(@NonNull Parcel out, int flags) {
        out.writeString(mCity);
        out.writeString(mCountry);
        out.writeString(mLine1);
        out.writeString(mLine2);
        out.writeString(mPostalCode);
        out.writeString(mState);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static class Builder implements ObjectBuilder<Address> {
        private String mCity;
        private String mCountry;
        private String mLine1;
        private String mLine2;
        private String mPostalCode;
        private String mState;

        @NonNull
        public Builder setCity(@Nullable String city) {
            mCity = city;
            return this;
        }

        @NonNull
        public Builder setCountry(@Nullable String country) {
            mCountry = country != null ? country.toUpperCase(Locale.ROOT) : null;
            return this;
        }

        @NonNull
        public Builder setLine1(@Nullable String line1) {
            mLine1 = line1;
            return this;
        }

        @NonNull
        public Builder setLine2(@Nullable String line2) {
            mLine2 = line2;
            return this;
        }

        @NonNull
        public Builder setPostalCode(@Nullable String postalCode) {
            mPostalCode = postalCode;
            return this;
        }

        @NonNull
        public Builder setState(@Nullable String state) {
            mState = state;
            return this;
        }

        @NonNull
        public Address build() {
            return new Address(this);
        }
    }
}

@@ -89,43 +89,8 @@
 e;%0A%0A
-import androidx.annotation.IntDef;%0A
 impo
@@ -121,16 +121,16 @@
 onNull;%0A
+
 import a
@@ -205,92 +205,8 @@
 r;%0A%0A
-import java.lang.annotation.Retention;%0Aimport java.lang.annotation.RetentionPolicy;%0A
 impo
@@ -565,16 +565,16 @@
 pi.%0A */%0A
+
 public f
@@ -659,397 +659,8 @@
 e %7B%0A
-%0A    @IntDef(%7B%0A            RequiredBillingAddressFields.NONE,%0A            RequiredBillingAddressFields.ZIP,%0A            RequiredBillingAddressFields.FULL,%0A            RequiredBillingAddressFields.NAME%7D)%0A    @Retention(RetentionPolicy.SOURCE)%0A    public @interface RequiredBillingAddressFields %7B%0A        int NONE = 0;%0A        int ZIP = 1;%0A        int FULL = 2;%0A        int NAME = 3;%0A    %7D%0A%0A
     

Remove unused Address.RequiredBillingAddressFields (#1588)