/*
 * Copyright (C) 2016 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.bluetooth.avrcpcontroller;

import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;

/*
 * Contains information Player Application Setting extended from BluetootAvrcpPlayerSettings
 */
class PlayerApplicationSettings {
    private static final String TAG = "PlayerApplicationSettings";

    /*
     * Values for SetPlayerApplicationSettings from AVRCP Spec V1.6 Appendix F.
     */
    static final byte REPEAT_STATUS = 0x02;
    static final byte SHUFFLE_STATUS = 0x03;

    @VisibleForTesting static final byte JNI_REPEAT_STATUS_OFF = 0x01;
    @VisibleForTesting static final byte JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT = 0x02;
    @VisibleForTesting static final byte JNI_REPEAT_STATUS_ALL_TRACK_REPEAT = 0x03;
    @VisibleForTesting static final byte JNI_REPEAT_STATUS_GROUP_REPEAT = 0x04;

    @VisibleForTesting static final byte JNI_SHUFFLE_STATUS_OFF = 0x01;
    @VisibleForTesting static final byte JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE = 0x02;
    @VisibleForTesting static final byte JNI_SHUFFLE_STATUS_GROUP_SHUFFLE = 0x03;

    @VisibleForTesting static final byte JNI_STATUS_INVALID = -1;

    /*
     * Hash map of current settings.
     */
    private SparseIntArray mSettings = new SparseIntArray();

    /*
     * Hash map of supported values, a setting should be supported by the remote in order to enable
     * in mSettings.
     */
    private SparseArray<ArrayList<Integer>> mSupportedValues =
            new SparseArray<ArrayList<Integer>>();

    /* Convert from JNI array to Java classes. */
    static PlayerApplicationSettings makeSupportedSettings(byte[] btAvrcpAttributeList) {
        PlayerApplicationSettings newObj = new PlayerApplicationSettings();
        try {
            for (int i = 0; i < btAvrcpAttributeList.length; ) {
                byte attrId = btAvrcpAttributeList[i++];
                byte numSupportedVals = btAvrcpAttributeList[i++];
                ArrayList<Integer> supportedValues = new ArrayList<Integer>();

                for (int j = 0; j < numSupportedVals; j++) {
                    // Yes, keep using i for array indexing.
                    supportedValues.add(
                            mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
                }
                newObj.mSupportedValues.put(attrId, supportedValues);
            }
        } catch (ArrayIndexOutOfBoundsException exception) {
            Log.e(TAG, "makeSupportedSettings attributeList index error.");
        }
        return newObj;
    }

    static PlayerApplicationSettings makeSettings(byte[] btAvrcpAttributeList) {
        PlayerApplicationSettings newObj = new PlayerApplicationSettings();
        try {
            for (int i = 0; i < btAvrcpAttributeList.length; ) {
                byte attrId = btAvrcpAttributeList[i++];

                newObj.mSettings.put(
                        attrId,
                        mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
            }
        } catch (ArrayIndexOutOfBoundsException exception) {
            Log.e(TAG, "makeSettings JNI_ATTRIButeList index error.");
        }
        return newObj;
    }

    public void setSupport(PlayerApplicationSettings updates) {
        mSettings = updates.mSettings;
        mSupportedValues = updates.mSupportedValues;
    }

    public boolean supportsSetting(int settingType, int settingValue) {
        if (null == mSupportedValues.get(settingType)) return false;
        return mSupportedValues.valueAt(settingType).contains(settingValue);
    }

    public boolean supportsSetting(int settingType) {
        return (null != mSupportedValues.get(settingType));
    }

    public int getSetting(int settingType) {
        return mSettings.get(settingType, -1);
    }

    // Convert a native Attribute Id/Value pair into the AVRCP equivalent value.
    @VisibleForTesting
    static int mapAttribIdValtoAvrcpPlayerSetting(byte attribId, byte attribVal) {
        if (attribId == REPEAT_STATUS) {
            switch (attribVal) {
                case JNI_REPEAT_STATUS_ALL_TRACK_REPEAT:
                    return PlaybackStateCompat.REPEAT_MODE_ALL;
                case JNI_REPEAT_STATUS_GROUP_REPEAT:
                    return PlaybackStateCompat.REPEAT_MODE_GROUP;
                case JNI_REPEAT_STATUS_OFF:
                    return PlaybackStateCompat.REPEAT_MODE_NONE;
                case JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT:
                    return PlaybackStateCompat.REPEAT_MODE_ONE;
            }
        } else if (attribId == SHUFFLE_STATUS) {
            switch (attribVal) {
                case JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE:
                    return PlaybackStateCompat.SHUFFLE_MODE_ALL;
                case JNI_SHUFFLE_STATUS_GROUP_SHUFFLE:
                    return PlaybackStateCompat.SHUFFLE_MODE_GROUP;
                case JNI_SHUFFLE_STATUS_OFF:
                    return PlaybackStateCompat.SHUFFLE_MODE_NONE;
            }
        }
        return JNI_STATUS_INVALID;
    }

    // Convert an AVRCP Setting/Value pair into the native equivalent value;
    static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) {
        if (mSetting == REPEAT_STATUS) {
            switch (mSettingVal) {
                case PlaybackStateCompat.REPEAT_MODE_NONE:
                    return JNI_REPEAT_STATUS_OFF;
                case PlaybackStateCompat.REPEAT_MODE_ONE:
                    return JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT;
                case PlaybackStateCompat.REPEAT_MODE_ALL:
                    return JNI_REPEAT_STATUS_ALL_TRACK_REPEAT;
                case PlaybackStateCompat.REPEAT_MODE_GROUP:
                    return JNI_REPEAT_STATUS_GROUP_REPEAT;
            }
        } else if (mSetting == SHUFFLE_STATUS) {
            switch (mSettingVal) {
                case PlaybackStateCompat.SHUFFLE_MODE_NONE:
                    return JNI_SHUFFLE_STATUS_OFF;
                case PlaybackStateCompat.SHUFFLE_MODE_ALL:
                    return JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE;
                case PlaybackStateCompat.SHUFFLE_MODE_GROUP:
                    return JNI_SHUFFLE_STATUS_GROUP_SHUFFLE;
            }
        }
        return JNI_STATUS_INVALID;
    }

    public static String repeatStatusToString(int repeatMode) {
        switch (repeatMode) {
            case PlaybackStateCompat.REPEAT_MODE_ALL:
                return "ALL";
            case PlaybackStateCompat.REPEAT_MODE_GROUP:
                return "GROUP";
            case PlaybackStateCompat.REPEAT_MODE_NONE:
                return "NONE";
            case PlaybackStateCompat.REPEAT_MODE_ONE:
                return "ONE";
            default:
                return "Unsupported";
        }
    }

    public static String shuffleStatusToString(int shuffleMode) {
        switch (shuffleMode) {
            case PlaybackStateCompat.SHUFFLE_MODE_NONE:
                return "NONE";
            case PlaybackStateCompat.SHUFFLE_MODE_ALL:
                return "ALL";
            case PlaybackStateCompat.SHUFFLE_MODE_GROUP:
                return "GROUP";
            default:
                return "Unsupported";
        }
    }

    @Override
    public String toString() {
        return "<PlayerApplicationSettings"
                + " repeat="
                + repeatStatusToString(getSetting(REPEAT_STATUS))
                + " shuffle="
                + shuffleStatusToString(getSetting(SHUFFLE_STATUS))
                + ">";
    }
}
