/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content.pm;

import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;

import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Set;

/**
 * The {@link com.android.server.pm.PackageManagerService} maintains some
 * {@link IntentFilterVerificationInfo}s for each domain / package name.
 *
 * @hide
 */
@SystemApi
public final class IntentFilterVerificationInfo implements Parcelable {
    private static final String TAG = IntentFilterVerificationInfo.class.getName();

    private static final String TAG_DOMAIN = "domain";
    private static final String ATTR_DOMAIN_NAME = "name";
    private static final String ATTR_PACKAGE_NAME = "packageName";
    private static final String ATTR_STATUS = "status";

    private ArraySet<String> mDomains = new ArraySet<>();
    private String mPackageName;
    private int mMainStatus;

    /** @hide */
    public IntentFilterVerificationInfo() {
        mPackageName = null;
        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
    }

    /** @hide */
    public IntentFilterVerificationInfo(String packageName, ArraySet<String> domains) {
        mPackageName = packageName;
        mDomains = domains;
        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
    }

    /** @hide */
    public IntentFilterVerificationInfo(XmlPullParser parser)
            throws IOException, XmlPullParserException {
        readFromXml(parser);
    }

    /** @hide */
    public IntentFilterVerificationInfo(Parcel source) {
        readFromParcel(source);
    }

    public String getPackageName() {
        return mPackageName;
    }

    public int getStatus() {
        return mMainStatus;
    }

    /** @hide */
    public void setStatus(int s) {
        if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED &&
                s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
            mMainStatus = s;
        } else {
            Log.w(TAG, "Trying to set a non supported status: " + s);
        }
    }

    public Set<String> getDomains() {
        return mDomains;
    }

    /** @hide */
    public void setDomains(ArraySet<String> list) {
        mDomains = list;
    }

    /** @hide */
    public String getDomainsString() {
        StringBuilder sb = new StringBuilder();
        for (String str : mDomains) {
            if (sb.length() > 0) {
                sb.append(" ");
            }
            sb.append(str);
        }
        return sb.toString();
    }

    String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
        String value = parser.getAttributeValue(null, attribute);
        if (value == null) {
            String msg = "Missing element under " + TAG +": " + attribute + " at " +
                    parser.getPositionDescription();
            Log.w(TAG, msg);
            return defaultValue;
        } else {
            return value;
        }
    }

    int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
        String value = parser.getAttributeValue(null, attribute);
        if (TextUtils.isEmpty(value)) {
            String msg = "Missing element under " + TAG +": " + attribute + " at " +
                    parser.getPositionDescription();
            Log.w(TAG, msg);
            return defaultValue;
        } else {
            return Integer.parseInt(value);
        }
    }

    /** @hide */
    public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
            IOException {
        mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
        if (mPackageName == null) {
            Log.e(TAG, "Package name cannot be null!");
        }
        int status = getIntFromXml(parser, ATTR_STATUS, -1);
        if (status == -1) {
            Log.e(TAG, "Unknown status value: " + status);
        }
        mMainStatus = status;

        int outerDepth = parser.getDepth();
        int type;
        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG
                    || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals(TAG_DOMAIN)) {
                String name = getStringFromXml(parser, ATTR_DOMAIN_NAME, null);
                if (!TextUtils.isEmpty(name)) {
                    mDomains.add(name);
                }
            } else {
                Log.w(TAG, "Unknown tag parsing IntentFilter: " + tagName);
            }
            XmlUtils.skipCurrentTag(parser);
        }
    }

    /** @hide */
    public void writeToXml(XmlSerializer serializer) throws IOException {
        serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
        serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus));
        for (String str : mDomains) {
            serializer.startTag(null, TAG_DOMAIN);
            serializer.attribute(null, ATTR_DOMAIN_NAME, str);
            serializer.endTag(null, TAG_DOMAIN);
        }
    }

    /** @hide */
    public String getStatusString() {
        return getStatusStringFromValue(((long)mMainStatus) << 32);
    }

    /** @hide */
    public static String getStatusStringFromValue(long val) {
        StringBuilder sb = new StringBuilder();
        switch ((int)(val >> 32)) {
            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
                sb.append("always : ");
                sb.append(Long.toHexString(val & 0x00000000FFFFFFFF));
                break;

            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
                sb.append("ask");
                break;

            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
                sb.append("never");
                break;

            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK:
                sb.append("always-ask");
                break;

            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
            default:
                sb.append("undefined");
                break;
        }
        return sb.toString();
    }

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

    private void readFromParcel(Parcel source) {
        mPackageName = source.readString();
        mMainStatus = source.readInt();
        ArrayList<String> list = new ArrayList<>();
        source.readStringList(list);
        mDomains.addAll(list);
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mPackageName);
        dest.writeInt(mMainStatus);
        dest.writeStringList(new ArrayList<>(mDomains));
    }

    public static final Creator<IntentFilterVerificationInfo> CREATOR =
            new Creator<IntentFilterVerificationInfo>() {
                public IntentFilterVerificationInfo createFromParcel(Parcel source) {
                    return new IntentFilterVerificationInfo(source);
                }
                public IntentFilterVerificationInfo[] newArray(int size) {
                    return new IntentFilterVerificationInfo[size];
                }
            };
}
