/*
 * Copyright 2018 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 com.android.settingslib.media;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;

import androidx.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * MediaDevice represents a media device(such like Bluetooth device, cast device and phone device).
 */
public abstract class MediaDevice implements Comparable<MediaDevice> {
    private static final String TAG = "MediaDevice";

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({MediaDeviceType.TYPE_CAST_DEVICE,
            MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
            MediaDeviceType.TYPE_PHONE_DEVICE})
    public @interface MediaDeviceType {
        int TYPE_PHONE_DEVICE = 1;
        int TYPE_CAST_DEVICE = 2;
        int TYPE_BLUETOOTH_DEVICE = 3;
    }

    private int mConnectedRecord;

    protected Context mContext;
    protected int mType;

    MediaDevice(Context context, @MediaDeviceType int type) {
        mType = type;
        mContext = context;
    }

    void initDeviceRecord() {
        ConnectionRecordManager.getInstance().fetchLastSelectedDevice(mContext);
        mConnectedRecord = ConnectionRecordManager.getInstance().fetchConnectionRecord(mContext,
                getId());
    }

    /**
     * Get name from MediaDevice.
     *
     * @return name of MediaDevice.
     */
    public abstract String getName();

    /**
     * Get summary from MediaDevice.
     *
     * @return summary of MediaDevice.
     */
    public abstract String getSummary();

    /**
     * Get icon of MediaDevice.
     *
     * @return drawable of icon.
     */
    public abstract Drawable getIcon();

    /**
     * Get unique ID that represent MediaDevice
     * @return unique id of MediaDevice
     */
    public abstract String getId();

    /**
     * Transfer MediaDevice for media
     *
     * @return result of transfer media
     */
    public abstract boolean connect();

    void setConnectedRecord() {
        mConnectedRecord++;
        ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
                mConnectedRecord);
    }

    /**
     * Stop transfer MediaDevice
     */
    public abstract void disconnect();

    /**
     * According the MediaDevice type to check whether we are connected to this MediaDevice.
     *
     * @return Whether it is connected.
     */
    public abstract boolean isConnected();

    /**
     * Rules:
     * 1. If there is one of the connected devices identified as a carkit, this carkit will
     * be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
     * 2. For devices without any usage data yet
     * WiFi device group sorted by alphabetical order + BT device group sorted by alphabetical
     * order + phone speaker
     * 3. For devices with usage record.
     * The most recent used one + device group with usage info sorted by how many times the
     * device has been used.
     * 4. Phone device always in the top and the connected Bluetooth devices, cast devices and
     * phone device will be always above on the disconnect Bluetooth devices.
     *
     * So the device list will look like 5 slots ranked as below.
     * Rule 4 + Rule 1 + the most recently used device + Rule 3 + Rule 2
     * Any slot could be empty. And available device will belong to one of the slots.
     *
     * @return a negative integer, zero, or a positive integer
     * as this object is less than, equal to, or greater than the specified object.
     */
    @Override
    public int compareTo(MediaDevice another) {
        // Check Bluetooth device is have same connection state
        if (isConnected() ^ another.isConnected()) {
            if (isConnected()) {
                return -1;
            } else {
                return 1;
            }
        }

        // Phone device always in the top.
        if (mType == MediaDeviceType.TYPE_PHONE_DEVICE) {
            return -1;
        } else if (another.mType == MediaDeviceType.TYPE_PHONE_DEVICE) {
            return 1;
        }
        // Check carkit
        if (isCarKitDevice()) {
            return -1;
        } else if (another.isCarKitDevice()) {
            return 1;
        }
        // Set last used device at the first item
        String lastSelectedDevice = ConnectionRecordManager.getInstance().getLastSelectedDevice();
        if (TextUtils.equals(lastSelectedDevice, getId())) {
            return -1;
        } else if (TextUtils.equals(lastSelectedDevice, another.getId())) {
            return 1;
        }
        // Sort by how many times the device has been used if there is usage record
        if ((mConnectedRecord != another.mConnectedRecord)
                && (another.mConnectedRecord > 0 || mConnectedRecord > 0)) {
            return (another.mConnectedRecord - mConnectedRecord);
        }
        // Both devices have never been used
        // To devices with the same type, sort by alphabetical order
        if (mType == another.mType) {
            final String s1 = getName();
            final String s2 = another.getName();
            return s1.compareToIgnoreCase(s2);
        }
        // Both devices have never been used, the priority is Phone > Cast > Bluetooth
        return mType - another.mType;
    }

    /**
     * Check if it is CarKit device
     * @return true if it is CarKit device
     */
    protected boolean isCarKitDevice() {
        return false;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof MediaDevice)) {
            return false;
        }
        final MediaDevice otherDevice = (MediaDevice) obj;
        return otherDevice.getId().equals(getId());
    }
}