/*
 * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <algorithm>
#include <assert.h>
#include "adviseFast.h"

using std::set;

AdviseFast::RuleSet::RuleSet(FactSet *f) :
	facts(f)
{
	FactSet::iterator i;
	for(i=facts->begin(); i!=facts->end(); ++i)
		addGeneral(i->first);
}

AdviseFast::RuleSet::~RuleSet(){
}

void AdviseFast::RuleSet::addRule(Entry const &entry)
{
	_rules.insert(entry);
}

bool AdviseFast::RuleSet::getSurePoint(Coord *sp)
{
	if(_surePoints.empty()){
		if(!apply()) return false;
	}

	CoordSet::iterator i = _surePoints.begin();
	*sp = *i;
	_surePoints.erase(i);

	return true;
}

bool AdviseFast::RuleSet::reveal(Coord what)
{
	CoordSet affectedFacts;
	if(!facts->reveal(what, &affectedFacts))
		// OOPS :(
		return false;

	CoordSet::iterator i;
	for(	i = affectedFacts.begin();
		i != affectedFacts.end();
		++i)
		this->addGeneral(*i);

	return true;
}

void AdviseFast::RuleSet::solve()
{
	Coord p;
	while(getSurePoint(&p)) {
          bool res = reveal(p);
          assert(res);
          Q_UNUSED(res);
        }
}

bool AdviseFast::RuleSet::apply()
{
	while(!_rules.empty()){
		set<Entry>::iterator i = _rules.begin();
		std::auto_ptr<Rule>  r (this->newRule(*i));
		_rules.erase(i);

		if(r->apply(&this->_surePoints)) return true;
	}

	return false;
}

AdviseFast::Rule *
AdviseFast::RuleSet::newRule(Entry const &e){
	CoordSet::const_iterator i = e.second.begin();
	Coord p, p1;
	switch(e.first){
		case EMPTY:
			assert(e.second.size() == 1);
			return new EmptyRule(*i, this);

		case FULL:
			assert(e.second.size() == 1);
			return new FullRule(*i, this);

		case INCLUDE:
			assert(e.second.size() == 2);
			p = *i; ++i; p1 = *i;
			return new InclusionRule(p, p1, this);

		case INCLUDE1:
			assert(e.second.size() == 2);
			p = *i; ++i; p1 = *i;
			return new InclusionRule(p1, p, this);

		case INTERSECT:
			assert(e.second.size() == 2);
			p = *i; ++i; p1 = *i;
			return new IntersectionRule(p, p1, this);

		case INTERSECT1:
			assert(e.second.size() == 2);
			p = *i; ++i; p1 = *i;
			return new IntersectionRule(p1, p, this);

		case GENERAL:
			assert(e.second.size() == 1);
			return new GeneralRule(*i, this);

		default:
			assert(false);
	}

	// Make compiler happy
	return 0;
}

void AdviseFast::RuleSet::removeRef(Coord p){
	set<Entry>::iterator i, j;

	for(	i = j = _rules.begin();
		i != _rules.end();
		i = j)
	{
		++j;
		if(i->second.count(p)) _rules.erase(i);
	}
}

void AdviseFast::RuleSet::addGeneral(Coord p){
	this->removeRef(p);
	Entry e;
	e.first = GENERAL;
	e.second.insert(p);
	this->addRule(e);
}

#if defined(DEBUG) && DEBUG >= 2
int AdviseFast::Rule::leaks = 0;
#endif

AdviseFast::Rule::Rule(RuleSet *parent) :
	_parent(parent),
	_facts(parent->facts)
{
#if defined(DEBUG) && DEBUG >= 2
	cout << "Rule::Rule, leaks = " << ++leaks << endl;
#endif
}

AdviseFast::Rule::~Rule()
{
#if defined(DEBUG) && DEBUG >= 2
	cout << "Rule::~Rule, leaks = " << --leaks << endl;
#endif
}

AdviseFast::GeneralRule::GeneralRule(
	Coord fact,
	RuleSet *parent) :
	Rule(parent),
	_fact(fact)
{}

bool AdviseFast::GeneralRule::apply(CoordSet *)
{

#if defined(DEBUG) && DEBUG >= 2
	operator <<(
			cout << "Applying general rule ",
			_fact) << endl;
#endif

	// Return if there's no more such fact
	if(!_facts->count(_fact)) return false;
	Fact const &f = (*_facts)[_fact];

#if defined(DEBUG) && DEBUG >= 2
	cout << f << endl;
#endif

	// Insert intersection rules first
	// relatedFacts -- facts which have non-zero intersection
	CoordSet relatedFacts;
	{
		CoordSet::const_iterator i;
		for(	i=f.pointSet.begin();
			i!=f.pointSet.end();
			++i){

			CoordSet const & ps =
				*_facts->getContainingFacts(*i);
			relatedFacts.insert(
				ps.begin(), ps.end());
		}
	}
	relatedFacts.erase(_fact); // ;)

	CoordSet::iterator i;
	for(	i=relatedFacts.begin();
		i!=relatedFacts.end();
		++i)
	{
		RuleSet::Entry e;
		e.second.insert(_fact);
		e.second.insert(*i);

		e.first = RuleSet::INTERSECT1; _parent->addRule(e);
		e.first = RuleSet::INTERSECT; _parent->addRule(e);
		e.first = RuleSet::INCLUDE1; _parent->addRule(e);
		e.first = RuleSet::INCLUDE; _parent->addRule(e);
	}

	// Now simple rules, so that they appear first in the list
	RuleSet::Entry e; e.second.insert(_fact);
	e.first = RuleSet::FULL; _parent->addRule(e);
	e.first = RuleSet::EMPTY; _parent->addRule(e);

	// No point revealed, so...
	return false;
}

AdviseFast::EmptyRule::EmptyRule(
	Coord fact,
	RuleSet *parent) :
	Rule(parent),
	_fact(fact)
{}

bool AdviseFast::EmptyRule::apply(
	CoordSet *surePoints)
{

#if defined(DEBUG) && DEBUG >= 2
	operator <<(
			cout << "Applying empty rule ",
			_fact) << endl;
#endif

	if(!_facts->count(_fact)) return false;
	Fact const &f = (*_facts)[_fact];

#if defined(DEBUG) && DEBUG >= 2
	cout << f << endl;
#endif

	// FactSet does not contain empty facts!!
	assert(!f.pointSet.empty());

	// If there are mines around, alas :(
	if(f.mines) return false;

#if defined(DEBUG) && DEBUG >= 2
	cout << "succeeded!" << endl;
#endif

	surePoints->insert(
		f.pointSet.begin(),
		f.pointSet.end());

	_parent->removeRef(_fact);

	return true;
}

AdviseFast::FullRule::FullRule(
	Coord fact,
	RuleSet *parent) :
	Rule(parent),
	_fact(fact)
{}

bool AdviseFast::FullRule::apply(
	CoordSet */*surePoints*/)
{

#if defined(DEBUG) && DEBUG >= 2
	operator <<(
			cout << "Applying full rule ",
			_fact) << endl;
#endif

	if(!_facts->count(_fact)) return false;
	Fact f = (*_facts)[_fact];

#if defined(DEBUG) && DEBUG >= 2
	cout << f << endl;
#endif

	// FactSet does not contain empty facts!!
	assert(!f.pointSet.empty());

	// The point set is not full of mines... :(
	if(f.mines != (int)f.pointSet.size()) return false;

#if defined(DEBUG) && DEBUG >= 2
	cout << "succeeded!" << endl;
#endif

	CoordSet affectedFacts;
	CoordSet::iterator i;
	for(	i=f.pointSet.begin();
		i!=f.pointSet.end();
		++i)
		_facts->mark(*i, &affectedFacts);

	for(	i=affectedFacts.begin();
		i!=affectedFacts.end();
		++i)
		_parent->addGeneral(*i);
	_parent->removeRef(_fact);

	// No mines revealed
	return false;
}

AdviseFast::InclusionRule::InclusionRule(
	Coord bigger, Coord smaller,
	RuleSet *parent) :
	Rule(parent),
	_bigger(bigger), _smaller(smaller)
{}

bool AdviseFast::InclusionRule::apply(
	CoordSet */*surePoints*/)
{

#if defined(DEBUG) && DEBUG >= 2
	cout << "Applying inclusion rule ";
	operator <<(cout, _bigger) << ' ';
	operator <<(cout, _smaller) << endl;
#endif

	if(!_facts->count(_bigger)) return false;
	if(!_facts->count(_smaller)) return false;

	Fact b = (*_facts)[_bigger];
	Fact s = (*_facts)[_smaller];

#if defined(DEBUG) && DEBUG >= 2
	cout << b << endl << s << endl;
#endif

	assert(!s.pointSet.empty());

	CoordSet diff;
	set_difference(
		s.pointSet.begin(),
		s.pointSet.end(),
		b.pointSet.begin(),
		b.pointSet.end(),
		inserter(diff, diff.begin()));
	if(!diff.empty())
		// That is s is not included in b
		return false;

#if defined(DEBUG) && DEBUG >= 2
	cout << "succeeded!" << endl;
#endif

	diff.clear();
	set_difference(
		b.pointSet.begin(),
		b.pointSet.end(),
		s.pointSet.begin(),
		s.pointSet.end(),
		inserter(diff, diff.begin()));

	if(diff.empty()){
		_facts->deleteFact(_bigger);
		_parent->removeRef(_bigger);
	} else {
		b.pointSet = diff;
		b.mines -= s.mines;
		_facts->addFact(_bigger, b);
		_parent->addGeneral(_bigger);
	}

	// No points revealed
	return false;
}

AdviseFast::IntersectionRule::IntersectionRule(
	Coord bigger, Coord smaller,
	RuleSet *parent) :
	Rule(parent),
	_bigger(bigger), _smaller(smaller)
{}

bool AdviseFast::IntersectionRule::apply(
	CoordSet *surePoints)
{

#if defined(DEBUG) && DEBUG >= 2
	cout << "Applying intersection rule ";
	operator <<(cout, _bigger) << ' ';
	operator <<(cout, _smaller) << endl;
#endif

	if(!_facts->count(_bigger)) return false;
	if(!_facts->count(_smaller)) return false;

	Fact b = (*_facts)[_bigger];
	Fact s = (*_facts)[_smaller];

#if defined(DEBUG) && DEBUG >= 2
	cout << b << endl << s << endl;
#endif

	CoordSet diff;
	set_difference(
		b.pointSet.begin(),
		b.pointSet.end(),
		s.pointSet.begin(),
		s.pointSet.end(),
		inserter(diff, diff.begin()));

	if((int)diff.size() != b.mines - s.mines)
		// Oops :(
		return false;

#if defined(DEBUG) && DEBUG >= 2
	cout << "succeeded!" << endl;
#endif

	CoordSet cross, diffs;
	set_difference(
		s.pointSet.begin(),
		s.pointSet.end(),
		b.pointSet.begin(),
		b.pointSet.end(),
		inserter(diffs, diffs.begin()));
	set_intersection(
		s.pointSet.begin(),
		s.pointSet.end(),
		b.pointSet.begin(),
		b.pointSet.end(),
		inserter(cross, cross.begin()));

	b.pointSet = diff;
	b.mines -= s.mines;
	_facts->addFact(_bigger, b);

	s.pointSet = cross;
	_facts->addFact(_smaller, s);

	{
		_parent->removeRef(_bigger);
		_parent->addGeneral(_smaller);

		RuleSet::Entry e;
		e.first = RuleSet::FULL;
		e.second.insert(_bigger);
		_parent->addRule(e);
	}

	if(diffs.empty()) return false;

	// Otherwise we have something to reveal!!
	surePoints->insert(diffs.begin(), diffs.end());
	return true;
}
