package routines;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class NumberUtil {

   /**
    * returns the first not null Integer
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} Integer
    * 
    * {param} Interer(null,1,2) intnumbers: Integer.
    * 
    * {example} coalesce(null,13,null,2) # 13
    */
	public static Integer coalesce(Integer ...intnumbers) {
		for (Integer i : intnumbers) {
			if (i != null) {
				return i;
				}
					}
		return null;
	}
   /**
    * returns the first not null Integer
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} Float
    * 
    * {param} Float(null,1,2) floatnumbers: Float.
    * 
    * {example} coalesce(null,13,null,2) # 13
    */
	public static Float coalesce(Float ...floatnumbers) {
		for (Float f : floatnumbers) {
			if (f != null) {
				return f;
				}
					}
		return null;
	}
	
	   /**
	    * returns the first not null Integer
	    * 
	    * {Category} NumberUtil
	    * 
	    * {talendTypes} Float
	    * 
	    * {param} Double(null,1,2) double numbers: Double.
	    * 
	    * {example} coalesce(null,13,null,2) # 13
	    */
		public static Double coalesce(Double ...floatnumbers) {
			for (Double f : floatnumbers) {
				if (f != null) {
					return f;
					}
						}
			return null;
		}
	
	 /**
    * returns the first not null Long
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} Long
    * 
    * {param} Long(null,2,2) longnumbers: Long.
    * 
    * {example} coalesce(null,2,0,2) # 13
    */
	public static Long coalesce(Long ...longnumbers) {
		for (Long i : longnumbers) {
			if (i != null) {
				return i;
				}
					}
		return null;
	}
	
   /**
    * returns the first not null BigDecimal
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} BigDecimal
    * 
    * {param} BigDecimal(null,1,2) numbers: BigDecimal.
    * 
    * {example} coalesce(null,new BigDecimal("13"),null) # new BigDecimal("13")
    */
	public static BigDecimal coalesce(BigDecimal ...numbers) {
		for (BigDecimal i : numbers) {
			if (i != null) {
				return i;
			}
		}
		return null;
	}
	
   /**
    * return long and accepts Long, Double and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} long | Long
    * 
    * {param} Long(123) input: long format.
    * 
    * {example} getNullSafeLong("123") result: 123 ...
    * 
    */
	public static long getNullSafeLong(Object input) {
		if (input == null) {
			return 0;
		} else if (input instanceof Long) {
			Long i = (Long) input;
			if (i == 0) {
				return 0;
			} else {
				return i.longValue();
			}
		} else if (input instanceof Double) {
			long n = ((Double)input).longValue();
			return n;
		} else if (input instanceof String) {
			String s = (String) input;
			if (s.isEmpty()) {
				return 0;
			}
			return Long.parseLong((String) s);
		} else {
			return 0;
		}
	}

	/**
    * return integer and accepts Integer, Double and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} int | Integer
    * 
    * {param} Integer(123) input: float pointing format.
    * 
    * {example} getNullSafeInt("123") result: 123 ...
    * 
    */
	public static int getNullSafeInt(Object input) {
		if (input == null) {
			return 0;
		} else if (input instanceof Integer) {
			Integer i = (Integer) input;
			if (i == 0) {
				return 0;
			} else {
				return i.intValue();
			}
		} else if (input instanceof Double) {
			int n = ((Double)input).intValue();
			return n;
		} else if (input instanceof String) {
			String s = (String) input;
			if (s.isEmpty()) {
				return 0;
			}
			return Integer.parseInt((String) s);
		} else {
			return 0;
		}
	}
		
   /**
    * return integer and accepts Integer and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} int | Integer
    * 
    * {param} Integer(123) input: float pointing format.
    * 
    * {example} getNullSafeInt("123", "replacements) result: 123 ...
    * 
    */
	public static int getNullSafeInt(Object input, String ...toRemoveString) {
		if (input instanceof Integer) {
			Integer i = (Integer) input;
			if (i == 0) {
				return 0;
			} else {
				return i.intValue();
			}
		} else if (input instanceof String) {
			String s = (String) input;
			if (toRemoveString != null) {
				for (String r : toRemoveString) {
					s = s.replace(r, "");
				}
			}
			if (s == null || s.isEmpty()) {
				return 0;
			}
			return Integer.parseInt((String) s);
		} else {
			return 0;
		}
	}

   /**
    * return double and accepts Double and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} Double(123) input: float pointing format.
    * 
    * {example} getNullSafeDouble("123") result: 123 ...
    * 
    */
	public static double getNullSafeDouble(String input) {
		return getNullSafeDouble(input, Locale.US);
	}

   /**
    * return double and accepts Double and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} Double(123) input: float pointing format.
    * 
    * {param} Object(java.util.Local) locale: Locale.
    * 
    * {example} getNullSafeDouble("123") result: 123 ...
    * 
    */
	public static double getNullSafeDouble(String input, Locale locale) {
		if (input != null && input.isEmpty() == false) {
			input = input.replace("%", "").replace('"', ' ').trim();
			if (input == null || input.isEmpty()) {
				return 0d;
			}
			NumberFormat nf = NumberFormat.getInstance(locale);
			Number n = null;
			try {
				n = nf.parse(input);
			} catch (Exception e) {
				return 0d;
			}
			return n.doubleValue();
		} else {
			return 0;
		}
	}

	public static double getNullSafeDouble(Number input) {
		if (input != null) {
			return input.doubleValue();
		} else {
			return 0;
		}
	}

	/**
    * return double and accepts Double and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} Double(123) input: float pointing format.
    * 
    * {example} getNullSafeDouble("123", "toRemoveString) result: 123 ...
    * 
    */
	public static double getNullSafeDouble(String input, String ...toRemoveString) {
		return getNullSafeDoubleLocal(input, Locale.US, toRemoveString);
	}	
	
	/**
    * return double and accepts Double and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} Double(123) input: float pointing format.
    * 
    * {param} Object(java.util.Local) locale: Locale.
    * 
    * {example} getNullSafeDoubleLocal("123", "toRemoveString) result: 123 ...
    * 
    */
	public static double getNullSafeDoubleLocal(String input, Locale locale, String ...toRemoveString) {
		if (input != null && input.isEmpty() == false) {
			String s = (String) input;
			if (toRemoveString != null) {
				for (String r : toRemoveString) {
					s = s.replace(r, "");
				}
			}
			return getNullSafeDouble(s, locale);
		} else {
			return 0;
		}
	}
	
	/**
    * return true if the first parameter is in the list of the following parameters
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} boolean | Boolean
    * 
    * {param} int(99) test: int test value.
    * 
    * {param} int(2,5,99,120) possibleValues: one or more int.
    * 
    * {example} in(25,2,4,25,66) result: true
    * 
    */
	public static boolean in(Integer test, int ... possibleValues) {
		if (test != null) {
			for (int v : possibleValues) {
				if (test.intValue() == v) {
					return true;
				}
			}
		}
		return false;
	}

	/**
    * build the sum of given Integers regardless of null
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} int | Integer
    * 
    * {param} int(2,5,99,120) integers: integers to sum.
    * 
    * {example} sum(25,2,4,25,66) result: 8889
    * 
    */
	public static int sum(Integer ...integers) {
		int sum = 0;
		for (Integer i : integers) {
			if (i != null) {
				sum = sum + i.intValue();
			}
		}
		return sum;
	}
	
	/**
    * build the division of given Doubles regardless of null
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} double(2.5) dividend: dividend
    * 
    * {param} double(4.3) quotient: quotient
    * 
    * {example} div(2,4) result: 0.5
    * 
    */
	public static double div(Double dividend, Double quotient) {
		if (dividend == null || quotient == null) {
			return 0;
		} else {
			if (quotient.doubleValue() == 0) {
				return 0;
			} else {
				return dividend.doubleValue() / quotient.doubleValue();
			}
		}
	}
	
	/**
    * build the division of given Doubles regardless of null
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} double(2.5) dividend: dividend
    * 
    * {param} int(4.3) quotient: quotient
    * 
    * {example} div(2,4) result: 0.5
    * 
    */
	public static double div(Double dividend, Integer quotient) {
		if (dividend == null || quotient == null) {
			return 0;
		} else {
			if (quotient.doubleValue() == 0) {
				return 0;
			} else {
				return dividend.doubleValue() / (double) quotient.intValue();
			}
		}
	}

	/**
    * build the division of given Doubles regardless of null
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} double(2.5) dividend: dividend
    * 
    * {param} int(4.3) quotient: quotient
    * 
    * {example} div(2,4) result: 0.5
    * 
    */
	public static double div(Integer dividend, Integer quotient) {
		if (dividend == null || quotient == null) {
			return 0;
		} else {
			if (quotient.doubleValue() == 0) {
				return 0;
			} else {
				return (double) dividend.intValue() / quotient.intValue();
			}
		}
	}

	/**
    * build the division of given Doubles regardless of null
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} double(2.5) dividend: dividend
    * 
    * {param} int(4.3) quotient: quotient
    * 
    * {example} div(2,4) result: 0.5
    * 
    */
	public static double div(Integer dividend, Double quotient) {
		if (dividend == null || quotient == null) {
			return 0;
		} else {
			if (quotient.doubleValue() == 0) {
				return 0;
			} else {
				return ((double) dividend.intValue()) / quotient.doubleValue();
			}
		}
	}

	/**
    * formats the number
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} String
    * 
    * {param} double(2.5) number: number to format
    * 
    * {example} numberToPercent2Scale(0.12345) result: 12,34%
    * 
    */
	public static String formatToPercent2Scale(Double number) {
		if (number != null) {
			return formatToString(roundScale2(number * 100d)) + "%";
		} else {
			return "";
		}
	}

	/**
    * formats the number
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} String
    * 
    * {param} double(2.5) number: number to format
    * 
    * {example} numberToString(1234.12345) result: "1.234,12345"
    * 
    */
	public static String formatToString(Double number) {
		if (number != null) {
			NumberFormat nf = NumberFormat.getInstance(Locale.GERMANY);
			return nf.format(number);
		} else {
			return "";
		}
	}
	
	/**
    * formats the number
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} String
    * 
    * {param} double(2.5) number: number to format
    * 
    * {example} numberToStringRoundScale2(1234.12345) result: "1.234,12"
    * 
    */
	public static String formatToStringRoundScale2(Double number) {
		if (number != null) {
			NumberFormat nf = NumberFormat.getInstance(Locale.GERMANY);
			return nf.format(roundScale2(number));
		} else {
			return "";
		}
	}

	/**
    * formats the number
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} String
    * 
    * {param} double(2.5) number: number to format
    * 
    * {example} numberToString(1234) result: 1.234
    * 
    */
	public static String formatToString(Integer number) {
		if (number != null) {
			NumberFormat nf = NumberFormat.getInstance(Locale.GERMANY);
			return nf.format(number);
		} else {
			return "";
		}
	}
	
	/**
    * rounds the number to a value with 2 decimal digits
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} double | Double
    * 
    * {param} double(2.5) number: number to format
    * 
    * {example} numberToString(1234.12345) result: 1234.12
    * 
    */
	public static Double roundScale2(Double number) {
		if (number != null) {
			return Math.round(number * 100d) / 100d;
		} else {
			return null;
		}
	}
	
	/**
    * rounds the number to a value with 2 decimal digits
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} BigDecimal
    * 
    * {param} BigDecimal(2.5) number: number to format
    * 
    * {example} formatToString(new BigDecimal("1234.12345"), 6) result: 1234.123450
    * 
    */
	public static String formatToString(BigDecimal number, int scale) {
		return number.setScale(scale, BigDecimal.ROUND_HALF_DOWN).toPlainString();
	}
	
	/**
    * return float and accepts Float and String. Null returns 0
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} float | Float
    * 
    * {param} Float(123) input: float pointing format.
    * 
    * {example} getNullSafeFloat("123.4") result: 123.4 ...
    * 
    */
	public static float getNullSafeFloat(Object input) {
		if (input instanceof Float) {
			Float i = (Float) input;
			if (i == 0) {
				return 0;
			} else {
				return i.floatValue();
			}
		} else if (input instanceof String) {
			String s = (String) input;
			if (s == null || s.isEmpty()) {
				return 0;
			}
			return Float.parseFloat((String) s);
		} else {
			return 0;
		}
	}
	
	/**
    * returns the given text as integer or 0 otherwise
    * 
    * {Category} NumberUtil
    * 
    * {talendTypes} int | Integer
    * 
    * {param} String("123") text: string.
    * 
    * {example} parseNullSafeInt("AB54") result: 0
    * 
    */
	public static int parseNullSafeInt(String text) {
		try {
			return Integer.parseInt(text);
		} catch (Exception e) {
			return 0;
		}
	}
	
	/**
	* returns the given text as long or 0 otherwise
	* 
	* {Category} NumberUtil
	* 
	* {talendTypes} long | Long
	* 
	* {param} String("123") text: string.
	* 
	* {example} parseNullSafeLong("AB54") result: 0
	* 
	*/
	public static long parseNullSafeLong(String text) {
		try {
			return Long.parseLong(text);
		} catch (Exception e) {
			return 0;
		}
	}
	
	/**
	 * returns true, if elements contains at least one an element x so that lowerBound <= x <= upperBound
	 * 
	 * @param elements   elements to check
	 * @param lowerBound lower range bound
	 * @param upperBound upper range bound
	 * @return see above
	 */
	public static boolean collectionElementInRange(Iterable<Integer> elements, int lowerBound, int upperBound) {
		for(Integer i : elements)
			if(lowerBound <= i && i <= upperBound)
				return true;
		return false;
	}
}
