// Copyright 2008 ProjectWizards GmbH. All rights reserved.

//console.log("Formatter.js");

var Formatter = Base.extend({
	constructor: function()
	{},

	stringForObjectValue: function(object){
		return object ? object.toString() : "";
	},
	
	objectValueForString: function(string){
		return string;
	},
	
	setPreferedWidth: function(width){
		
	}
});

// ************************* RoleFormatter *********************** 

var RoleFormatter = Formatter.extend({
	constructor: function(project){
		this.base();
		this.project = project;
	},

	stringForObjectValue: function(role){
		return role.valueForKey('title');
	},
	
	objectValueForString: function(string){
		var objectValue = null;
		if(string) {
			var roles = this.project.valueForKey('roles');
			if(roles) {
				var role = null;
				for(var i=0; i<roles.length; i++) {
					role = roles[i];
					if(role.valueForKey('title') == string) {
						objectValue = role;
						break;
					}
				}
				if(!objectValue && string.length) {
					// create a new role with the given name:
					objectValue = this.project.context.insertNewObjectForEntityForName('Role', this.project.storeID());
					objectValue.setValueForKey(this.project, 'project');
					objectValue.setValueForKey(string, 'title');
					this.project.context.synchronize();
				}
			}
		}
		return objectValue;
	}
});



// ************************* DateFormatter *********************** 

var DateFormatter = Formatter.extend({
	constructor: function(datePattern, timePattern, utcTimezone, environment){
		this.base();
		this.datePattern = datePattern;
		this.timePattern = timePattern;
		this.environment = environment;
		this.utcTimezone = utcTimezone;
	},

	setPreferedWidth: function(width){
		if(!width)
			this.useShortDescriptions = null;
		this.useShortDescriptions = width < 90;		
	},

	stringForObjectValue: function(date){
		if(date){
			var string = date.format(this.format(), true);
			string = string.strip();
			return string;
		}
		return "";
	},
	
	objectValueForString: function(string){
		if(!string.endsWith(' '))
			string+=" ";
		var date = null;
		var time = Date.getDateFromFormat(string, this.format(), true);
		if(!time && this.datePattern)
			time = Date.getDateFromFormat(string, this.datePattern + " ", true); 
		if(time)
			date = new Date(time);
		return date;
	},
	
	format: function(){
		var timePattern = this.useShortDescriptions ? null : this.timePattern;
		return "" + (this.datePattern ? this.datePattern + " " : "") + (timePattern ? timePattern : "");
	}

});

// ************************* FlagStatusFormatter *********************** 

var FlagStatusFormatter = Formatter.extend({
	constructor: function(environment){
		this.base();
		this.environment = environment;
	},

	stringForObjectValue: function(flagStatus){
		return this.environment.format.flagStatusTable[flagStatus];
	}			
});

// ************************* PercentFormatter *********************** 

var PercentFormatter = Formatter.extend({
	constructor: function(){
		this.base();
		this.percentSymbol = "%"
	},

	stringForObjectValue: function(value){
		return value*100 + " "+this.percentSymbol;
	},	

	objectValueForString: function(string) {
		var value = parseFloat(string);
		if(!isNaN(value))
			return value/100.0;
		return 0.0;
	}
});
 
function formattedNumberString(numberString, environment){
	var array = numberString.toArray().reverse();
	var newArray = [];
	var countPos = false;
	var pos = 0;
	array.each(function(char, index){
		if(char != '.')
			newArray.push(char);
		else{
			newArray.push(environment.decimalSeparator);
			countPos = true;
		}
		if(countPos){
			if(pos!= 0 && pos%3 == 0 && index<array.length-2)
				newArray.push(environment.thousandSeparator);
			pos++;
		}
	});
	newArray.reverse();
	var string = "";
	newArray.each(function(char){
		string+=char;
	});
	return string;
}

function numberStringFromFormattedString(formattedString, environment) {
	var result = "";
	formattedString.toArray().each(function(char){
		if(char == environment.decimalSeparator)
			result += ".";
		else if(char != environment.thousandSeparator)
			result += char;
	});
	return result;
}
 
// ************************* CurrencyFormatter *********************** 

var CurrencyFormatter = Formatter.extend({
	constructor: function(environment){
		this.base();
		this.environment = environment;
	},

	stringForObjectValue: function(value, allowZeroValues){
		if(!allowZeroValues && value == 0)
			return ""; 
		var string = value == 0 ? "0" : formattedNumberString(stringWithFormat("%.2f", value), this.environment);
		if(this.environment.currencySymbolBeforeAmount)
			return this.environment.currencySymbol+" "+string; 
		return string+" "+this.environment.currencySymbol; 
	},	
										 
	objectValueForString: function(string) {
		if(!string || !string.length)
			return 0.0;
		var currencySymbol = environment.currencySymbol.strip(); 
		var array = string.toArray();
		var newString = "";
		array.each(function(char){
			if(char != currencySymbol && char != this.environment.thousandSeparator) {
				if(char == this.environment.decimalSeparator)
					newString+='.';
				else
					newString+=char;
			}
		});
		var value = parseFloat(newString);
		if(isNaN(value))
			value = 0.0;
		return value;
	}


});

// ************************* BudgetFormatter *********************** 

var BudgetFormatter = Formatter.extend({
	constructor: function(environment){
		this.base();
		this.environment = environment;
		this.currencyFormatter = new CurrencyFormatter(environment);
		this.percentFormatter = new PercentFormatter(environment);
	},

	stringForObjectValue: function(value){
		if(value.isFractional == undefined)
			return "";
		else if(value.isFractional)
			return this.percentFormatter.stringForObjectValue(value.amount);
		else
			return this.currencyFormatter.stringForObjectValue(value.amount);			
	},	

	objectValueForString: function(string) {
		var result;
		var amount = 0.0;
		if(string==null || string.length==0)
			result = null;
		else if(string.indexOf(this.percentFormatter.percentSymbol) != -1)
		{
			amount = this.percentFormatter.objectValueForString(string);
			if(amount != null)
				result = MEBudget.budgetWithAmountIsFractional(amount, true);
		}
		else
		{
			amount = this.currencyFormatter.objectValueForString(string);
			if(amount != null)
				result = MEBudget.budgetWithAmountIsFractional(amount, false);
		}
		return result;
	}
});



// ************************* BooleanFormatter *********************** 

var BooleanFormatter = Formatter.extend({
	constructor: function(environment){
		this.environment = environment;
		this.base();
	},

	stringForObjectValue: function(value){
		if(value)
			return this.environment.localizedTrueString; 
		return this.environment.localizedFalseString; 
	},
	
	objectValueForString: function(string) {
		if(string == this.environment.localizedTrueString)
			return true;
		else if(string == this.environment.localizedFalseString)
			return false;
		return parseInt(string) ? true : false;
	}

});

// ************************* DurationFormatter *********************** 

var DurationFormatter = Formatter.extend( {
    constructor: function(environment) {
        this.base();
		this.useShortDescriptions = false;
		this.materialUnit = null;
		this.allowsFloating = true;
		this.environment = environment;
		var pattern = "[+]*([-0-9"+environment.decimalSeparator+environment.thousandSeparator+"]*)\\s*([^?\\ ]*)\\s*\\??\\s*";
		this.amountRegex = new RegExp(pattern);
    },
    
	setPreferedWidth: function(width)
	{
		this.useShortDescriptions = width < 55;
	},
	
	allowsNoUnit: function() {
		return true;
	},
	
    stringForObjectValue: function(duration) {
        if(duration){
			var amount = duration.amount;
			var unit = duration.unit;
			var result;
            
			if(unit==MEDuration.DurationUnit.MEFraction){
				amount *= 100.0;
				result = formattedNumberString(amount.toString(), this.environment);
			}else{
				var places;
				if(!this.useShortDescriptions && Math.abs(amount % 0.1) > 0.001)
					places = 2;
				else if(Math.abs(amount % 1.0) > 0.01)
					places = 1;
				else
					places = 0;			
				result = stringWithFormat("%1."+places+"f", amount);			
				if(result.endsWith(".00"))
					result = result.substring(0, result.length-3);
				result = formattedNumberString(result, this.environment);
			}
			if(this.materialUnit && unit== MEMaterialUnit)
				result += " " + materialUnit;
			else{
				var floating = duration.isFloating;
				var plural = !(Math.abs(amount-1.0)<0.00001 || Math.abs(amount+1.0)<0.00001);
				var unitDescription;
				if(this.useShortDescriptions){
					unitDescription = this.shortDescriptionForUnitPluralFloating(unit, plural, floating);
				}else{
					result += " ";
					unitDescription = this.descriptionForUnitPluralFloating(unit, plural, floating);
				}
				result += unitDescription;
				if(duration.relativeError)
					result += this.useShortDescriptions ? "?" : " ?";
			}
			
			return result;
		}
        return "";
    },
    
	objectValueForString: function(string) {
		var result = null;
		if(string && string.length > 0)
		{
			string = numberStringFromFormattedString(string, this.environment);
			var match = this.amountRegex.exec(string);
			if(match) 
			{
				var amountString = match[1]; 
				var unitString = match[2]; 					
				var amountNumber = parseFloat(amountString);
				var amount = amountNumber || 0.0;
				var relativeError = string.search(/\?/) == -1 ? 0 : 1;
				var durUnit = {};
				if(this.materialUnit && unitString == materialUnit)
				{
					durUnit.unit = eDurationUnit.MEMaterialUnit;
					durUnit.isFloating = false;
				}
				else
					durUnit = this.unitForString(unitString);
				
				if(durUnit.unit == eDurationUnit.MEFraction)
					amount /= 100.0;
				
				var floating = durUnit.isFloating;
				
				if(durUnit.unit == eDurationUnit.MENoUnit && !this.allowsNoUnit())
					durUnit.unit = eDurationUnit.MEDays;
					
				result = MEDuration.durationWithAmountUnitIsFloatingRelativeError(amount, durUnit.unit, floating, relativeError);
			}
		}
		return result;
	},

	shortDescriptionForUnitPluralFloating: function(unit, plural, floating){
		var Unit = MEDuration.DurationUnit;
		if(floating){
			switch(unit){
				default:
				case Unit.MENoUnit:
					return "";
				case Unit.MEDays:
					return plural ? this.environment.localizedString("edays3") : this.environment.localizedString("eday3");
				case Unit.MECalendarWeeks:
				case Unit.MEWeeks:
					return plural ? this.environment.localizedString("eweeks3") : this.environment.localizedString("eweek3");
				case Unit.MEMonths:
					return plural ? this.environment.localizedString("emonths3") : this.environment.localizedString("emonth3");
				case Unit.MEYears:
					return plural ? this.environment.localizedString("eyears3") : this.environment.localizedString("eyear3");
				case Unit.MESeconds:
					return plural ? this.environment.localizedString("eseconds3") : this.environment.localizedString("esecond3");
				case Unit.MEMinutes:
					return plural ? this.environment.localizedString("eminutes3") : this.environment.localizedString("eminute3");
				case Unit.MEHours:
					return plural ? this.environment.localizedString("ehours3") : this.environment.localizedString("ehour3");
				case Unit.MEFraction:
					return "%";
			}
		}else{
			switch(unit) {
				default:
				case Unit.MENoUnit:
					return "";
				case Unit.MEDays:
					return plural ? this.environment.localizedString("days3") : this.environment.localizedString("day3");
				case Unit.MECalendarWeeks:
				case Unit.MEWeeks:
					return plural ? this.environment.localizedString("weeks3") : this.environment.localizedString("week3");
				case Unit.MEMonths:
					return plural ? this.environment.localizedString("months3") : this.environment.localizedString("month3");
				case Unit.MEYears:
					return plural ? this.environment.localizedString("years3") : this.environment.localizedString("year3");
				case Unit.MESeconds:
					return plural ? this.environment.localizedString("seconds3") : this.environment.localizedString("second3");
				case Unit.MEMinutes:
					return plural ? this.environment.localizedString("minutes3") : this.environment.localizedString("minute3");
				case Unit.MEHours:
					return plural ? this.environment.localizedString("hours3") : this.environment.localizedString("hour3");
				case Unit.MEFraction:
					return "%";
				case Unit.MEMaterialUnit:
					return plural ? this.environment.localizedString("materialUnits3") : this.environment.localizedString("materialUnit3");
			}
		}
	},
	
	descriptionForUnitPluralFloating: function(unit, plural, floating){
		var Unit = MEDuration.DurationUnit;
		if(floating){
			switch(unit){
				default:
				case Unit.MENoUnit:
					return "";
				case Unit.MEDays:
					return plural ? this.environment.localizedString("edays") : this.environment.localizedString("eday");
				case Unit.MECalendarWeeks:
				case Unit.MEWeeks:
					return plural ? this.environment.localizedString("eweeks") : this.environment.localizedString("eweek");
				case Unit.MEMonths:
					return plural ? this.environment.localizedString("emonths") : this.environment.localizedString("emonth");
				case Unit.MEYears:
					return plural ? this.environment.localizedString("eyears") : this.environment.localizedString("eyear");
				case Unit.MESeconds:
					return plural ? this.environment.localizedString("eseconds") : this.environment.localizedString("esecond");
				case Unit.MEMinutes:
					return plural ? this.environment.localizedString("eminutes") : this.environment.localizedString("eminute");
				case Unit.MEHours:
					return plural ? this.environment.localizedString("ehours") : this.environment.localizedString("ehour");
				case Unit.MEFraction:
					return "%";
			}
		}else{
			switch(unit) {
				default:
				case Unit.MENoUnit:
					return "";
				case Unit.MEDays:
					return plural ? this.environment.localizedString("days") : this.environment.localizedString("day");
				case Unit.MECalendarWeeks:
				case Unit.MEWeeks:
					return plural ? this.environment.localizedString("weeks") : this.environment.localizedString("week");
				case Unit.MEMonths:
					return plural ? this.environment.localizedString("months") : this.environment.localizedString("month");
				case Unit.MEYears:
					return plural ? this.environment.localizedString("years") : this.environment.localizedString("year");
				case Unit.MESeconds:
					return plural ? this.environment.localizedString("seconds") : this.environment.localizedString("second");
				case Unit.MEMinutes:
					return plural ? this.environment.localizedString("minutes") : this.environment.localizedString("minute");
				case Unit.MEHours:
					return plural ? this.environment.localizedString("hours") : this.environment.localizedString("hour");
				case Unit.MEFraction:
					return "%";
				case Unit.MEMaterialUnit:
					return plural ? this.environment.localizedString("materialUnits") : this.environment.localizedString("materialUnit");
			}
		}
	},

	unitForString: function(str){
		var results = [];
		str = str.toLowerCase();
		var matchResult;
		if(str.length) {
			matchResult = this.stringMatchesUnit(str, "hours");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEHours, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "days");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEDays, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "weeks");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEWeeks, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "years");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEYears, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "minutes");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEMinutes, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "months");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEMonths, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "seconds");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MESeconds, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "%");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEFraction, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "fraction");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEFraction, isFloating:false}});
			matchResult = this.stringMatchesUnit(str, "edays");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEDays, isFloating:true}});
			matchResult = this.stringMatchesUnit(str, "eweeks");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEWeeks, isFloating:true}});
			matchResult = this.stringMatchesUnit(str, "eminutes");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEMinutes, isFloating:true}});
			matchResult = this.stringMatchesUnit(str, "emonths");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEMonths, isFloating:true}});
			matchResult = this.stringMatchesUnit(str, "eyears");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEYears, isFloating:true}});
			matchResult = this.stringMatchesUnit(str, "ehours");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEHours, isFloating:true}});
			matchResult = this.stringMatchesUnit(str, "eseconds");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MESeconds, isFloating:true}});
			matchResult = this.stringMatchesUnit(str, "material unit");
			if(matchResult)
				results.push({diff:matchResult.diff, result:{unit:eDurationUnit.MEMaterialUnit, isFloating:false}});
		}
		if(results.length){
			var optimalUnit = null;
			for(var i=0; i<results.length; i++) {
				var result = results[i]
				if(!optimalUnit || result.diff < optimalUnit.diff)
					optimalUnit = result;
			}
			return optimalUnit.result;
		}
		return {unit:eDurationUnit.MENoUnit, isFloating:false};
	},

		
	stringMatchesUnit: function(string, key){
		var result = null;
		for(var alternative=1; alternative<4; alternative++){
			var keyPlusNum = alternative>1 ? key+alternative : key;
			var localizedString = this.environment.localizedString(keyPlusNum);
			if(localizedString && localizedString.toLowerCase().startsWith(string)){
				var diff = Math.abs(localizedString.length-string.length); 
				if(!result || diff < result.diff)
					result = {diff:diff};
			}
		}
		return result;
	}
	

});


// ************************* UtilisationFormatter *********************** 

var UtilisationFormatter = Formatter.extend({
	constructor: function(environment){
		this.environment = environment;
		this.base();
		this.validateAmountOnly = false;
		this.durationFormatter = new DurationFormatter(environment);
		this.materialUnit = null;
		this.decimalPlaces = 2;
		var pattern = "([0-9"+environment.decimalSeparator+environment.thousandSeparator+"]*)(.*)";
		this.regex = new RegExp(pattern);		
	},

	stringForObjectValue: function(value){
		var result = "";
		if(value){
			var amount = value.amount;
			if(value.denominatorUnit == eDurationUnit.MEFraction)
				amount *= 100.0;
			var amountPart = stringWithFormat("%."+this.decimalPlaces+"f%s", amount, value.denominatorUnit == eDurationUnit.MEFraction ? "%" : "");
			if(this.validateAmountOnly)
				return amountPart;			
			result += amountPart;
			if(value.denominatorUnit != eDurationUnit.MEFraction){
				var denom = this.durationFormatter.descriptionForUnitPluralFloating(value.denominatorUnit, false, false);
				if(this.materialUnit)
					result += materialUnit;
				result += "/";
				if(denom)
					result += denom;
			}
		}
		return result;		
	},

	objectValueForString: function(string) {
		var result = null;
		if(!string || string.length==0)
			result = null;
		else if(this.validateAmountOnly){	
			var value = parseFloat(string);
			if(isNaN(value))
				value = 0.0;
			var unit = eDurationUnit.MESpecialCompareUnit;
			if(string.search(/\%/) != -1){
				unit = eDurationUnit.MEFraction;
				value /= 100.0;
			}
			result = MEUtilisation.utilisationWithAmountDenominatorUnit(value, unit);
		}else{ 
			var numerator = null;
			var denominator = null;
			var location = string.indexOf("/");
			if(location != -1){
				numerator = string.substring(0, location);
				denominator = string.substring(location+1);
			}
			else
				numerator = string;
			
			var match = this.regex.exec(numerator);
			if(match) {
				var numeratorAmountString = match[1]; 
				var numeratorAmount = parseFloat(numeratorAmountString); 					
				if(isNaN(numeratorAmount))
					numeratorAmount = 0.0;
				if(numeratorAmount<0.0)
					numeratorAmount *= -1.0;
				
				var unit = {};
				if(string.indexOf("%") != -1){
					unit.isFloating = false;
					unit.unit = eDurationUnit.MEFraction;
				}else if(!denominator){
					unit.isFloating = false;
					unit.unit = eDurationUnit.MENoUnit;
				}else
					unit = this.durationFormatter.unitForString(denominator);
							
				if(unit.unit==eDurationUnit.MEFraction)
					numeratorAmount /= 100.0;
				result = numeratorAmount ? MEUtilisation.utilisationWithAmountDenominatorUnit(numeratorAmount, unit.unit) : null;
			}
		}
		return result;
	}

});


// ************************* RateFormatter *********************** 

var RateFormatter = Formatter.extend({
	constructor: function(environment){
		this.currencyFormatter = new CurrencyFormatter(environment);
		this.durationFormatter = new DurationFormatter(environment); 
		this.environment = environment;
		this.materialUnit = null;
		this.base();
	},

	stringForObjectValue: function(value){
		var currencyPart = this.currencyFormatter.stringForObjectValue(value.amount, true);
		var result = currencyPart;
		var denominator = value.denominatorUnit == MEDuration.DurationUnit.MEMaterialUnit ? this.materialUnit : this.durationFormatter.descriptionForUnitPluralFloating(value.denominatorUnit, false, value.denominatorUnitIsFloating);
		if(denominator)
			result += "/" + denominator;
		return result;		
	},

	objectValueForString: function(string) {
		var result = null;
		if(string && string.length > 0)
		{
			var numerator   = null;
			var denominator = null;
			var parts = string.split("/");
			if(parts.length >= 1)
				numerator = parts[0];
			if(parts.length >= 2)
				denominator = parts[1];				
			
			var amount = this.currencyFormatter.objectValueForString(numerator);
			var Units = MEDuration.DurationUnit;
			var unit  = {};
			if(this.materialUnit)
			{
				unit.isFloating = NO;
				unit.unit = Units.MEMaterialUnit;
			}
			else if(!denominator)
			{
				unit.isFloating = NO;
				unit.unit = Units.MENoUnit;
			}
			else
				unit = MEDuration.sharedFormatter(this.environment).unitForString(denominator);
			result = MERate.RateWithAmountUnitUnitIsFloating(amount, unit.unit, unit.isFloating);
		}
		return result;
	}

});






























