#ifndef SCANBAG_H
#define SCANBAG_H

/*
 * Copyright (C) 2005--2008  Enrico Zini <enrico@enricozini.org>
 *
 * 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 <map>
#include <list>
#include <set>
#include <vector>
#include <string>

#include <wibble/sys/mutex.h>
#include "scanner/scan.h"

struct ScanBagListener
{
	virtual ~ScanBagListener() {}
	virtual void candidatesChanged() = 0;
};

/**
 * A collection of scans, with the selection logic.
 *
 * A scan will either succeed or time out, but never fail: this means that we
 * cannot use boolean logic.  What we do instead is, when a success is
 * reported, we discard all profiles that do not contain the test that
 * succeeded.  When only one profile is left, that is the resulting profile.
 *
 * Another important design point of ScanBag is that a decision must be taken
 * as soon as possible, and we need to do everything possible to avoid waiting
 * for scans to time out before taking a decision.  Therefore a result is
 * produced as soon as a profile is matched in a non ambiguous way.
 *
 * The main data structure of ScanBag maps profile names to all their tests,
 * represented by the test signatures.  For every test, a boolean is kept to
 * represent if that test has succeeded.
 */
class ScanBag : protected std::map< std::string, std::map<std::string, bool> >
{
	wibble::sys::Mutex mutex;

	// List of scans in order of parsing
	std::list<scanner::Scan*> scans;

	// Name of the default profile to use when all others fail
	std::string defaultProfile;

	// True if at least one scan reported success
	bool any_success;

	ScanBagListener* listener;

	ScanBag() : any_success(false), listener(0) {}
	// TODO: deallocate scans
	~ScanBag();

public:
	// These functions are unsafe to call after the scans start
	void setListener(ScanBagListener* listener)
	{
		this->listener = listener;
	}

	/// Add a new test to the candidate profiles
	void add(scanner::Scan* scan);

	/// Get the list of available tests
	const std::list<scanner::Scan*>& getScans() const { return scans; }

	/// Remove all items from this ScanBag
	void clear()
	{
		std::map<std::string, std::map<std::string, bool> >::clear();
		scans.clear();
		defaultProfile.clear();
		any_success = false;
	}

	// These functions are safe to call after the scans start

	/// Notify the success of a test, returning the list of candidate profiles
	void notifySuccess(scanner::Scan* scan);

	/// Return true if at least one scanner has notified success so far
	bool anySuccess();

	/// Get the less specific profile among the available ones
	std::string getLessSpecific();

	/// Get the default profile
	std::string getDefault();

	/**
	 * If some success has been reported and only one candidate is left, return
	 * it.  Else, return the empty string
	 */
	std::string getFinal();

	/// Get the remaning profiles
	std::vector<std::string> getRemaining();

	/// Debug method: dump the contents to stdout
	void dump();

	/// Singleton access method
	static ScanBag& get();
};

// vim:set ts=3 sw=3:
#endif
