// preferences should be a member
wns::scheduler::ConnectionID
ProportionalFair::getNextConnection(SchedulerStatePtr schedulerState,
                                    std::priority_queue<UserPreference> preferences)
{
    wns::scheduler::ConnectionID next = -1;

    while (!preferences.empty())
    {
        int priority = schedulerState->currentState->getCurrentPriority();
        const float preference = preferences.top().first;
        const UserID user = preferences.top().second;
        MESSAGE_SINGLE(NORMAL, logger, "Selected user="******"Selected connection with CID="<<next);
            return next;
        }
        preferences.pop();
    }
    return next;
}
// maybe we could get rid of the phase length
void
ProportionalFair::updatePastDataRates(std::map<UserID, float> bitsBeforeThisFrame,
                                      std::map<UserID, float> bitsAfterThisFrame,
                                      simTimeType phaseLength)
{
    UserID user;
    float bitsThisFrame = 0.0;
    float pastDataRate = 0.0;
    float currentRate = 0.0;

    for (std::map<UserID, float>::const_iterator iter = bitsBeforeThisFrame.begin();
         iter != bitsBeforeThisFrame.end(); ++iter)
    {
        user = iter->first;
        bitsThisFrame = bitsBeforeThisFrame[user] - bitsAfterThisFrame[user];
        currentRate = bitsThisFrame / phaseLength;
        pastDataRate = pastDataRates[user];

        if (pastDataRates.find(user) != pastDataRates.end()) {
            pastDataRates[user] = (1.0-historyWeight) * currentRate + historyWeight * pastDataRates[user];
        } else {
            // new user
            pastDataRates[user] = currentRate;
        }
        MESSAGE_SINGLE(NORMAL, logger, "updatePastDataRates("<<user.getName()<<","<<phaseLength<<"s): pastDataRate: new= "<< pastDataRates[user]<<" bit/s, old= "<<pastDataRate<<" bit/s, currentRate= "<<currentRate<<" bit/s");
    }
}
wns::scheduler::PowerCapabilities
Strategy::getPowerCapabilities(const UserID user) const
{
    /* This method changes the state "schedulerState->powerCapabilities".
       This is good and ok if all users are the same (default of all systems),
       but if they are different (future), APC must call this function anytime.
    */
    switch (schedulerState->powerControlType)
    {
        case PowerControlDLMaster:
        {
            schedulerState->powerCapabilities = 
                colleagues.registry->getPowerCapabilities(); 
            break;
        }
        case PowerControlULMaster:
        {
            // peer unknown. Assume peer=UT.
            if (!user.isValid()) 
            { 
                MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities(NULL): asking registry...");
                schedulerState->powerCapabilities = colleagues.registry->getPowerCapabilities(user); 

                MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities(NULL): nominal="
                    << schedulerState->powerCapabilities.nominalPerSubband);

                return schedulerState->powerCapabilities;
            }
            schedulerState->powerCapabilities = colleagues.registry->getPowerCapabilities(user); 
            break;
        }
        case PowerControlULSlave:
        {
            if (schedulerState->defaultTxPower!=wns::Power()) 
            {
                // don't use powerCapabilities but masterBurst instead
                assure(schedulerState->defaultTxPower!=wns::Power(),"undefined defaultTxPower");
                schedulerState->powerCapabilities.nominalPerSubband = schedulerState->defaultTxPower;
                schedulerState->powerCapabilities.maxPerSubband = schedulerState->defaultTxPower;
                // limit not used for slave
                schedulerState->powerCapabilities.maxOverall = schedulerState->defaultTxPower * 1000.0; 
            } 
            // not given (by masterBurst) because there is an InputSchedulingMap (new method)
            else 
            { 
                assure(schedulerState->currentState->strategyInput->inputSchedulingMap != wns::scheduler::SchedulingMapPtr(), 
                    "need inputSchedulingMap with txPower information");

            // the power settings in inputSchedulingMap can be overwritten
            // if APC strategy is changed to use nominal power. Therefore we need these values:
            schedulerState->powerCapabilities = colleagues.registry->getPowerCapabilities(); 
            }
            break;
        }
        default:
        {
            throw wns::Exception("invalid powerControlType");
        }
    } // switch(powerControlType)
    if (user.isValid()) 
    {
        MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities("
            << user.getName() << "): nominal="
            << schedulerState->powerCapabilities.nominalPerSubband);

    } 
    else 
    {
        MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities(NULL): nominal="
            << schedulerState->powerCapabilities.nominalPerSubband);
    }
    assure(schedulerState->powerCapabilities.nominalPerSubband != wns::Power(),
        "undefined power nominalPerSubband="<<schedulerState->powerCapabilities.nominalPerSubband);

    return schedulerState->powerCapabilities;
}
std::priority_queue<ProportionalFair::UserPreference>
ProportionalFair::calculateUserPreferences(UserSet activeUsers, bool txStrategy) const
{
    std::map<UserID, ChannelQualityOnOneSubChannel> sinrs;

    // preference for every user in a priority queue which automatically sorts
    std::priority_queue<UserPreference> preferences;
    assure(preferences.size()==0, "preferences.size() must be 0");

    for ( UserSet::const_iterator iter = activeUsers.begin();
          iter != activeUsers.end(); ++iter)
    {
        UserID user = *iter;
        assure(user.isValid(), "No valid user");

        //determines the users SINRs depending on the tx/Rx mode of the strategy
        if (txStrategy)
        {
            sinrs[user] = colleagues.registry->estimateTxSINRAt(user);
        }
        else
        {
            sinrs[user] = colleagues.registry->estimateRxSINROf(user);
        }

        // calculate PhyModeRate for available users
        wns::Ratio sinr(sinrs[user].carrier / sinrs[user].interference);
        wns::service::phy::phymode::PhyModeInterfacePtr phyMode = colleagues.registry->getPhyModeMapper()->getBestPhyMode(sinr);
        assure(phyMode->isValid(),"invalid PhyMode");

        // calculate userRate, which is the maximum possible data rate
        // for one subChannel for the best PhyMode available here
        float phyModeRate = phyMode->getDataRate(); // rate [b/s] of one subChannel
        float referenceRate;
        float pastDataRate = 1.0;

        // get the past data rate for this user:
        // iterate over the global past data rates map of all users and
        // find the past data rate for this user
        // there is exactly one pastDataRate value per userID
        int weight = colleagues.registry->getTotalNumberOfUsers(user);
        for (std::map<UserID, float>::const_iterator iter = pastDataRates.begin();
             iter != pastDataRates.end(); ++iter)
        {
            if (iter->first/*userID*/ == user)
            {
                float dataRate = iter->second;
                // a RN must get a better share of the bandwidth
                // here: proportional to its number of users:
                assure(weight>0, "numberOfUsers(" <<user.getName()<<")=" << weight);
                dataRate /= static_cast<float>(weight);
                // dataRate now has the meaning of a weight.
                pastDataRate = dataRate;
            }
        } // for all userIDs in pastDataRates

        if (pastDataRate < 0.01)
            pastDataRate = 0.01;

        // preference is achievable current user rate divided by a history
        // factor that takes the past throughput of this user into account

        assure(maxRateOfSubchannel>0.0, "unknown maxRateOfSubchannel");

        // goal is either rate (true) or resource (false) fairness
        if (rateFairness == true)
        {
            referenceRate = maxRateOfSubchannel;
        }
        else
        {
            referenceRate = phyModeRate;
        }

        // maxRateOfSubchannel is constant, with range [0..1][bit/s]
        float resultMaxThroughput = referenceRate / maxRateOfSubchannel;
        float resultPropFair      = referenceRate / pastDataRate; // can be any range
        if (scalingBetweenMaxTPandPFair <= 0.5) {
            // variate the preference for each user, so that they differ a little bit (1%)
            // and the automatic sorting does not always give the same order
            // (would be a problem for identical preference weights).
            resultMaxThroughput *=  (1 + 0.01*(*preferenceVariationDistribution)());
        }
        float UserPref =
            (1.0-scalingBetweenMaxTPandPFair) * resultMaxThroughput
            +scalingBetweenMaxTPandPFair  * resultPropFair;

        MESSAGE_SINGLE(NORMAL, logger, "getPreference("<<user.getName()<<"): weight=" << weight << ", pastDataRate= "<<pastDataRate<<" bit/s, UserPreference= "<<UserPref<<" (resultMaxThroughput="<<resultMaxThroughput<<",resultProportionalFair="<<resultPropFair<<")");

        // calculate preferences for users and order them
        preferences.push(UserPreference(UserPref, user));
    }
    return preferences;
}
DSAResult
BestCapacity::getSubChannelWithDSA(RequestForResource& request,
                                   SchedulerStatePtr schedulerState,
                                   SchedulingMapPtr schedulingMap)
{
    DSAResult dsaResult;
    UserID user = request.user;
    if (userInfoMap.find(user) == userInfoMap.end()) {
        userInfoMap.insert(UserInfoMap::value_type(user, UserInfo(schedulerState->currentState->strategyInput->fChannels)));
    }
    UserInfo& userInfo = userInfoMap.find(user)->second;

    int lastUsedSubChannel = userInfo.lastUsedSubChannel;
    int subChannel = lastUsedSubChannel;
    int maxSubChannel = schedulingMap->subChannels.size();
    int maxTimeSlots = schedulerState->currentState->strategyInput->getNumberOfTimeSlots();
    int lastUsedTimeSlot = userInfo.lastUsedTimeSlot;
    int timeSlot = lastUsedTimeSlot;

    assure(maxSubChannel==schedulerState->currentState->strategyInput->fChannels,"maxSubChannel="<<maxSubChannel<<"!=fChannels");
    ChannelQualitiesOnAllSubBandsPtr channelQualitiesOnAllSubBands = userInfo.channelQualitiesOnAllSubBands;
    if (channelQualitiesOnAllSubBands==ChannelQualitiesOnAllSubBandsPtr()) { // empty
        assure(channelQualitiesOfAllUsers->knowsUser(user),"channelQualitiesOfAllUsers["<<user.getName()<<"] invalid");
        // ^ or should we ask the registryProxy for CQI here?
        channelQualitiesOnAllSubBands = channelQualitiesOfAllUsers->find(user)->second;
        userInfo.channelQualitiesOnAllSubBands = channelQualitiesOnAllSubBands;
    }
    MESSAGE_SINGLE(NORMAL, logger, "getSubChannelWithDSA("<<request.toString()<<"): lastSC="<<lastUsedSubChannel);

    int spatialLayer=0;
    bool found  = false;
    bool giveUp = false;
    if (!schedulerState->isTx
        && adjacentSubchannelsOnUplink
        && (lastUsedSubChannel!=DSAsubChannelNotFound) // already used subChannel
        )
    { // UL SC-FDMA
        // adjacentSubchannelsOnUplink
        while(!found && !giveUp)
        {
            // try same old subChannel again:
            subChannel = lastUsedSubChannel;
            if (!userInfo.usedSubChannels[subChannel]
                && channelIsUsable(subChannel, timeSlot, request, schedulerState, schedulingMap))
            { // PDU fits in
                found=true; break;
            } else { // mark unusable
                userInfo.usedSubChannels[subChannel] = true;
            }
            // TODO: consider timeSlots !!!

            // try +/-1 +/-2 and so on
            for (int tryThisSubChannelOffset=0; tryThisSubChannelOffset<maxSubChannel/2; tryThisSubChannelOffset++)
            {
                subChannel = (lastUsedSubChannel + tryThisSubChannelOffset*userInfo.toggleOffset);
                if ((subChannel>=0) && !userInfo.usedSubChannels[subChannel]
                    && (subChannel<maxSubChannel)
                    && channelIsUsable(subChannel, timeSlot, request, schedulerState, schedulingMap))
                { // PDU fits in
                    found=true; break;
                } else { // mark unusable
                    userInfo.usedSubChannels[subChannel] = true;
                }
                subChannel = (lastUsedSubChannel - tryThisSubChannelOffset*userInfo.toggleOffset);
                if ((subChannel>=0) && !userInfo.usedSubChannels[subChannel]
                    && (subChannel<maxSubChannel)
                    && channelIsUsable(subChannel, timeSlot, request, schedulerState, schedulingMap))
                { // PDU fits in
                    found=true; break;
                } else { // mark unusable
                    userInfo.usedSubChannels[subChannel] = true;
                }
            } // for offset +/-
            // the resulting DSA may not be contiguous in the case of another small-band user nearby
        } // while
        userInfo.toggleOffset *= -1;
    } else { // free search
        BetterChannelCapacity comparator; // from SchedulerTypes.hpp
        wns::service::phy::phymode::PhyModeMapperInterface* phyModeMapper = colleagues.registry->getPhyModeMapper();
        double channelCapacity = 0.0;
        double remainingTimeOnthisChannel = 0.0;
        wns::Power nominalPower;
        // to get tx power, default or nominal
        // the same power will be used on all subchannels for comparing, so that
        // the best capacity depend only on the free time and datarate
        if (schedulerState->defaultTxPower!=wns::Power())
        { // predefined, e.g. in slave mode
            nominalPower = schedulerState->defaultTxPower;
        } else {
            wns::scheduler::PowerCapabilities powerCapabilities =
                schedulerState->strategy->getPowerCapabilities(request.user);
            nominalPower = powerCapabilities.nominalPerSubband;
        }

        // O(N^2) operations:
        while(!found && !giveUp)
        {
            subChannel = DSAsubChannelNotFound;
            double bestChannelCapacity = 0.0;
            // find channel with best capacity of the remaining subChannels:
            for (int tryThisSubChannel=0; tryThisSubChannel<maxSubChannel; tryThisSubChannel++)
            {
                for (int tryThisTimeSlot=0; tryThisTimeSlot<maxTimeSlots; tryThisTimeSlot++)
                {
                    // TODO: userInfo.usedSubChannels[tryThisSubChannel][tryThisTimeSlot]
                    if (!userInfo.usedSubChannels[tryThisSubChannel]) { // could be free (at least not checked before)
                        ChannelQualityOnOneSubChannel channelQuality
                            = (*channelQualitiesOnAllSubBands)[tryThisSubChannel];
                        if (channelIsUsable(tryThisSubChannel, tryThisTimeSlot, request, schedulerState, schedulingMap))
                        {
                            spatialLayer = getSpatialLayerForSubChannel(tryThisSubChannel, tryThisTimeSlot, request, schedulerState, schedulingMap);
                        }
                        else
                        {
                            continue;
                        }

                        remainingTimeOnthisChannel = schedulingMap->subChannels[tryThisSubChannel].temporalResources[tryThisTimeSlot]->physicalResources[spatialLayer].getFreeTime();
                        wns::Ratio sinr = nominalPower/(channelQuality.interference * channelQuality.pathloss.get_factor());
                        wns::SmartPtr<const wns::service::phy::phymode::PhyModeInterface> bestPhyMode = phyModeMapper->getBestPhyMode(sinr);
                        channelCapacity = bestPhyMode->getDataRate() * remainingTimeOnthisChannel;

                        if (comparator(channelCapacity, bestChannelCapacity)) {
                            bestChannelCapacity = channelCapacity;
                            subChannel = tryThisSubChannel;
                            timeSlot = tryThisTimeSlot;
                        }
                    } // if unused
                } // forall tryThisTimeSlot
            } // forall tryThisSubChannel

            if (subChannel==DSAsubChannelNotFound)
            { // one complete round already done
                giveUp=true; break;
            }
            // best subChannel and spatialLayer found; now check if usable:
            if (channelIsUsable(subChannel, timeSlot, spatialLayer, request, schedulerState, schedulingMap))
            { // PDU fits in
                found=true; break;
            } else { // mark unusable
                // TODO: userInfo.usedSubChannels[tryThisSubChannel][tryThisTimeSlot]
                userInfo.usedSubChannels[subChannel] = true;
            }
        } // while
    } // if free search or linear (SC-FDMA)

    if (giveUp) {
        MESSAGE_SINGLE(NORMAL, logger, "getSubChannelWithDSA(): no free subchannel");
        return dsaResult; // empty with subChannel=DSAsubChannelNotFound
    } else {
        MESSAGE_SINGLE(NORMAL, logger, "getSubChannelWithDSA(): subChannel="<<subChannel);
        userInfo.lastUsedSubChannel = subChannel;
        dsaResult.subChannel = subChannel;
        dsaResult.spatialLayer = spatialLayer;
        return dsaResult;
    }
    return dsaResult; // empty with subChannel=DSAsubChannelNotFound
}