// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: safeabs.cpp 881 2012-01-18 22:46:02Z Martin Wehrle $
//
////////////////////////////////////////////////////////////////////

#include "system/system.h"
#include "system/target.h"
#include "system/task.h"
#include "system/parser.h"
#include "system/process.h"
#include "system/variable.h"
#include "system/location.h"
#include "system/edge.h"
#include "system/guard.h"
#include "system/invariant.h"

#include "common/message.h"

#include "safeabs.h"

#include "causalgraph/dtg.h"
#include "causalgraph/cg_variable.h"
#include "causalgraph/cg_operator.h"
#include "causalgraph/causalGraph.h"

#include <cassert>
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

using namespace std;
using namespace cg;

// returns true iff all clock constraints with c are of the form c > 0
bool harmlessClockConstraints(const Clock* c, const Conjunction<ClockConstraint>& clockconstraints) {
    
    for (vector<ClockConstraint*>::const_iterator it = clockconstraints.begin(); it != clockconstraints.end(); it++) {
	
	ClockConstraint* cc = *it;
	
	if (cc->lhs->id == c->id) {
	    if (cc->comp != Constraint::GT || cc->rhs != 0) {
		return false;
	    }
	}

    }
    
    return true;

}


// identify clocks x that only occur in resets and in guards with
// x > 0 and nowhere else
void get_irrelevant_clocks(const System* system, vector<const Clock*>& irrelevant_clocks) {
    
    std::vector<Clock*> clocks = system->clocks;
    bool irrelevant_clock;
    for (uint32_t i = 0; i < clocks.size(); i++) {
	const Clock* c = clocks[i];
	
	// standard clock not needed in our considerations
	if (c->id == 0)
	    continue;
	
	irrelevant_clock = true;
	
	//check if c only occurs as c > 0 in guards
	for (uint32_t j = 0; j < system->procs.size(); j++) {
	    for (uint32_t k = 0; k < system->procs[j]->edges.size(); k++) {
		Edge* edge = system->procs[j]->edges[k];
		// check edge guard
		irrelevant_clock = irrelevant_clock && harmlessClockConstraints(c, edge->guard->clockconstraints);
		
		// check invariants of src and dst loc of edge
		irrelevant_clock = irrelevant_clock && harmlessClockConstraints(c, edge->src->inv->clockconstraints);
		irrelevant_clock = irrelevant_clock && harmlessClockConstraints(c, edge->dst->inv->clockconstraints);
		
	    }
	}
	
	if (irrelevant_clock) {
	    irrelevant_clocks.push_back(c);
	}
    }
}

// returns complement to irrelevant_clocks
void get_relevant_clocks(const System* system, vector<const Clock*>& relevant_clocks) {
    
    std::vector<Clock*> clocks = system->clocks;
    std::vector<const Clock*> irrelevant_clocks;
    get_irrelevant_clocks(system, irrelevant_clocks);
    
    bool is_relevant;
    for (uint32_t i = 0; i < clocks.size(); i++) {
	
	is_relevant = true;
	const Clock* c = clocks[i];
	// standard clock not needed in our considerations
	if (c->id == 0)
	    continue;
	
	for (uint32_t j = 0; j < irrelevant_clocks.size() && is_relevant; j++) {
	    
	    if (c->id == irrelevant_clocks[j]->id) {
		is_relevant = false;
	    }
	    
	}
	
	if (is_relevant) {
	    relevant_clocks.push_back(c);
	}
	
    }
    
}


void dump_graphviz(const Task* task) {
    const CausalGraph* cg = new CausalGraph(task);
    cg->graphviz(cout);
}

void dump_variables(Task* task) {
    SafeAbstractor* safeabs = new SafeAbstractor(task);    
    vector<CGVariable*> safevars; 
    safeabs->get_safe_variables(safevars);
    
    varset_t target_vars = task->target->readVars();
    set<const Process*> target_procs;
    foreach (const Variable* v, target_vars) {
	const Location* loc = dynamic_cast<const Location*>(v);
	if (loc) {
	    target_procs.insert(loc->proc);
	}
    }

    cout << "process variables: ";
    foreach(const Process* p, task->system->procs) {
	if (target_procs.find(p) == target_procs.end()) {
	    cout << p->name << " ";
	}
    }
    cout << endl << endl << "global integer variables: ";
    foreach(const Integer* i, task->system->ints) {
	if (!target_vars.contains(i)) {//find(i) == target_vars.end()) {
	    cout << i->name << " ";
	}
    }
    cout << endl << endl << "local integer variables: ";
    foreach(const Process* p, task->system->procs) {
	foreach(const Integer* i, p->ints) {
	    if (!target_vars.contains(i)) {
		cout << p->name << "." << i->name << " ";
	    }
	}
    }
    
    cout << endl << endl << "safe variables: ";
    foreach(const CGVariable* v, safevars) {
	cout << v->name << " ";    
    }
    cout << endl;
}

void display_weights(const Task* task) {
    const CausalGraph* cg = new CausalGraph(task);
    cout << *cg << endl;    
}

int main(int argc, char *argv[]) {
    bool oldSyntax = true;
    bool show_weights = false;
    bool graphviz = false;
    bool irrelevant_clocks = false;
    bool relevant_clocks = false;

    int c;
    debug().setSilent(true);
    while ((c = getopt(argc, argv, "ndwgcr")) != -1) {
	switch (c) {
	case 'n':
	    oldSyntax = false;
	    break;
	case 'd':
	    debug().setSilent(false);
	    break;
	case 'w':
	    show_weights = true;
	    break;
	case 'g':
	    graphviz = true;
	    break;
	case 'c':
	    // displays all clocks x that only occur as x > 0 in
	    // guards
	    irrelevant_clocks = true;
	    break;
	case 'r':
	    // displays all clocks *except* for the irrelevant clocks
	    // (ie, displays the complement)
	    relevant_clocks = true;
	    break;
	default:
	    cout << "error: unknown option" << endl;
	    break;
	}
    }
    if (argc - optind < 1) {
	error() << "not enough arguments" << endl;
    }    
    
    Task* task = SystemParser::parser()(argv[optind], oldSyntax);
    if (graphviz) {
	dump_graphviz(task);
    } else if (show_weights) {
	display_weights(task);
    } else if (irrelevant_clocks) {
	vector<const Clock*> irrelevant_clocks;
	get_irrelevant_clocks(task->system, irrelevant_clocks);
	cout << "irrelevant clocks:";
	for (uint32_t i = 0; i < irrelevant_clocks.size(); i++) {
	    cout << " " << irrelevant_clocks[i]->name;
	}
	cout << endl;
    } else if (relevant_clocks) {
	vector<const Clock*> relevant_clocks;
	get_relevant_clocks(task->system, relevant_clocks);
        cout << "relevant clocks:";
	for (uint32_t i = 0; i < relevant_clocks.size(); i++) {
	    cout << " " << relevant_clocks[i]->name;
	}
	cout << endl;
    }
    
    else {
	dump_variables(task);
    }
}

SafeAbstractor::SafeAbstractor(const Task* task) :
    task(task)
{
    initCGVariables(task->system);
    initCGOperators(task->system);
}

void SafeAbstractor::initCGVariables(const System* system) {

    initGlobalVariables(system);
    initLocalVariables(system);
  
}

void SafeAbstractor::initGlobalVariables(const System* system) {
    int id = 0;
    
    // global integers
    std::vector<Integer*> integers = system->ints;
    for (uint32_t i = 0; i < integers.size(); i++) {
	CGVariable* var = new CGVariable(integers[i]->name, integers[i]->lower, integers[i]->upper, GLOBAL_INTEGER);
	var->id = id;
	var->system_id = integers[i]->id;
	
	local_to_global_id(GLOBAL_INTEGER, var->system_id) = id;
	id++;
	variables.push_back(var);
	assert((int)variables.size() == var->id + 1);
    }
    
    // global clocks
    std::vector<Clock*> clocks = system->clocks;
    for (uint32_t i = 0; i < clocks.size(); i++) {
	// upper and lower value not needed for clocks
	CGVariable* var = new CGVariable(clocks[i]->name, 0, -1, GLOBAL_CLOCK);
	var->id = id;
	var->system_id = clocks[i]->id;
	local_to_global_id(GLOBAL_CLOCK, var->system_id) = id;
	id++;
	variables.push_back(var);
	assert((int)variables.size() == var->id + 1);
    }
    
    // processes
    for (uint32_t i = 0; i < system->procs.size(); i++) {
	Process* proc = system->procs[i];
	string name = proc->name;
	int upper = proc->locs.size() - 1;
	if (upper > 0) {
	    CGVariable* var = new CGVariable(name, 0, upper, PROCESS);
	    var->id = id;
	    var->system_id = proc->id;
	    local_to_global_id(PROCESS, var->system_id) = id;
	    id++;
	    variables.push_back(var);
	    assert((int)variables.size() == var->id + 1);
	} else {
	    // processes with only one location are treated as constants
	    local_to_global_id(PROCESS, proc->id) = -1;
	}
    }
}

void SafeAbstractor::initLocalVariables(const System* system) {
    int id = variables.size();

    // integers
    for (uint32_t k = 0; k < system->procs.size(); k++) {
	std::vector<Integer*> integers = system->procs[k]->ints;
	for (uint32_t i = 0; i < integers.size(); i++) {
	    string varname = system->procs[k]->name + "." + integers[i]->name;
	    CGVariable* var = new CGVariable(varname, integers[i]->lower, integers[i]->upper, LOCAL_INTEGER);
	    var->id = id;
	    var->system_id = integers[i]->id;
	    local_to_global_id(LOCAL_INTEGER, var->system_id) = id;
	    id++;
	    variables.push_back(var);
	    assert((int)variables.size() == var->id + 1);
	}
    }

    // clocks
    for (uint32_t k = 0; k < system->procs.size(); k++) {
	std::vector<Clock*> clocks = system->procs[k]->clocks;
	for (uint32_t i = 0; i < clocks.size(); i++) {
	    // upper and lower value not needed for clocks
	    string varname = system->procs[k]->name + "." + clocks[i]->name;
	    CGVariable* var = new CGVariable(varname, 0, -1, LOCAL_CLOCK);
	    var->id = id;
	    var->system_id = clocks[i]->id;
	    local_to_global_id(LOCAL_CLOCK, var->system_id) = id;
	    id++;
	    variables.push_back(var);
	    assert((int)variables.size() == var->id + 1);
	}
    }
    
    // TODO: what about channels ??
    
}

void SafeAbstractor::initCGOperators(const System* system) {
    vector<Edge*> bangEdges;
    vector<Edge*> queueEdges;
    bangEdges.clear();
    queueEdges.clear();
    for (uint32_t i = 0; i < system->procs.size(); i++) {
	Process* proc = system->procs[i];
	for (uint32_t j = 0; j < proc->edges.size(); j++) {
	    Edge* edge = proc->edges[j];
	    
	    switch(edge->getType()) {
	    case Edge::TAU:
		operators.push_back(new CGOperator(edge, NULL, local_to_global_id));
		break;
	    case Edge::BANG:
		bangEdges.push_back(edge);
		break;
	    case Edge::QUE:
		queueEdges.push_back(edge);
		break;
	    default:
		assert(false);
		break;
	    }
	}
    }

    // process bang and queue edges and construct corresponding sync operator
    for (uint32_t i = 0; i < bangEdges.size(); i++) {
	Edge* bangEdge = bangEdges[i];
	
	for (uint32_t j = 0; j < queueEdges.size(); j++) {
	    Edge* queueEdge = queueEdges[j];
	    
	    if (bangEdge->getAction()->id == queueEdge->getAction()->id && 
		bangEdge->getProcess()->id != queueEdge->getProcess()->id) {

		
		CGOperator* sync_op = new CGOperator(bangEdge, queueEdge, local_to_global_id);
		operators.push_back(sync_op);
	    }
	}
    }
}

void SafeAbstractor::get_safe_variables(vector<CGVariable*>& safevars) {    
    CausalGraph* cg = new CausalGraph(task);
    cg->compute_safe_variables(safevars);
}

