// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: open_list.cpp 815 2011-09-10 12:57:27Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "open_list.h"

#include "system/state.h"

#include <climits>
#include <iomanip>
#include <iostream>

using namespace std;

std::ostream& OpenList::printstat(std::ostream& o) const {
    const uint32_t width = 8;
    return o << setw(width) << pushed_states << ", " 
	     << setw(width) << popped_states << ", " 	     
	     << setw(width) << current_states;
}

////////////////////////////////////////////////////////////////////

StackList::StackList() : 
    states() 
{}

void StackList::insert(State* state, int32_t, int32_t) {
    current_states++;
    pushed_states++;

    states.push(state);
}

State* StackList::get() {
    assert(!states.empty());
    popped_states++;
    current_states--;

    State* next = states.top();
    states.pop();
    return next;
}

////////////////////////////////////////////////////////////////////

QueueList::QueueList() : 
    states() 
{}

void QueueList::insert(State* state, int32_t, int32_t) {
    current_states++;
    pushed_states++;
    states.push(state);
}

State* QueueList::get() {
    assert(!states.empty());
    current_states--;
    popped_states++;
    
    State* next = states.front();
    states.pop();

    return next;
}

////////////////////////////////////////////////////////////////////

PriorityQueue::PriorityQueue() : 
    buckets(),
    lowest_bucket(0)
{}

void PriorityQueue::insert(State* state, int32_t, int32_t key) {    
    assert(key >= 0);
    if (key == INT_MAX) {
	// NOTE: do not delete the state, since a pointer to this
	// state can still be in use (e.g. DiscretePartCache)
	return;
    }
    if (key >= int32_t(buckets.size())) {
	buckets.resize(key + 1);
    }
    else if (key < lowest_bucket) {
	lowest_bucket = key;
    }
    current_states++;
    pushed_states++;
    buckets[key].push_back(state);
}

State* PriorityQueue::get() {
    assert(!empty());
    while (buckets[lowest_bucket].empty()) {
	lowest_bucket++;
    }
    current_states--;
    popped_states++;
    // State* result = buckets[lowest_bucket].front();
    // buckets[lowest_bucket].pop_front();

    State* result = buckets[lowest_bucket].back();
    buckets[lowest_bucket].pop_back();

    return result;
}

////////////////////////////////////////////////////////////////////

MultiQueue::MultiQueue(const OpenList* prototype) : 
    prototype(prototype),
    queues(1, prototype->clone())
{}

void MultiQueue::insert(State* state, int32_t key1, int32_t key2) {
    assert(key1 >= 0);
    assert(key2 >= 0);
    if (key1 == INT_MAX || key2 == INT_MAX) {
	return;
    }
    while (int32_t(queues.size()) <= key1) {
	queues.push_back(prototype->clone());
    }    
    queues[key1]->insert(state, key1, key2);
    current_states++;
    pushed_states++;
}

State* MultiQueue::get() {
    for (uint32_t i = 0; i < queues.size(); i++) {
	if (!queues[i]->empty()) {
	    current_states--;
	    popped_states++;
	    return queues[i]->get();
	}
    }    
    assert(false);
    return NULL;    
}

std::ostream& MultiQueue::printstat(std::ostream& o) const {
    for (uint32_t i = 0; i < queues.size(); i++) {
	queues[i]->printstat(o << "queue " << i << ": ") << endl;
    }
    return o;
}

////////////////////////////////////////////////////////////////////

void UTIPQueue::insert(State* state, int32_t key1, int32_t key2) {
    if (key1 == INT_MAX || key2 == INT_MAX) {
	return;
    }
    current_states++;
    pushed_states++;
    switch (key1) {
    case -1: useless->insert(state, 0, key2); break;
    case  0: normal->insert(state, 0, key2); break;
    default: ip->insert(state, key1, key2); break;
    }
}

State* UTIPQueue::get() {
    current_states--;
    popped_states++;
    
    if (!normal->empty()) {
	return normal->get();
    } else if (!useless->empty()) {
	return useless->get();
    } else {
	return ip->get();
    }
}

std::ostream& UTIPQueue::printstat(std::ostream& o) const {
    return o << "normal  " << *normal << endl
	     << "useless " << *useless << endl
	     << "ip      " << endl 
	     << *ip << endl;
}

////////////////////////////////////////////////////////////////////

void UTQueue::insert(State* state, int32_t key1, int32_t key2) {
    if (key1 == INT_MAX || key2 == INT_MAX) {
	return;
    }
    current_states++;
    pushed_states++;
    switch (key1) {
    case -1: useless->insert(state, 0, key2); break;
    default: normal->insert(state, 0, key2); break;
    }
}

State* UTQueue::get() {
    current_states--;
    popped_states++;
    
    if (!normal->empty()) {
	return normal->get();
    } else {
	return useless->get();
    }    
}

std::ostream& UTQueue::printstat(std::ostream& o) const {
    return o << "normal  " << *normal << endl
	     << "useless " << *useless << endl;
}
