/*
 * 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.
 */

#define LOG_TAG "APM::SessionRoute"
//#define LOG_NDEBUG 0

#include "SessionRoute.h"
#include "HwModule.h"
#include "AudioGain.h"
#include "DeviceDescriptor.h"
#include <utils/Log.h>

namespace android {

// --- SessionRoute class implementation
void SessionRoute::log(const char* prefix)
{
    ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d",
          prefix, mStreamType, mSource, mSession,
          mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE,
          mRefCount, mActivityCount);
}

// --- SessionRouteMap class implementation
bool SessionRouteMap::hasRoute(audio_session_t session)
{
    return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0;
}

bool SessionRouteMap::getAndClearRouteChanged(audio_session_t session)
{
    if (indexOfKey(session) >= 0) {
        if (valueFor(session)->mChanged) {
            valueFor(session)->mChanged = false;
            return true;
        }
    }
    return false;
}

void SessionRouteMap::removeRoute(audio_session_t session)
{
    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
    if (route != 0) {
        ALOG_ASSERT(route->mRefCount > 0);
        --route->mRefCount;
        if (route->mRefCount <= 0) {
            removeItem(session);
        }
    }
}

int SessionRouteMap::incRouteActivity(audio_session_t session)
{
    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
    return route != 0 ? ++(route->mActivityCount) : -1;
}

int SessionRouteMap::decRouteActivity(audio_session_t session)
{
    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
    if (route != 0 && route->mActivityCount > 0) {
        return --(route->mActivityCount);
    } else {
        return -1;
    }
}

void SessionRouteMap::log(const char* caption)
{
    ALOGI("%s ----", caption);
    for (size_t index = 0; index < size(); index++) {
        valueAt(index)->log("  ");
    }
}

void SessionRouteMap::addRoute(audio_session_t session,
                               audio_stream_type_t streamType,
                               audio_source_t source,
                               const sp<DeviceDescriptor>& descriptor,
                               uid_t uid)
{
    if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) {
        ALOGE("Adding Output Route to InputRouteMap");
        return;
    } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) {
        ALOGE("Adding Input Route to OutputRouteMap");
        return;
    }

    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;

    if (route != 0) {
        if (descriptor != 0 || route->mDeviceDescriptor != 0) {
            route->mChanged = true;
        }
        route->mRefCount++;
        route->mDeviceDescriptor = descriptor;
    } else {
        route = new SessionRoute(session, streamType, source, descriptor, uid);
        route->mRefCount++;
        if (descriptor != 0) {
            route->mChanged = true;
        }
        add(session, route);
    }
}

audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType,
                                                          const DeviceVector& availableDevices)
{
    audio_devices_t device = AUDIO_DEVICE_NONE;

    for (size_t index = 0; index < size(); index++) {
        sp<SessionRoute> route = valueAt(index);
        if (streamType == route->mStreamType && route->isActiveOrChanged()
                && route->mDeviceDescriptor != 0) {
            device = route->mDeviceDescriptor->type();
            if (!availableDevices.getDevicesFromType(device).isEmpty()) {
                break;
            }
        }
    }
    return device;
}

} // namespace android
