package kembe.sim;

import fj.F;
import kembe.Time;
import kembe.sim.rand.DoubleFromZeroIncToOne;
import kembe.sim.rand.Rand;
import org.joda.time.*;

import java.util.Random;

public abstract class RandWait {


    public static RandWait waitUntil(final Instant instant) {
        return new RandWait() {
            @Override public Rand<Instant> after(final Instant i) {
                return new Rand<Instant>() {
                    @Override public Instant next(Random t) {
                        return instant;
                    }
                };
            }
        };
    }

    public static RandWait waitUntil(final LocalTime localTime) {
        return new RandWait() {
            @Override public Rand<Instant> after(final Instant instant) {
                return new Rand<Instant>() {
                    @Override public Instant next(Random t) {
                        return Time.next( localTime, instant ).toInstant();
                    }
                };
            }
        };
    }

    public static RandWait waitUntilBetween(final LocalTime from, final LocalTime to) {
        return new RandWait() {
            @Override public Rand<Instant> after(final Instant instant) {

                return within( Time.from( Time.next( from, instant ) ).until( Time.next( to, instant ) ) );
            }
        };
    }

    public static RandWait waitFor(final Duration dur) {
        return new RandWait() {
            @Override public Rand<Instant> after(final Instant i) {
                return new Rand<Instant>() {
                    @Override public Instant next(Random t) {
                        return i.plus( dur );
                    }
                };
            }
        };
    }

    public static RandWait waitFor(final ReadablePeriod p){
        return waitFor( p.toPeriod().toStandardDuration() );
    }

    public static RandWait waitForBetween(final Duration min, final Duration max) {
        return new RandWait() {
            @Override public Rand<Instant> after(final Instant i) {
                return within( Time.from( i.plus( min ) ).until( i.plus( max ) ) );
            }
        };
    }

    public static RandWait waitForAtLeast(final Duration dur) {
        return new RandWait() {
            @Override public Rand<Instant> after(final Instant i) {
                return within( Time.from( i ).lasting( dur ) );
            }
        };
    }
    public static RandWait waitForAtLeast(final ReadablePeriod p){
        return waitForAtLeast( p.toPeriod().toStandardDuration() );
    }

    private static Rand<Instant> within(final Interval interval) {
        Rand<Instant> rg = new Rand<Instant>() {

            @Override public Instant next(Random random) {
                return Rand.randomDouble().map( new F<DoubleFromZeroIncToOne, Instant>() {
                    @Override
                    public Instant f(DoubleFromZeroIncToOne d) {
                        long fraction = (long) (interval.toDurationMillis() * d.value);
                        return new Instant( interval.getStartMillis() + fraction );
                    }
                } ).next( random );
            }
        };

        return rg;
    }

    public abstract Rand<Instant> after(Instant instant);
}

@@ -1230,33 +1230,93 @@
     
-return within( Time.from(
+DateTime fromInstant = Time.next( from, instant );%0A                DateTime todayTo =
  Tim
@@ -1315,36 +1315,34 @@
 To = Time.next( 
-from
+to
 , instant ) ).un
@@ -1340,42 +1340,262 @@
 nt )
- ).until( Time.next
+;%0A                DateTime toInstant = todayTo.isAfter( fromInstant )%0A                                     ? todayTo%0A                                     : todayTo.plusDays( 1 );%0A%0A                return within( Time.from( fromInstant ).until
 ( to
-, i
+I
 nstant )
  ) )
@@ -1590,18 +1590,16 @@
 nstant )
- )
  );%0A    
@@ -2059,32 +2059,33 @@
 eadablePeriod p)
+ 
 %7B%0A        return
@@ -2693,24 +2693,25 @@
    %7D;%0A    %7D%0A
+%0A
     public s
@@ -2763,16 +2763,17 @@
 eriod p)
+ 
 %7B%0A      

Fix bug in RandWait.waitUntilBetween