/*
 * Copyright (c) 2010 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * 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;
 * neither the name of the copyright holders 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
 * OWNER 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: Korey Sewell
 *
 */

#include <cstdio>
#include <list>
#include <vector>

#include "cpu/inorder/pipeline_traits.hh"
#include "cpu/inorder/resource_sked.hh"
#include "debug/SkedCache.hh"

using namespace std;
using namespace ThePipeline;

ResourceSked::ResourceSked()
{
    stages.resize(NumStages);
}

void
ResourceSked::init()
{
    assert(!stages[0].empty());

    curSkedEntry = stages[0].begin();
}

int
ResourceSked::size()
{
    int total = 0;
    for (int i = 0; i < stages.size(); i++) {
        total += stages[i].size();
    }

    return total;
}

bool
ResourceSked::empty()
{
    return size() == 0;
}


ResourceSked::SkedIt
ResourceSked::begin()
{
    int num_stages = stages.size();
    for (int i = 0; i < num_stages; i++) {
        if (stages[i].size() > 0)
            return stages[i].begin();
    }

    return stages[num_stages - 1].end();
}

ResourceSked::SkedIt
ResourceSked::end()
{
    int num_stages = stages.size();
    return stages[num_stages - 1].end();
}

ResourceSked::SkedIt
ResourceSked::end(int stage_num)
{
    return stages[stage_num].end();
}

ResourceSked::SkedIt
ResourceSked::find(int stage_num, int cmd)
{
    SkedIt stage_it = stages[stage_num].begin();
    SkedIt stage_end = stages[stage_num].end();

    while (stage_it != stage_end) {
        if ((*stage_it)->cmd == cmd)
            return stage_it;
        stage_it++;
    }

    return stages[stage_num].end();
}

ScheduleEntry*
ResourceSked::top()
{
    assert(size() > 0);

    return *curSkedEntry;
}

void
ResourceSked::pop()
{
    int stage_num = (*curSkedEntry)->stageNum;

    stages[stage_num].erase(curSkedEntry);

    if (!stages[stage_num].empty()) {
        curSkedEntry = stages[stage_num].begin();
    } else {
        int next_stage = stage_num + 1;

        while (next_stage < NumStages) {
            if (stages[next_stage].empty()) {
                next_stage++;
            } else {
                curSkedEntry = stages[next_stage].begin();
                break;
            }
        }
    }
}

void
ResourceSked::push(ScheduleEntry* sked_entry)
{
    int stage_num = sked_entry->stageNum;
    assert(stage_num < NumStages);

    SkedIt pri_iter = findIterByPriority(sked_entry, stage_num);

    stages[stage_num].insert(pri_iter, sked_entry);
}

void
ResourceSked::pushBefore(ScheduleEntry* sked_entry, int sked_cmd,
                         int sked_cmd_idx)
{

    int stage_num = sked_entry->stageNum;
    assert(stage_num < NumStages);

    SkedIt pri_iter = findIterByCommand(sked_entry, stage_num,
                                        sked_cmd, sked_cmd_idx);

    assert(pri_iter != stages[stage_num].end() &&
           "Could not find command to insert in front of.");

    stages[stage_num].insert(pri_iter, sked_entry);
}

ResourceSked::SkedIt
ResourceSked::findIterByPriority(ScheduleEntry* sked_entry, int stage_num)
{
    if (stages[stage_num].empty()) {
        return stages[stage_num].end();
    }

    int priority = sked_entry->priority;

    SkedIt sked_it = stages[stage_num].begin();
    SkedIt sked_end = stages[stage_num].end();

    while (sked_it != sked_end) {
        if ((*sked_it)->priority > priority)
            break;

        sked_it++;
    }

    return sked_it;
}

ResourceSked::SkedIt
ResourceSked::findIterByCommand(ScheduleEntry* sked_entry, int stage_num,
                                int sked_cmd, int sked_cmd_idx)
{
    if (stages[stage_num].empty()) {
        return stages[stage_num].end();
    }

    SkedIt sked_it = stages[stage_num].begin();
    SkedIt sked_end = stages[stage_num].end();

    while (sked_it != sked_end) {
        if ((*sked_it)->cmd == sked_cmd &&
            (sked_cmd_idx != -1) ? (*sked_it)->idx == sked_cmd_idx : true)
            break;

        sked_it++;
    }

    return sked_it;
}

void
ResourceSked::print()
{
    for (int i = 0; i < stages.size(); i++) {
        //ccprintf(cerr, "Stage %i\n====\n", i);
        SkedIt sked_it = stages[i].begin();
        SkedIt sked_end = stages[i].end();
        while (sked_it != sked_end) {
            DPRINTF(SkedCache, "\t stage:%i res:%i cmd:%i idx:%i\n",
                    (*sked_it)->stageNum,
                    (*sked_it)->resNum,
                    (*sked_it)->cmd,
                    (*sked_it)->idx);
            sked_it++;
        }
    }
}