StatisticUtil.java

package ntnu.idatt2016.v233.SmartMat.util;

import ntnu.idatt2016.v233.SmartMat.entity.group.Waste;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;


/**
 * A utility class for calculating statistics related to waste and CO2 emissions.
 * @author Pedro Cardona
 * @version 1.0
 */
public class StatisticUtil {

    private final static double CO2_KG_MEAT = (99.48 + 39.72 + 33.3 + 26.87 + 12.31 + 9.87 )/6.0;
    private final static double CO2_KG_DAIRY = (23.88 + 4.67 + 3.15)/3;
    private final static double CO2_KG_BAKED = (4.45 + 1.7 + 1.57)/3;
    private final static double CO2_KG_VEGETABLE = (2.09 + 0.98 + 0.86 + 0.46)/4;
    private final static double CO2_KG_OTHER = (CO2_KG_MEAT + CO2_KG_DAIRY  + CO2_KG_BAKED + CO2_KG_VEGETABLE)/4.0;


    /**
     * Calculates the annual average amount of CO2 emissions per person based on a list of waste objects and a number of persons.
     * @param wastes a list of waste objects
     * @param number the number of persons
     * @return the annual average amount of CO2 emissions per person
     */
    public static double getAnnualAverage(List<Waste> wastes, int number){
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        List<Double> CO2 = new ArrayList<>();
        for(Waste waste: wastes){
            if(waste.getTimestamp().before(timestamp)) timestamp = waste.getTimestamp();
            CO2.add(getCO2ByCategory(waste.getEan().getCategory().getCategoryName()) * getCorrectRelation(waste.getEan().getUnit()) * waste.getAmount());
        }
        return getAnnualAveragePerPerson(CO2,number,timestamp);
    }


    /**
     * Returns the amount of CO2 emissions per kg for a given category of waste.
     * @param category the waste category
     * @return the amount of CO2 emissions per kg
     */
    private static double getCO2ByCategory(String category){
        switch (category){
            case "meat, fish and chicken" -> {
                return CO2_KG_MEAT;
            }
            case "baked goods and grains" -> {
                return CO2_KG_BAKED;
            }
            case "dairy and egg" ->{
                return CO2_KG_DAIRY;
            }
            case "fruit and vegetables" ->{
                return CO2_KG_VEGETABLE;
            }
            default -> {
                return CO2_KG_OTHER;
            }
        }
    }

    /**
     * Returns the correct CO2 relation for a given unit of waste.
     * @param unit the unit of waste
     * @return the correct CO2 relation
     */
    private static double getCorrectRelation(String unit){
        switch (unit){
            case "l" -> {
                return 0.998;
            }
            case "kg" -> {
                return 1.0;
            }
            case "g" -> {
                return 0.001;
            }
            case "ml" -> {
                return 0.000998;
            }
            case "cl" -> {
                return 0.00998;
            }
            case "dl" -> {
                return 0.0998;
            }
            default -> {
                return 0.1;
            }
        }
    }

    /**
     * Get the annual average amount of CO2 emissions per person.
     *
     * @param co2List The list of CO2 emissions for a group of people.
     * @param numberOfPerson The number of people in the group.
     * @param firstDate The date on which the group started tracking their CO2 emissions.
     * @return The annual average amount of CO2 emissions per person.
     */
    private static double getAnnualAveragePerPerson(List<Double> co2List, int numberOfPerson, Timestamp firstDate) {
        java.util.Date currentDate = new java.util.Date();
        long diffInMillis = currentDate.getTime() - firstDate.getTime();
        long diffInDays = diffInMillis / (24 * 60 * 60 * 1000);
        double co2Sum = 0.0;
        for (double c02 : co2List) co2Sum += c02;
        if(diffInDays >= 365){
            return co2Sum / (((double) (diffInDays +1) / 365.0) * (double) numberOfPerson);
        }else {
            return (118.0/((double) diffInDays +1.0)) + (( co2Sum / (((double) (diffInDays +1) / 365.0) * (double) numberOfPerson))/(365.0-(double) diffInDays));
        }
    }

    /**
     * Get the number of waste items for each category name in a list of waste items.
     *
     * @param wastes The list of waste items for which to calculate the number of items by category name.
     * @return An array of doubles representing the number of waste items for each category name.
     */
    public static double[] getNumberOfWasteByCategoryName(List<Waste> wastes) {
        double[] numberOfWastes = new double[5];
        for (Waste waste : wastes) {
            switch (waste.getEan().getCategory().getCategoryName()) {
                case "meat, fish and chicken" -> numberOfWastes[0] += waste.getAmount();
                case "baked goods and grains" -> numberOfWastes[1] += waste.getAmount();
                case "dairy and egg" -> numberOfWastes[2] += waste.getAmount();
                case "fruit and vegetables" -> numberOfWastes[3] += waste.getAmount();
                default -> numberOfWastes[4] += waste.getAmount();
            }
        }
        return numberOfWastes;
    }

    /**
     * Calculates the sum of waste for each of the four previous months.
     * The amount of waste is converted to kilograms if the unit is in liters, grams, milliliters, centiliters, or deciliters.
     *
     * @param wastes the list of wastes to calculate the sum from
     * @return an array of four doubles representing the sum of waste for each of the last four months,
     *         in the same order as the months are counted backwards from the current month
     */
    public static double[] getNumberOfWasteByLastMonth(List<Waste> wastes){
        double[] result = new double[4];
        HashMap<Integer,List<Waste>> hashMap = new HashMap<>();

        LocalDate localDate = LocalDate.now();
        int currentMonth = localDate.getMonthValue();
        int currentYear = localDate.getYear();
        for(Waste waste : wastes){
            LocalDate localDate1 = waste.getTimestamp().toLocalDateTime().toLocalDate();
            if(currentMonth == localDate1.getMonthValue() && currentYear == localDate1.getYear()){
                hashMap.computeIfAbsent(0, k -> new ArrayList<>());
                hashMap.get(0).add(waste);
            }
            if(Math.abs((currentMonth-1) % 12) == localDate1.getMonthValue() && currentYear == localDate1.getYear()){
                hashMap.computeIfAbsent(1, k -> new ArrayList<>());
                hashMap.get(1).add(waste);
            }
            if(Math.abs((currentMonth-2) % 12) == localDate1.getMonthValue() && currentYear == localDate1.getYear()){
                hashMap.computeIfAbsent(2, k -> new ArrayList<>());
                hashMap.get(2).add(waste);
            }
            if(Math.abs((currentMonth-3) % 12) == localDate1.getMonthValue() && currentYear == localDate1.getYear()){
                hashMap.computeIfAbsent(3, k -> new ArrayList<>());
                hashMap.get(3).add(waste);
            }
        }
        for(int i = 0; i < 4; i++){
            result[i] = getSumOfWaste(hashMap.get(i));
        }
        return result;
    }

    /**
     * Calculates the sum of waste for a list of wastes, and converts the amounts to kilograms if the unit is in liters,
     * grams, milliliters, centiliters, or deciliters.
     *
     * @param wastes the list of wastes to calculate the sum from
     * @return the sum of waste in kilograms
     */
    private static double getSumOfWaste(List<Waste> wastes){
        double sum = 0.0;
        if(wastes == null){
            wastes = new ArrayList<>();
        }
        for(Waste waste: wastes){
            sum += getCorrectRelation(waste.getEan().getUnit()) * waste.getAmount();
        }
        return sum;
    }

    /**
     * Calculates the total amount of money lost due to expired or discarded items in the last 30 days,
     * based on the list of wastes passed as a parameter.
     *
     * @param wastes the list of wastes to be analyzed
     * @return the total amount of money lost due to expired or discarded items in the last 30 days
     */
    public static double getLostMoneyInLastMonth(List<Waste> wastes){
        List<Waste> wasteList = new ArrayList<>();
        LocalDateTime time = LocalDateTime.now().minusDays(30);
        for(Waste waste: wastes){
            if(waste.getTimestamp().after(Timestamp.valueOf(time))){
                wasteList.add(waste);
            }
        }
        return lostMoney(wasteList);
    }

    /**
     * Calculates the total amount of money lost due to expired or discarded items,
     * based on the list of wastes passed as a parameter.
     *
     * @param wastes the list of wastes to be analyzed
     * @return the total amount of money lost due to expired or discarded items
     */
    private static double lostMoney(List<Waste> wastes){
        double sum = 0.0;
        for(Waste waste: wastes){
            sum += waste.getAmount()*waste.getBuyPrice();
        }
        return sum;
    }

}