/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/


#ifndef __ANALYSIS_H__
#define __ANALYSIS_H__

//#define EXPERIMENTAL

#include <iostream>
#include "classad/classad_distribution.h"
#include "condor_classad.h"
#include "condor_attributes.h"
#include "boolExpr.h"
#include "resourceGroup.h"
//#include "classadList.h"
#include "explain.h"
#include "interval.h"
#include "boolValue.h"
#include "conversion.h"

#include "result.h"

#include <sstream>

/// The analyzer object
class ClassAdAnalyzer
{
 public:

		/** Default Constructor */
	ClassAdAnalyzer( bool result_as_struct = false );

		/** Destructor */
	~ClassAdAnalyzer( );

	classad_analysis::job::result GetResult() { return (result_as_struct && m_result) ? *m_result : classad_analysis::job::result(); }
	bool GetErrors(bool clear_errs=true, std::string * perrs = NULL) { 
		bool had_errors = ! errstm.str().empty();
		if (perrs) *perrs = errstm.str();
		if (clear_errs) errstm.clear();
		return had_errors;
	}

		/** Analyze a job ClassAd requirements expression.
		 *	@param request The job ClassAd
		 *	@param offers A list of machine ClassAds
		 *	@param buffer The results of the analysis
		 *  @return true on success false on failure
		 */
	bool AnalyzeJobReqToBuffer( ClassAd *request, ClassAdList &offers,
								std::string &buffer, std::string &pretty_req);

#if defined( COLLECTIONS )
		/** Analyze a job ClassAd requirements expression.
		 *	@param request The job ClassAd
		 *	@param offers A collection of machine ClassAds
		 *	@param buffer The results of the analysis
		 *	@return true on success false on failure
		 */
	bool AnalyzeJobReqToBuffer( classad::ClassAd *request,
								classad::ClassAdCollectionServer &offers,
								std::string &buffer );
#endif

		/** Analyze job ClassAd attributes.
		 *	@param request The job ClassAd
		 *	@param offers A list of machine ClassAds
		 *	@param buffer The results of the analysis
		 *	@return true on success false on failure
		 */
	bool AnalyzeJobAttrsToBuffer( ClassAd *request, ClassAdList &offers,
								  std::string &buffer );

		/** Analyze an arbitrary expression.
		 *	@param mainAd The ClassAd contaning the expression
		 *	@param contextAd The target ClassAd
		 *	@param attr The attribute corresponding to the expression
		 *	@param buffer The results of the analysis
		 *	@return true on success false on failure		 
		 */
	bool AnalyzeExprToBuffer( classad::ClassAd *mainAd,
							  classad::ClassAd *contextAd,
							  std::string &attr, std::string &buffer );

 private:
	bool result_as_struct;
	classad_analysis::job::result *m_result;

	MultiProfile *jobReq;
	classad::MatchClassAd mad;

	ExprTree* std_rank_condition;
	ExprTree* preempt_rank_condition;
	ExprTree* preempt_prio_condition;
	ExprTree* preemption_req;

	std::stringstream errstm;

	void ensure_result_initialized(classad::ClassAd *request);

	// wrapper functions to add information to the result only if we're generating one
	void result_add_suggestion(classad_analysis::suggestion s);
	void result_add_explanation(classad_analysis::matchmaking_failure_kind mfk, const classad::ClassAd &resource);
	void result_add_explanation(classad_analysis::matchmaking_failure_kind mfk, ClassAd *resource);
	void result_add_machine(const classad::ClassAd &resource);

	bool AnalyzeJobReqToBuffer( classad::ClassAd *request, ResourceGroup &offers, std::string &buffer, std::string &pretty_req );
	bool AnalyzeJobAttrsToBuffer( classad::ClassAd *request, ResourceGroup &offers, std::string &buffer );

	bool BuildBoolTable( MultiProfile *, ResourceGroup &, BoolTable &result );
	bool BuildBoolTable( Profile *, ResourceGroup &, BoolTable &result );
	
	bool MakeResourceGroup( ClassAdList &, ResourceGroup &result );

#if defined( COLLECTIONS )
	bool MakeResourceGroup( classad::ClassAdCollectionServer &, ResourceGroup &result );
#endif

	bool NeedsBasicAnalysis( ClassAd *request );
	void BasicAnalyze(ClassAd* request, ClassAd* offer);

	bool SuggestCondition( MultiProfile *, ResourceGroup & );
	bool SuggestConditionRemove( Profile *, ResourceGroup & );
	bool SuggestConditionModify( Profile *, ResourceGroup & );

	bool FindConflicts( MultiProfile *, ResourceGroup & );
	bool FindConflicts( Profile *, ResourceGroup & );

	bool AnalyzeAttributes( classad::ClassAd *, ResourceGroup &,
							ClassAdExplain &result );

	bool EqualsIgnoreCase( const std::string &, const std::string & );
	bool DefinedLiteralValue( classad::Value & );
	bool AddConstraint( ValueRange *&, Condition * );
	bool AddDefaultConstraint( ValueRange *&vr );

	bool PruneDisjunction( classad::ExprTree *, classad::ExprTree *& result );
	bool PruneConjunction( classad::ExprTree *, classad::ExprTree *& result );
	bool PruneAtom( classad::ExprTree *, classad::ExprTree *& result );

		// unsupported
//	bool InDNF( classad::ExprTree * );
//	bool IsAtomicBooleanFormula( classad::Operation * );
//	bool PropagateNegation( classad::ExprTree *, classad::ExprTree *&result );
//	bool ToDNF( classad::ExprTree *, classad::ExprTree *&result );
	
};

#endif // __ANALYSIS_H__

