package com.infinite.focus.server.utils;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

import com.infinite.focus.server.dashboard.TimeLine;

public class DateUtils {

	public static final SimpleDateFormat dayWitTimeFormat = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss", Locale.getDefault());
	public static final SimpleDateFormat dailyDateFormat = new SimpleDateFormat("MMM dd, yyyy", Locale.getDefault());
	public static final SimpleDateFormat weeklyDateFormat = new SimpleDateFormat("MMM dd", Locale.getDefault());
	public static final SimpleDateFormat monthlyDateFormat = new SimpleDateFormat("MMM yyyy", Locale.getDefault());
	public static final SimpleDateFormat yearlyDateFormat = new SimpleDateFormat("yyyy", Locale.getDefault());

	public static Date getToDateByTimeLine(TimeLine timeLine) {

		Date toDate = new Date();

		switch (timeLine) {
		case DAILY: {
			toDate = getDailyDate();
			break;
		}
		case WEEKLY: {
			toDate = getWeeklyDate();
			break;
		}
		case MONTHLY: {
			toDate = getMonthlyDate();

			break;
		}
		case YEARLY: {
			toDate = getYearlyDate();
			break;
		}
		}

		return toDate;
	}

	public static Date getDailyDate() {
		Calendar c = Calendar.getInstance();
		c.add(Calendar.DAY_OF_YEAR, -14);
		Date startDate = c.getTime();
		return startDate;
	}

	public static Date getWeeklyDate() {
		Calendar c = Calendar.getInstance();
		Date date = new Date();
		c.setTime(date);
		int i = c.get(Calendar.DAY_OF_WEEK) - c.getFirstDayOfWeek();
		c.add(Calendar.DATE, -i - (7 * 5));
		Date startDate = c.getTime();
		return startDate;
	}

	public static Date getMonthlyDate() {
		Calendar c = Calendar.getInstance();
		Date date = new Date();
		c.setTime(date);
		c.set(Calendar.DATE, 1);
		c.add(Calendar.MONTH, -5);
		Date startDate = c.getTime();
		return startDate;
	}

	public static Date getYearlyDate() {
		Calendar c = Calendar.getInstance();
		Date date = new Date();
		c.setTime(date);
		c.set(Calendar.DATE, 1);
		c.add(Calendar.YEAR, -4);
		Date startDate = c.getTime();
		return startDate;
	}

	public static List<String> getLabelsByTimeLine(TimeLine timeLine) {

		List<String> labels = new ArrayList<>();

		switch (timeLine) {
		case DAILY: {
			labels = getDailyLabels();
			break;
		}
		case WEEKLY: {
			labels = getWeeklyLabels();
			break;
		}
		case MONTHLY: {
			labels = getMonthlyLabels();

			break;
		}
		case YEARLY: {
			labels = getYearlyLabels();
			break;
		}
		}

		return labels;
	}
	
	public static List<String> getDailyLabels() {

		List<String> labels = new ArrayList<>();

		Calendar c = Calendar.getInstance();

		for (int i = 0; i < 15; i++) {
			Date date = new Date();
			c.setTime(date);
			c.add(Calendar.DAY_OF_YEAR, -i);
			labels.add(dailyDateFormat.format(c.getTime()));
		}

		return labels;
	}

	public static String getWeekendByDate(Date date) {
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		int x = c.get(Calendar.DAY_OF_WEEK) - c.getFirstDayOfWeek();
		c.add(Calendar.DATE, -x);
		Date startDate = c.getTime();
		c.add(Calendar.DATE, +6);
		Date endDate = c.getTime();
		String weekend = weeklyDateFormat.format(startDate) + " - " + weeklyDateFormat.format(endDate);
		return weekend;
	}

	public static final List<String> getWeeklyLabels() {

		List<String> labels = new ArrayList<>();

		Calendar c = Calendar.getInstance();

		for (int i = 0; i < 6; i++) {
			Date date = new Date();
			c.setTime(date);
			int x = c.get(Calendar.DAY_OF_WEEK) - c.getFirstDayOfWeek();
			c.add(Calendar.DATE, -x - (7 * i));
			Date startDate = c.getTime();
			c.add(Calendar.DATE, +6);
			Date endDate = c.getTime();
			if (labels.isEmpty()) {
				labels.add(weeklyDateFormat.format(startDate) + " - " + weeklyDateFormat.format(endDate));
			} else {
				labels.add(weeklyDateFormat.format(startDate) + " - " + weeklyDateFormat.format(endDate));
			}
		}

		return labels;
	}

	public static List<String> getMonthlyLabels() {

		List<String> labels = new ArrayList<>();

		Calendar c = Calendar.getInstance();
		c.set(Calendar.DATE, 1);

		for (int i = 0; i < 6; i++) {
			Date date = new Date();
			c.setTime(date);
			c.add(Calendar.MONTH, -i);
			labels.add(monthlyDateFormat.format(c.getTime()));
		}

		return labels;
	}

	public static List<String> getYearlyLabels() {

		List<String> labels = new ArrayList<>();

		Calendar c = Calendar.getInstance();
		c.set(Calendar.DATE, 1);
		for (int i = 0; i < 5; i++) {
			Date date = new Date();
			c.setTime(date);
			c.add(Calendar.YEAR, -i);
			labels.add(yearlyDateFormat.format(c.getTime()));
		}

		return labels;
	}
	
	public static int getDayCountByFromDateAndToDate(Date fromDate, Date toDate) {
		
		Calendar c = Calendar.getInstance();
		c.setTime(setTimeToFromDate(fromDate));
		Date endDate = c.getTime();
		String day =  dailyDateFormat.format(endDate);
		//System.out.println(day);
		
		toDate = setTimeToToDate(toDate);
		
		int count = 0;
		
		while(endDate.getTime() <= toDate.getTime() ) {
			c.add(Calendar.DATE, +1);
			endDate = c.getTime();
			count++;
			//System.out.println(dailyDateFormat.format(endDate));
		}
		
		return count;
	}
	
	public static int getWeekCountByFromDateAndToDate(Date fromDate, Date toDate) {
		
		Calendar c = Calendar.getInstance();
		Date startDate  = getWeekendLastDate(fromDate);
		c.setTime(startDate);
		Date endDate = getWeekendLastDate(toDate);
		
		String weekend = weeklyDateFormat.format(startDate) + " - " + weeklyDateFormat.format(endDate);
		//System.out.println(weekend);
		int count = 0;
		//System.out.println("WEek calculation " + weeklyDateFormat.format(getWeekendFirstDate(fromDate)) + " - " + weeklyDateFormat.format(getWeekendLastDate(toDate)));

		while(startDate.getTime() <= endDate.getTime()) {
			//System.out.println(weeklyDateFormat.format(getWeekendFirstDate(startDate)) + " - " + weeklyDateFormat.format(getWeekendLastDate(startDate)));
			c.add(Calendar.DATE, +1);
			startDate = getWeekendLastDate(c.getTime());
			c.setTime(startDate);
			count++;
		}
		
		return count;
	}
	
	public static Date getWeekendFirstDate(Date date) {
		Calendar c = Calendar.getInstance();
		c.setTime(setTimeToFromDate(date));
		int x = c.get(Calendar.DAY_OF_WEEK) - c.getFirstDayOfWeek();
		c.add(Calendar.DATE, -x);
		return c.getTime();
	}
	public static Date getWeekendLastDate(Date date) {
		Calendar c = Calendar.getInstance();
		c.setTime(setTimeToFromDate(date));
		int x = c.get(Calendar.DAY_OF_WEEK) - c.getFirstDayOfWeek();
		c.add(Calendar.DATE, -x);
		c.add(Calendar.DATE, +6);
		return c.getTime();
	}
	
	public static int getMonthCountByFromDateAndToDate(Date fromDate, Date toDate) {
		Calendar c = Calendar.getInstance();
		
		c.setTime(setTimeToToDate(toDate));
		c.set(Calendar.DATE, 1);
		toDate = c.getTime();
		
		c.setTime(setTimeToFromDate(fromDate));
		c.set(Calendar.DATE, 1);
		Date endDate = c.getTime();
		
		String month =  monthlyDateFormat.format(endDate);
		//System.out.println(month);
		
		int count = 0;
		
		while(endDate.getTime() <= toDate.getTime()) {
			c.add(Calendar.MONTH, +1);
			endDate = c.getTime();
			count++;
			//System.out.println(dailyDateFormat.format(endDate));
		}
		
		return count;
	}
	
	public static int getYearCountByFromDateAndToDate(Date fromDate, Date toDate) {
		Calendar c = Calendar.getInstance();
		
		c.setTime(setTimeToToDate(toDate));
		c.set(Calendar.DATE, 1);
		c.set(Calendar.MONTH,11);
		toDate = c.getTime();
		
		c.setTime(setTimeToFromDate(fromDate));
		c.set(Calendar.DATE, 1);
		c.set(Calendar.MONTH,11);
		Date endDate = c.getTime();
		
		String month = yearlyDateFormat.format(endDate);
		//System.out.println(month);
		
		int count = 0;
		
		while(endDate.getTime() <= toDate.getTime()) {
			c.add(Calendar.YEAR, +1);
			endDate = c.getTime();
			count++;
			//System.out.println(dailyDateFormat.format(endDate));
		}
		
		return count;
	}
	
	public static Date between(Date startInclusive, Date endExclusive) {
		
	    long startMillis = startInclusive.getTime();
	    long endMillis = endExclusive.getTime();
	    long randomMillisSinceEpoch = ThreadLocalRandom
	      .current()
	      .nextLong(startMillis, endMillis);

	    return new Date(randomMillisSinceEpoch);
	}
	
	public static Date setTimeToFromDate(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}

	public static Date setTimeToToDate(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 23);
		cal.set(Calendar.MINUTE, 59);
		cal.set(Calendar.SECOND, 59);
		cal.set(Calendar.MILLISECOND, 999);
		return cal.getTime();
	}
	
	
	
	public static Period getPeriodByFromAndToDate(Date fromDate, Date toDate)
    {
        LocalDate pdate =  convertToLocalDateViaInstant(setTimeToFromDate(fromDate));
        LocalDate now = convertToLocalDateViaInstant(setTimeToToDate(toDate));
 
        Period diff = Period.between(pdate, now);
        
     System.out.printf("\nDifference is %d years, %d months and %d days old\n\n", 
                    diff.getYears(), diff.getMonths(), diff.getDays());
     
     return diff;
    }
	public static LocalDate convertToLocalDateViaInstant(Date dateToConvert) {
	    return dateToConvert.toInstant()
	      .atZone(ZoneId.systemDefault())
	      .toLocalDate();
	}
	
	/**
	 * This Method is unit tested properly for very different cases , 
	 * taking care of Leap Year days difference in a year, 
	 * and date cases month and Year boundary cases (12/31/1980, 01/01/1980 etc)
	**/

	public static int getAge(Date dateOfBirth) {

	    Calendar today = Calendar.getInstance();
	    Calendar birthDate = Calendar.getInstance();

	    int age = 0;

	    birthDate.setTime(dateOfBirth);
	    if (birthDate.after(today)) {
	        throw new IllegalArgumentException("Can't be born in the future");
	    }

	    age = today.get(Calendar.YEAR) - birthDate.get(Calendar.YEAR);

	    // If birth date is greater than todays date (after 2 days adjustment of leap year) then decrement age one year   
	    if ( (birthDate.get(Calendar.DAY_OF_YEAR) - today.get(Calendar.DAY_OF_YEAR) > 3) ||
	            (birthDate.get(Calendar.MONTH) > today.get(Calendar.MONTH ))){
	        age--;

	     // If birth date and todays date are of same month and birth day of month is greater than todays day of month then decrement age
	    }else if ((birthDate.get(Calendar.MONTH) == today.get(Calendar.MONTH )) &&
	              (birthDate.get(Calendar.DAY_OF_MONTH) > today.get(Calendar.DAY_OF_MONTH ))){
	        age--;
	    }

	    return age;
	}
	
	public static Date getFromDateWithoutTime(Date fromDate) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(fromDate);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}

	public static Date getToDateWithoutTime(Date toDate) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(toDate);
		cal.set(Calendar.HOUR_OF_DAY, 23);
		cal.set(Calendar.MINUTE, 59);
		cal.set(Calendar.SECOND, 59);
		cal.set(Calendar.MILLISECOND, 999);
		return cal.getTime();
	}
}
