/*
    CCCC - C and C++ Code Counter
    Copyright (C) 1994-2005 Tim Littlefair (tim_littlefair@hotmail.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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
*/
// cccc_opt.cc
#include "cccc.h"
#include <fstream>

#include "cccc_opt.h"
#include "cccc_utl.h"
#include "cccc_met.h"

#include <map>

typedef std::map<string,string> file_extension_language_map_t;
typedef std::map<string,Metric_Treatment*> metric_treatment_map_t;
typedef std::pair<string,string> dialect_keyword_t;
typedef std::map<dialect_keyword_t,string> dialect_keyword_map_t;

static file_extension_language_map_t extension_map;
static metric_treatment_map_t treatment_map;
static dialect_keyword_map_t dialect_keyword_map;

// these are declared extern so that it can be defined later in the file
extern const char *default_fileext_options[];
extern const char *default_treatment_options[];
extern const char *default_dialect_options[];

static void add_file_extension(CCCC_Item& fileext_line)
{
	string ext, lang;
	if(
		fileext_line.Extract(ext) &&
		fileext_line.Extract(lang)
		)
    {
		file_extension_language_map_t::value_type extension_pair(ext,lang);
		extension_map.insert(extension_pair);
    }
}

void add_treatment(CCCC_Item& treatment_line)
{
	Metric_Treatment *new_treatment=new Metric_Treatment(treatment_line);
	metric_treatment_map_t::iterator iter=
		treatment_map.find(new_treatment->code);
	
	if(iter!=treatment_map.end())
    {
		delete (*iter).second;
		(*iter).second=new_treatment;
    }
	else
    {
		metric_treatment_map_t::value_type 
			treatment_pair(new_treatment->code,new_treatment);
		treatment_map.insert(treatment_pair);
    }
}

static void add_dialect_keyword(CCCC_Item& dialect_keyword_line)
{
	string dialect, keyword, policy;
	if(
		dialect_keyword_line.Extract(dialect) &&
		dialect_keyword_line.Extract(keyword) &&
		dialect_keyword_line.Extract(policy) 
		)
    {
		dialect_keyword_map_t::key_type kt(dialect,keyword);
		dialect_keyword_map_t::value_type vt(kt,policy);
		dialect_keyword_map.insert(vt);
    }
}

void CCCC_Options::Add_Option(CCCC_Item& option)
{
	string first_token;
	bool retval=option.Extract(first_token);
	
	if(retval==false)
    {
		// do nothing
    }
	else if(first_token=="CCCC_FileExt")
    {
		add_file_extension(option);
    }
	else if(first_token=="CCCC_MetTmnt")
    {
		add_treatment(option);
    }
	else if(first_token=="CCCC_Dialect")
    {
		add_dialect_keyword(option);
    }
	else
    {
		retval=false;
    }
}


void CCCC_Options::Save_Options(const string& filename)
{
	ofstream optstr(filename.c_str());
	file_extension_language_map_t::iterator felIter;
	for(felIter=extension_map.begin();
	felIter!=extension_map.end();
	++felIter)
    {
		CCCC_Item extLine;
		extLine.Insert("CCCC_FileExt");
		extLine.Insert((*felIter).first.c_str());
		extLine.Insert((*felIter).second.c_str());
		extLine.ToFile(optstr);
    }
	
	metric_treatment_map_t::iterator tIter;
	for(tIter=treatment_map.begin();
	tIter!=treatment_map.end();
	++tIter)
    {
		CCCC_Item tmtLine;
		tmtLine.Insert("CCCC_MetTmnt");
		tmtLine.Insert((*tIter).second->code); 
		tmtLine.Insert((*tIter).second->lower_threshold);
		tmtLine.Insert((*tIter).second->upper_threshold);
		tmtLine.Insert((*tIter).second->numerator_threshold);
		tmtLine.Insert((*tIter).second->width);     
		tmtLine.Insert((*tIter).second->precision);
		tmtLine.Insert((*tIter).second->name);
		tmtLine.ToFile(optstr);
    }
	
	dialect_keyword_map_t::iterator dkIter;
	for(dkIter=dialect_keyword_map.begin();
	dkIter!=dialect_keyword_map.end();
	++dkIter)
    {
		CCCC_Item dkLine;
		dkLine.Insert("CCCC_Dialect");
		dkLine.Insert((*dkIter).first.first);
		dkLine.Insert((*dkIter).first.second);
		dkLine.Insert((*dkIter).second);
		dkLine.ToFile(optstr);
    }
}

void CCCC_Options::Load_Options(const string& filename)
{
	ifstream optstr(filename.c_str());
	
	while(optstr.good())
    {
		CCCC_Item option_line;
		if(optstr.good() &&
			option_line.FromFile(optstr)
			)
		{
			Add_Option(option_line);
		}
    }
	
}

// initialise using hard-coded defaults
void CCCC_Options::Load_Options()
{  
	int i=0;
	
	const char **option_ptr;
	
	option_ptr=default_fileext_options;
	while( (*option_ptr)!=NULL)
    {
		string option_string="CCCC_FileExt@";
		option_string+=(*option_ptr);
		CCCC_Item option_line(option_string);
		Add_Option(option_line);
		option_ptr++;
    }
	
	option_ptr=default_treatment_options;
	while( (*option_ptr)!=NULL)
    {
		string option_string="CCCC_MetTmnt@";
		option_string+=(*option_ptr);
		CCCC_Item option_line(option_string);
		Add_Option(option_line);
		option_ptr++;
    }
	
	option_ptr=default_dialect_options;
	while( (*option_ptr)!=NULL)
    {
		string option_string="CCCC_Dialect@";
		option_string+=(*option_ptr);
		CCCC_Item option_line(option_string);
		Add_Option(option_line);
		option_ptr++;
    }
}

// map a filename to a language
string CCCC_Options::getFileLanguage(const string& filename)
{
	string retval;
	string extension;
	file_extension_language_map_t::iterator iter;
	
	size_t extpos=filename.rfind(".");
	if(extpos!=string::npos)
    {
		extension=filename.substr(extpos);
		iter=extension_map.find(extension);
		if(iter!=extension_map.end())
		{
			retval=(*iter).second;
		}
    }
	if(retval.size()==0)
    {
		iter=extension_map.find("");
		if(iter!=extension_map.end())
		{
			retval=(*iter).second;
		}
		else
		{
			// could not find language for extension
			cerr << "No language found for extension " << extension.c_str() << endl;
		}
    }
	return retval;
}

// map a metric name to a Metric_Treatment object
Metric_Treatment *CCCC_Options::getMetricTreatment(const string& metric_tag)
{
	Metric_Treatment *retval=NULL;
	metric_treatment_map_t::iterator iter=treatment_map.find(metric_tag);
	if(iter!=treatment_map.end())
    {
		retval=(*iter).second;
    }
	return retval;
}

string CCCC_Options::dialectKeywordPolicy(const string& lang, const string& kw)
{
	string retval;
	dialect_keyword_map_t::key_type kt(lang,kw);
	dialect_keyword_map_t::const_iterator iter=dialect_keyword_map.find(kt);
	if(iter!=dialect_keyword_map.end())
    {
		retval=(*iter).second;
    }
	return retval;
}


const char *default_fileext_options[]=
{
	// file extensions
	".c@c.ansi@",
	".h@c.ansi@",

	".cc@c++.ansi@",
	".cpp@c++.ansi@",
	".cxx@c++.ansi@",
	".c++@c++.ansi@",
	".C@c++.ansi@",
	".CC@c++.ansi@",
	".CPP@c++.ansi@",
	".CXX@c++.ansi@",
	".hh@c++.ansi@",
	".hpp@c++.ansi@",
	".hxx@c++.ansi@",
	".h++@c++.ansi@",
	".H@c++.ansi@",
	".HH@c++.ansi@",
	".HPP@c++.ansi@",
	".HXX@c++.ansi@",
	".H++@c++.ansi@",

	".j@java@",
	".jav@java@",
	".java@java@",
	".J@java@",
	".JAV@java@",
	".JAVA@java@",

	".ada@ada.95@",
	".ads@ada.95@",
	".adb@ada.95@",
	".ADA@ada.95@",
	".ADS@ada.95@",
	".ADB@ada.95@",

	// The language associated with the empty file extension would be used as a default
	// if defined.
	// This is presently disabled so that we don't process files in 
	// MSVC projects like .rc, .odl which are not in C++.
	// "@c++.ansi@",

	NULL
};

const char *default_treatment_options[] = 
{
	// metric treatments
	// all metric values are displayed using the class CCCC_Metric, which may be
	// viewed as ratio of two integers associated with a character string tag
	// the denominator of the ratio defaults to 1, allowing simple counts to
	// be handled by the same code as is used for ratios
	//
	// the tag associated with a metric is used as a key to lookup a record
	// describing a policy for its display (class Metric_Treatment)
	//
	// the fields of each treatment record are as follows:
	// TAG     the short string of characters used as the lookup key. 
	// T1, T2  two numeric thresholds which are the lower bounds for the ratio of
	// the metric's numerator and denominator beyond which the 
	//  value is treated as high or extreme by the analyser
	//  these will be displayed in emphasized fonts, and if the browser
	//  supports the BGCOLOR attribute, extreme values will have a red
	//  background, while high values will have a yellow background.
	//  The intent is that high values should be treated as suspicious but
	//  tolerable in moderation, whereas extreme values should almost 
	//  always be regarded as defects (not necessarily that you will fix 
	//  them).
	// NT  a third threshold which supresses calculation of ratios where
	//     the numerator is lower than NT.
	//     The principal reason for doing this is to prevent ratios like L_C
	//  being shown as *** (infinity) and displayed as extreme when the 
	//  denominator is 0, providing the numerator is sufficiently low.
	//  Suitable values are probably similar to those for T1.
	// W       the width of the metric (total number of digits).
	// P       the precision of the metric (digits after the decimal point).
	// Comment a free form field extending to the end of the line.
	
	// TAG      T1      T2   NT  W  P Comment
	"LOCf@      30@    100@   0@ 6@ 0@Lines of code/function@",
	"LOCm@     500@   2000@   0@ 6@ 0@Lines of code/single module@", 
	"LOCper@   500@   2000@   0@ 6@ 3@Lines of code/average module@", 
	"LOCp@  999999@ 999999@   0@ 6@ 0@Lines of code/project@", 
	"MVGf@      10@     30@   0@ 6@ 0@Cyclomatic complexity/function@",
	"MVGm@     200@   1000@   0@ 6@ 0@Cyclomatic complexity/single module@",
	"MVGper@   200@   1000@   0@ 6@ 3@Cyclomatic complexity/average module@",
	"MVGp@  999999@ 999999@   0@ 6@ 0@Cyclomatic complexity/project@",
	"COM@   999999@ 999999@   0@ 6@ 0@Comment lines@",
	"COMper@999999@ 999999@   0@ 6@ 3@Comment lines (averaged)@",
	"M_C@        5@     10@   5@ 6@ 3@MVG/COM McCabe/comment line@",
	"L_C@        7@     30@  20@ 6@ 3@LOC/COM Lines of code/comment line@",
	"FI@        12@     20@   0@ 6@ 0@Fan in (overall)@",
	"FIv@        6@     12@   0@ 6@ 0@Fan in (visible uses only)@",
	"FIc@        6@     12@   0@ 6@ 0@Fan in (concrete uses only)@",
	"FO@        12@     20@   0@ 6@ 0@Fan out (overall)@",
	"FOv@	       6@     12@   0@ 6@ 0@Fan out (visible uses only)@",
	"FOc@        6@     12@   0@ 6@ 0@Fan out (concrete uses only)@",
	"IF4@      100@   1000@   0@ 6@ 0@Henry-Kafura/Shepperd measure (overall)@",
	"IF4v@      30@    100@   0@ 6@ 0@Henry-Kafura/Shepperd measure (visible)@",
	"IF4c@      30@    100@   0@ 6@ 0@Henry-Kafura/Shepperd measure (concrete)@",
	 // WMC stands for weighted methods per class, 
	 // the suffix distinguishes the weighting function
	 "WMC1@      30@    100@   0@ 6@ 0@Weighting function=1 unit per method@",
	 "WMCv@      10@     30@   0@ 6@ 0@Weighting function=1 unit per visible method@",
	 "DIT@	       3@      6@   0@ 6@ 0@Depth of Inheritance Tree@",
	 "NOC@	       4@     15@   0@ 6@ 0@Number of children@",
	 "CBO@	      12@     30@   0@ 6@ 0@Coupling between objects@",
	 "8.3@    999999@ 999999@   0@ 8@ 3@General format for fixed precision 3 d.p.@",
	 NULL
};

const char *default_dialect_options[] = 
{
	// This configuration item allows the description of 
	// dialects in which C/C++ identifiers get treated
	// as supplementary keyword.  The rules specified
	// here (or in the runtime option file, if specified)
	// allow the parser to make the call 
	// CCCC_Options::dialect_keyword_policy(lang,kw) which
	// returns a string.  If nothing is known about the 
	// pair <lang,kw>, an empty string is returned.  If
	// an entry is specified here the associated string is 
	// returned, which is called the policy, which the
	// parser uses to guide its actions.  The most common
	// policy is to ignore, but any string can be 
	// specified, providing the person implementing
	// the parser can think of an intelligent way to 
	// proceed.
	"c++.mfc@BEGIN_MESSAGE_MAP@start_skipping@",
		"c++.mfc@END_MESSAGE_MAP@stop_skipping@",
		"c++.stl@__STL_BEGIN_NAMESPACE@ignore@",
		"c++.stl@__STL_END_NAMESPACE@ignore@",
		NULL
};











