package com.cronutils.model;

import com.cronutils.model.field.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.util.List;
import java.util.Map;

class ExecutionTime {

    private Map<CronFieldName, List<Integer>> executionTimes;

    private ExecutionTime(Map<CronFieldName, CronField> fields){
        this.executionTimes = Maps.newHashMap();
    }

    public static ExecutionTime forCron(Cron cron){
        return new ExecutionTime(cron.retrieveFieldsAsMap());
    }

//    public DateTime afterDate(DateTime date){
//    }
//
//    public DateTime beforeDate(DateTime date){
//    }

    private List<Integer> fromFieldToTimeValues(FieldExpression fieldExpression, int max){
        if(fieldExpression instanceof And){
            return fromFieldToTimeValues((And)fieldExpression, max);
        }
        if(fieldExpression instanceof Between){
            return fromFieldToTimeValues((Between)fieldExpression, max);
        }
        if(fieldExpression instanceof On){
            return fromFieldToTimeValues((On)fieldExpression, max);
        }
        if(fieldExpression instanceof Always){
            return fromFieldToTimeValues((Always)fieldExpression, max);
        }
        return Lists.newArrayList();
    }

    private List<Integer> fromFieldToTimeValues(And fieldExpression, int max){
        List<Integer> values = Lists.newArrayList();
        for(FieldExpression expression : fieldExpression.getExpressions()){
            values.addAll(fromFieldToTimeValues(expression, max));
        }
        return values;
    }

    private List<Integer> fromFieldToTimeValues(Between fieldExpression, int max){
        List<Integer> values = Lists.newArrayList();
        int every = fieldExpression.getEvery().getTime();
        for(int j = fieldExpression.getFrom(); j < fieldExpression.getTo() + 1; j+=every){
            values.add(j);
        }
        return values;
    }

    private List<Integer> fromFieldToTimeValues(On fieldExpression, int max){
        List<Integer> values = Lists.newArrayList();
        values.add(fieldExpression.getTime());
        return values;
    }

    private List<Integer> fromFieldToTimeValues(Always fieldExpression, int max){
        List<Integer> values = Lists.newArrayList();
        int every = fieldExpression.getEvery().getTime();
        for(int j = 1; j <= max; j+=every){
            values.add(j);
        }
        return values;
    }
}

@@ -142,54 +142,464 @@
 ps;%0A
-%0Aimport java.util.List;%0Aimport java.util.Map;%0A
+import org.joda.time.DateTime;%0A%0Aimport java.util.Collections;%0Aimport java.util.Iterator;%0Aimport java.util.List;%0Aimport java.util.Map;%0A%0A//Approach 1: %5BDiscarded%5D brute force, iterate over all possible future dates until finding first matching cron.%0A//Approach 1: %5B...%5D precalculate possible values for each field and take nearest ones%0A//Approach 2: %5B...%5D nearestValue(FieldExpression, int timeValue): return plus -%3E method to retrieve value for field an sum
 %0Acla
@@ -780,24 +780,66 @@
 s = 
-Maps.newHashMap(
+Collections.unmodifiableMap(fieldsToExecutionTimes(fields)
 );%0A 
@@ -1005,32 +1005,54 @@
 DateTime date)%7B%0A
+//        date.plus()%0A
 //    %7D%0A//%0A//   
@@ -1187,32 +1187,85 @@
 sion, int max)%7B%0A
+        List%3CInteger%3E values = Lists.newArrayList();%0A
         if(field
@@ -1296,38 +1296,40 @@
 d)%7B%0A            
-return
+values =
  fromFieldToTime
@@ -1425,38 +1425,40 @@
 n)%7B%0A            
-return
+values =
  fromFieldToTime
@@ -1553,38 +1553,40 @@
 n)%7B%0A            
-return
+values =
  fromFieldToTime
@@ -1688,22 +1688,24 @@
         
-return
+values =
  fromFie
@@ -1772,35 +1772,55 @@
     
-return Lists.newArrayList()
+Collections.sort(values);%0A        return values
 ;%0A  
@@ -3001,11 +3001,1141 @@
 s;%0A    %7D
+%0A%0A    private int getMaxForCronField(CronFieldName cronFieldName)%7B%0A        switch (cronFieldName)%7B%0A            case YEAR:%0A                return DateTime.now().getYear() + 60;%0A            case MONTH:%0A                return 12;%0A            case DAY_OF_MONTH:%0A                return 31;%0A            case DAY_OF_WEEK:%0A                return 7;%0A            default:%0A                return 60;%0A        %7D%0A    %7D%0A%0A    private Map%3CCronFieldName, List%3CInteger%3E%3E fieldsToExecutionTimes(Map%3CCronFieldName, CronField%3E fields)%7B%0A        Map%3CCronFieldName, List%3CInteger%3E%3E executionTimes = Maps.newHashMap();%0A        Iterator%3CMap.Entry%3CCronFieldName, CronField%3E%3E iterator =%0A                fields.entrySet().iterator();%0A        while(iterator.hasNext())%7B%0A            Map.Entry%3CCronFieldName, CronField%3E entry = iterator.next();%0A            executionTimes.put(%0A                    entry.getKey(),%0A                    fromFieldToTimeValues(%0A                            entry.getValue().getExpression(),%0A                            getMaxForCronField(entry.getKey())%0A                    )%0A            );%0A        %7D%0A        return executionTimes;%0A    %7D
 %0A%7D%0A

Document approaches for ExecutionTime