// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: pdb_hash.cpp 814 2011-09-10 12:44:37Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "pdb_hash.h"

#include "system/location.h"
#include "system/process.h"
#include "system/state.h"
#include "system/system.h"
#include "system/variable.h"

#include <boost/foreach.hpp>
#include <cassert>
#include <climits>
#include <fstream>
#define foreach BOOST_FOREACH

using namespace std;

PDBHash::PDBHash(const System* system, const char* filename) : 
    system(system) 
{    
    ifstream in(filename, ifstream::in);	
    parseHeader(in);
    string colon;
    while (in.good()) {	   
	State* state = system->builder->newState(in);
	in >> colon;
	assert(colon == ":");
	int32_t heur;
	in >> heur;
	insert(state, heur);
    }    
    in.close();
}

void PDBHash::parse(istream& in, vector<int32_t>& abstracted) {
    for (;;) {
	char c;
	in >> c;
	if (c == '.') {
	    return;
	}
	in.unget();
	int32_t index;
	in >> index;
	abstracted.push_back(index);
    }
}

void PDBHash::parseHeader(istream& in) {
    parse(in, abstracted_procs);
    parse(in, abstracted_ints);
    parse(in, abstracted_clocks);
}

DiscretePart PDBHash::abstract(const State* state) const {
    DiscretePart res(state->dp);
    foreach (uint32_t ap, abstracted_procs) {
	res.proc(ap) = system->procs[ap]->init->idInProcess;
    }
    foreach (uint32_t av, abstracted_ints) {
	res.var(av) = system->ints[av]->initial;
    }
    return res;
}

int32_t PDBHash::lookup(const State* state) const {
    DiscretePart astate = abstract(state);

    pdb_t::const_iterator pos = pdb.find(astate.hashKey());
    if (pos == pdb.end()) {	
	return INT_MAX;
    }
    int32_t result = -1;
    const list_t& l = pos->second;    

    for (list_t::const_iterator it = l.begin(); it != l.end(); ++it) {
	const state_t& s = *it;
	
	if (astate != s.first->dp) {
	    continue;
	}
	
	switch (s.first->zone().relation(state->zone()))
	{
	case base_SUPERSET:  // state < s.first
	    result = std::max(result, s.second);
	    break;
	case base_EQUAL:     // state == s.first
	    return s.second;
	case base_SUBSET:    // s.first < state
	case base_DIFFERENT: // state & s.first == 0
	    break;
	}
	
	// assert(false);
    }
    if (result == -1) {
	// TODO: can come here when we are dealing with an over-approx????
	assert(false);
	cout << "STANGE" << endl;
	result = INT_MAX;
    }
    
    return result;
}

void PDBHash::insert(const State* state, int32_t heuristic_value) {
    pair<pdb_t::iterator, bool> pos = pdb.insert(make_pair(state->dp.hashKey(), list_t()));	
    list_t& l = pos.first->second;
    l.push_front(make_pair(state, heuristic_value));
}
