/*
 * Copyright (c) 2018, 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:
 *    Felipe S. Prado
 *    Matthias Jung
 */

#include "RecordableController.h"

tlm_sync_enum RecordableController::nb_transport_fw(tlm_generic_payload
                                                    &payload, tlm_phase &phase, sc_time &fwDelay)
{
    recordPhase(payload, phase, fwDelay);

    return Controller::nb_transport_fw(payload, phase, fwDelay);
}

void RecordableController::sendToFrontend(tlm_generic_payload &payload,
                                          const tlm_phase &phase, const sc_time &delay)
{
    recordPhase(payload, phase, delay);

    Controller::sendToFrontend(payload, phase, delay);
}

tlm_sync_enum RecordableController::nb_transport_bw(tlm_generic_payload
                                                    &payload, tlm_phase &phase, sc_time &bwDelay)
{
    recordPhase(payload, phase, bwDelay);

    return Controller::nb_transport_bw(payload, phase, bwDelay);
}

void RecordableController::recordPhase(tlm::tlm_generic_payload &trans,
                                       tlm::tlm_phase phase, sc_time delay)
{
    sc_time recTime = delay + sc_time_stamp();

    unsigned int thr = DramExtension::getExtension(trans).getThread().ID();
    unsigned int ch = DramExtension::getExtension(trans).getChannel().ID();
    unsigned int bg = DramExtension::getExtension(trans).getBankGroup().ID();
    unsigned int bank = DramExtension::getExtension(trans).getBank().ID();
    unsigned int row = DramExtension::getExtension(trans).getRow().ID();
    unsigned int col = DramExtension::getExtension(trans).getColumn().ID();

    printDebugMessage("Recording " + phaseNameToString(phase) + " thread " +
                      to_string(thr) + " channel " + to_string(ch) + " bank group " + to_string(
                          bg) + " bank " + to_string(bank) + " row " + to_string(row) + " column " +
                      to_string(col) + " at " + recTime.to_string());

    tlmRecorder->recordPhase(trans, phase, recTime);
}

void RecordableController::schedule(Command command, gp &payload)
{
    Controller::schedule(command, payload);
    if (commandIsIn(command, {Command::Read, Command::ReadA, Command::Write, Command::WriteA})) {
        ScheduledCommand scheduledCommand = controllerCore->state->getLastCommand(
                                                command, DramExtension::getBank(payload));
        TimeInterval dataStrobe = scheduledCommand.getIntervalOnDataStrobe();
        tlmRecorder->updateDataStrobe(dataStrobe.start, dataStrobe.end, payload);
    }
}
