/*
 * Copyright (C) 2013 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.printservice;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;

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

import java.io.IOException;

/**
 * This class describes a {@link PrintService}. A print service knows
 * how to communicate with one or more printers over one or more protocols
 * and exposes printers for use by the applications via the platform print
 * APIs.
 *
 * @see PrintService
 * @see android.print.PrintManager
 *
 * @hide
 */
@SystemApi
public final class PrintServiceInfo implements Parcelable {

    private static final String LOG_TAG = PrintServiceInfo.class.getSimpleName();

    private static final String TAG_PRINT_SERVICE = "print-service";

    private final String mId;

    private boolean mIsEnabled;

    private final ResolveInfo mResolveInfo;

    private final String mSettingsActivityName;

    private final String mAddPrintersActivityName;

    private final String mAdvancedPrintOptionsActivityName;

    /**
     * Creates a new instance.
     *
     * @hide
     */
    public PrintServiceInfo(Parcel parcel) {
        mId = parcel.readString();
        mIsEnabled = parcel.readByte() != 0;
        mResolveInfo = parcel.readParcelable(null, android.content.pm.ResolveInfo.class);
        mSettingsActivityName = parcel.readString();
        mAddPrintersActivityName = parcel.readString();
        mAdvancedPrintOptionsActivityName = parcel.readString();
    }

    /**
     * Creates a new instance.
     *
     * @param resolveInfo The service resolve info.
     * @param settingsActivityName Optional settings activity name.
     * @param addPrintersActivityName Optional add printers activity name.
     * @param advancedPrintOptionsActivityName Optional advanced print options activity.
     *
     * @hide
     */
    public PrintServiceInfo(ResolveInfo resolveInfo, String settingsActivityName,
            String addPrintersActivityName, String advancedPrintOptionsActivityName) {
        mId = new ComponentName(resolveInfo.serviceInfo.packageName,
                resolveInfo.serviceInfo.name).flattenToString();
        mResolveInfo = resolveInfo;
        mSettingsActivityName = settingsActivityName;
        mAddPrintersActivityName = addPrintersActivityName;
        mAdvancedPrintOptionsActivityName = advancedPrintOptionsActivityName;
    }

    /**
     * Return the component name for this print service.
     *
     * @return The component name for this print service.
     */
    public @NonNull ComponentName getComponentName() {
        return new ComponentName(mResolveInfo.serviceInfo.packageName,
                mResolveInfo.serviceInfo.name);
    }

    /**
     * Creates a new instance.
     *
     * @param context Context for accessing resources.
     * @param resolveInfo The service resolve info.
     * @return The created instance.
     *
     * @hide
     */
    public static PrintServiceInfo create(Context context, ResolveInfo resolveInfo) {
        String settingsActivityName = null;
        String addPrintersActivityName = null;
        String advancedPrintOptionsActivityName = null;

        XmlResourceParser parser = null;
        PackageManager packageManager = context.getPackageManager();
        parser = resolveInfo.serviceInfo.loadXmlMetaData(packageManager,
                PrintService.SERVICE_META_DATA);
        if (parser != null) {
            try {
                int type = 0;
                while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
                    type = parser.next();
                }

                String nodeName = parser.getName();
                if (!TAG_PRINT_SERVICE.equals(nodeName)) {
                    Log.e(LOG_TAG, "Ignoring meta-data that does not start with "
                            + TAG_PRINT_SERVICE + " tag");
                } else {
                    Resources resources = packageManager.getResourcesForApplication(
                            resolveInfo.serviceInfo.applicationInfo);
                    AttributeSet allAttributes = Xml.asAttributeSet(parser);
                    TypedArray attributes = resources.obtainAttributes(allAttributes,
                            com.android.internal.R.styleable.PrintService);

                    settingsActivityName = attributes.getString(
                            com.android.internal.R.styleable.PrintService_settingsActivity);

                    addPrintersActivityName = attributes.getString(
                            com.android.internal.R.styleable.PrintService_addPrintersActivity);

                    advancedPrintOptionsActivityName = attributes.getString(com.android.internal
                            .R.styleable.PrintService_advancedPrintOptionsActivity);

                    attributes.recycle();
                }
            } catch (IOException ioe) {
                Log.w(LOG_TAG, "Error reading meta-data:" + ioe);
            } catch (XmlPullParserException xppe) {
                Log.w(LOG_TAG, "Error reading meta-data:" + xppe);
            } catch (NameNotFoundException e) {
                Log.e(LOG_TAG, "Unable to load resources for: "
                        + resolveInfo.serviceInfo.packageName);
            } finally {
                if (parser != null) {
                    parser.close();
                }
            }
        }

        return new PrintServiceInfo(resolveInfo, settingsActivityName,
                addPrintersActivityName, advancedPrintOptionsActivityName);
    }

    /**
     * The accessibility service id.
     * <p>
     * <strong>Generated by the system.</strong>
     * </p>
     *
     * @return The id.
     *
     * @hide
     */
    public String getId() {
        return mId;
    }

    /**
     * If the service was enabled when it was read from the system.
     *
     * @return The id.
     *
     * @hide
     */
    public boolean isEnabled() {
        return mIsEnabled;
    }

    /**
     * Mark a service as enabled or not
     *
     * @param isEnabled If the service should be marked as enabled.
     *
     * @hide
     */
    public void setIsEnabled(boolean isEnabled) {
        mIsEnabled = isEnabled;
    }

    /**
     * The service {@link ResolveInfo}.
     *
     * @return The info.
     *
     * @hide
     */
    public ResolveInfo getResolveInfo() {
        return mResolveInfo;
    }

    /**
     * The settings activity name.
     * <p>
     * <strong>Statically set from
     * {@link PrintService#SERVICE_META_DATA meta-data}.</strong>
     * </p>
     *
     * @return The settings activity name.
     *
     * @hide
     */
    public String getSettingsActivityName() {
        return mSettingsActivityName;
    }

    /**
     * The add printers activity name.
     * <p>
     * <strong>Statically set from
     * {@link PrintService#SERVICE_META_DATA meta-data}.</strong>
     * </p>
     *
     * @return The add printers activity name.
     *
     * @hide
     */
    public String getAddPrintersActivityName() {
        return mAddPrintersActivityName;
    }

    /**
     * The advanced print options activity name.
     * <p>
     * <strong>Statically set from
     * {@link PrintService#SERVICE_META_DATA meta-data}.</strong>
     * </p>
     *
     * @return The advanced print options activity name.
     *
     * @hide
     */
    public String getAdvancedOptionsActivityName() {
        return mAdvancedPrintOptionsActivityName;
    }

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

    @Override
    public void writeToParcel(Parcel parcel, int flagz) {
        parcel.writeString(mId);
        parcel.writeByte((byte)(mIsEnabled ? 1 : 0));
        parcel.writeParcelable(mResolveInfo, 0);
        parcel.writeString(mSettingsActivityName);
        parcel.writeString(mAddPrintersActivityName);
        parcel.writeString(mAdvancedPrintOptionsActivityName);
    }

    @Override
    public int hashCode() {
        return 31 + ((mId == null) ? 0 : mId.hashCode());
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        PrintServiceInfo other = (PrintServiceInfo) obj;
        if (mId == null) {
            if (other.mId != null) {
                return false;
            }
        } else if (!mId.equals(other.mId)) {
            return false;
        }
        return true;
    }

    @NonNull
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("PrintServiceInfo{");
        builder.append("id=").append(mId);
        builder.append("isEnabled=").append(mIsEnabled);
        builder.append(", resolveInfo=").append(mResolveInfo);
        builder.append(", settingsActivityName=").append(mSettingsActivityName);
        builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName);
        builder.append(", advancedPrintOptionsActivityName=")
                .append(mAdvancedPrintOptionsActivityName);
        builder.append("}");
        return builder.toString();
    }

    public static final @android.annotation.NonNull Parcelable.Creator<PrintServiceInfo> CREATOR =
            new Parcelable.Creator<PrintServiceInfo>() {
        @Override
        public PrintServiceInfo createFromParcel(Parcel parcel) {
            return new PrintServiceInfo(parcel);
        }

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