/*
 * 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
 *    Matthias Jung
 *    Felipe S. Prado
 */

#include "ControllerState.h"
#include <algorithm>
#include "core/TimingCalculation.h"

using namespace std;

const ScheduledCommand ControllerState::getLastCommand(Command command,
                                                       Bank bank) //TODO const reference? and make const
{
    return lastScheduledByCommandAndBank[command][bank];
}

const ScheduledCommand ControllerState::getLastCommand(Command command)
{
    ScheduledCommand max;

    for (unsigned int i = 0; i < config->memSpec.NumberOfBanks; ++i) {
        ScheduledCommand current = getLastCommand(command, Bank(i));
        if (current.getStart() > max.getStart())
            max = current;
    }

    return max;
}

const ScheduledCommand ControllerState::getLastScheduledCommand()
{
    ScheduledCommand lastCommand;

    for (Command cmd : getAllCommands()) {
        for (Bank bank : Configuration::getInstance().memSpec.getBanks()) {
            ScheduledCommand &current = lastScheduledByCommandAndBank[cmd][bank];
            if (current.getStart() > lastCommand.getStart())
                lastCommand = current;
        }
    }

    printDebugMessage("Last scheduled command was " + commandToString(
                          lastCommand.getCommand()));

    return lastCommand;
}

const ScheduledCommand ControllerState::getLastScheduledCommand(Bank bank)
{
    ScheduledCommand lastCommand;

    for (Command cmd : getAllCommands()) {
        ScheduledCommand &current = lastScheduledByCommandAndBank[cmd][bank];
        if (current.getStart() > lastCommand.getStart())
            lastCommand = current;
    }

    printDebugMessage("Last scheduled command on bank " + to_string(
                          bank.ID()) + " was " + commandToString(lastCommand.getCommand()));

    return lastCommand;
}

void ControllerState::change(const ScheduledCommand &scheduledCommand)
{
    bus.blockSlot(scheduledCommand.getStart());

    printDebugMessage("Changing state on bank " +  to_string(
                          scheduledCommand.getBank().ID()) + " command is " + commandToString(
                          scheduledCommand.getCommand()));
    lastScheduledByCommandAndBank[scheduledCommand.getCommand()][scheduledCommand.getBank()]
        = scheduledCommand;

    switch (scheduledCommand.getCommand()) {
    case Command::Read:
        lastDataStrobeCommands.emplace_back(scheduledCommand);
        break;
    case Command::ReadA:
        rowBufferStates->closeRowBuffer(scheduledCommand.getBank());
        lastDataStrobeCommands.emplace_back(scheduledCommand);
        break;
    case Command::Write:
        lastDataStrobeCommands.emplace_back(scheduledCommand);
        break;
    case Command::WriteA:
        rowBufferStates->closeRowBuffer(scheduledCommand.getBank());
        lastDataStrobeCommands.emplace_back(scheduledCommand);
        break;
    case Command::AutoRefresh:
        break;
    case Command::ActB:
        rowBufferStates->openRowInRowBuffer(scheduledCommand.getBank(),
                                            scheduledCommand.getRow());
        lastActivatesB.emplace(scheduledCommand.getStart(), scheduledCommand);
        break;
    case Command::Activate:
        rowBufferStates->openRowInRowBuffer(scheduledCommand.getBank(),
                                            scheduledCommand.getRow());
        lastActivates.emplace(scheduledCommand.getStart(), scheduledCommand);
        break;
    case Command::PreB:
        rowBufferStates->closeRowBuffer(scheduledCommand.getBank());
        break;
    case Command::Precharge:
        rowBufferStates->closeRowBuffer(scheduledCommand.getBank());
        break;
    case Command::PrechargeAll:
        rowBufferStates->closeAllRowBuffers();
        break;
    case Command::SREF:
        rowBufferStates->closeRowBuffer(scheduledCommand.getBank());
        break;
    default:
        break;
    }
}

void ControllerState::cleanUp(sc_time time)
{
    bus.cleanUpSlots(time);
    vector<ScheduledCommand> tmp;
    for (ScheduledCommand &command : lastDataStrobeCommands) {
        if (command.getEnd() >= time
                || getDistance(command.getEnd(), time) <= config->memSpec.tDataStrobeHistory())
            tmp.push_back(command);
    }
    lastDataStrobeCommands = tmp;
    if (time >= config->memSpec.tActHistory())
        lastActivates.erase(lastActivates.begin(),
                            lastActivates.lower_bound(time - config->memSpec.tActHistory()));

    if (time >= config->memSpec.tActBHistory())
        lastActivatesB.erase(lastActivatesB.begin(),
                             lastActivatesB.lower_bound(time - config->memSpec.tActBHistory()));
}

void ControllerState::printDebugMessage(std::string message)
{
    DebugManager::getInstance().printDebugMessage(ownerName, message);
}

