/*
 * Copyright (c) 2015, University of Kaiserslautern
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors:
 *    Janik Schlemminger
 *    Robert Gernhardt
 *    Matthias Jung
 */

#include "Fr_Fcfs.h"
#include "../../common/dramExtension.h"
#include "../core/configuration/Configuration.h"
#include <algorithm>

using namespace std;

// The FR_FCFS is descibed in a 2000 paper from Rixner et al.:
// Memory Access Scheduling
//
// The FR_FCFS scheduler features for each bank in the DRAM a specific
// scheduling buffer for example:
//
// Bank0: OOOOOOOO
// Bank1: OOXXXXXX
// ...      ^    ^
// ...      |    |
// ...   back    |
// ...       front
// ...
// Bank6: OOOOO0XX
// Bank7: XXXXXXXX

void FR_FCFS::storeRequest(gp *payload)
{
    // FIXME: Question: what if the buffer is full? IMHO the schedule function
    // should provide a true or false when the placement into the buffer worked
    // out or not (?).
    buffer[DramExtension::getExtension(payload).getBank()]
    .emplace_back(payload);
}

std::pair<Command, gp *> FR_FCFS::getNextRequest(Bank bank)
{
    // If the bank is empty like Bank0 in the example we do nothing
    if (buffer[bank].empty()) {
        return pair<Command, tlm::tlm_generic_payload *>(Command::NOP, NULL);
    }

    // In FR_FCFS row hits have always the highest priority, therefore we search
    // for row hits. If we find a row hit, we remove the transaction from the
    // queue and send it to the DRAM.
    deque<gp *>::iterator it = FindRowHit(bank);
    if (it != buffer[bank].end()) {
        gp *payload = *it;
        buffer[bank].erase(it);
        return pair<Command, gp *>(getReadWriteCommand(*payload), payload);
    }

    // If there is no row hit, the FR_FCFS takes always the oldest transaction
    // in the buffer, i.e. the transaction in the front.
    return pair<Command, gp *>(getNextCommand(buffer[bank].front()),
                               buffer[bank].front());
}

// This function searches for a row hit in the scheduling queue of the specific
// bank. If no row hit is found the end of the queue is returned.
//
// Note: end() Returns an iterator referring to the past-the-end element in the
// deque container. The past-the-end element is the theoretical element that
// would follow the last element in the deque container. It does not point to
// any element, and thus shall not be dereferenced.
deque<gp *>::iterator FR_FCFS::FindRowHit(Bank bank)
{
    deque<gp *> &queue = buffer[bank];

    if (!controllerCore.getRowBufferStates().rowBufferIsOpen(bank))
        return queue.end();

    // Traverse the scheduling queue of the specific bank:
    for (auto it = queue.begin(); it != queue.end(); it++) {
        gp *payload = *it;
        //Found row-hit and return the according iterator
        if (DramExtension::getRow(payload)
                == controllerCore.getRowBufferStates().getRowInRowBuffer(bank)) {
            return it;
        }
    }

    return queue.end();
}

gp *FR_FCFS::getPendingRequest(Bank /*bank*/)
{
    return NULL;
}
