/*
 * 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:
 *    Robert Gernhardt
 *    Matthias Jung
 *    Eder F. Zulian
 */

#ifndef ARBITER_H_
#define ARBITER_H_

#include <tlm.h>
#include <systemc.h>
#include <iostream>
#include <vector>
#include <queue>
#include <tlm_utils/multi_passthrough_target_socket.h>
#include <tlm_utils/multi_passthrough_initiator_socket.h>
#include <tlm_utils/peq_with_cb_and_phase.h>
#include "../common/xmlAddressdecoder.h"
#include "../common/dramExtension.h"
#include "../controller/core/TimingCalculation.h"
#include "../controller/core/configuration/ConfigurationLoader.h"

using namespace std;
using namespace tlm;

class Arbiter : public sc_module
{
public:
    tlm_utils::multi_passthrough_initiator_socket<Arbiter> iSocket;
    tlm_utils::multi_passthrough_target_socket<Arbiter> tSocket;

    Arbiter(sc_module_name);
    SC_HAS_PROCESS(Arbiter);

private:
    tlm_utils::peq_with_cb_and_phase<Arbiter> payloadEventQueue;

    vector<bool> channelIsFree;

    // used to account for the request_accept_delay in the dram controllers
    // This is a queue of new transactions. The phase of a new request is BEGIN_REQ.
    vector<queue<tlm_generic_payload *>> pendingRequests;
    // used to account for the response_accept_delay in the initiators (traceplayer, core etc.)
    // This is a queue of responses comming from the memory side. The phase of these transactions is BEGIN_RESP.
    std::map<unsigned int, queue<tlm_generic_payload *>> receivedResponses;

    // Initiated by initiator side
    // This function is called when an arbiter's target socket receives a transaction from a device
    tlm_sync_enum nb_transport_fw(int id, tlm_generic_payload &payload,
                                  tlm_phase &phase, sc_time &fwDelay);

    // Initiated by dram side
    // This function is called when an arbiter's initiator socket receives a transaction from a memory controller
    tlm_sync_enum nb_transport_bw(int channelId, tlm_generic_payload &payload,
                                  tlm_phase &phase, sc_time &bwDelay);

    virtual unsigned int transport_dbg(int /*id*/, tlm::tlm_generic_payload &trans);

    void peqCallback(tlm_generic_payload &payload, const tlm_phase &phase);

    void sendToChannel(unsigned int channelId, tlm_generic_payload &payload,
                       const tlm_phase &phase, const sc_time &delay);

    void sendToInitiator(unsigned int id, tlm_generic_payload &payload,
                         const tlm_phase &phase, const sc_time &delay);

    void appendDramExtension(int socketId, tlm_generic_payload &payload);

    bool addressIsValid(DecodedAddress  &decodedAddress);

    void printDebugMessage(std::string message);
};

#endif /* ARBITER_H_ */
