package weatherhandler.processor;

import java.util.List;
import java.util.function.Function;
import java.util.function.BiConsumer;

import weatherhandler.data.Measurement;
import weatherhandler.data.MeasurementsCache;

public class CompleteMissingProcessor implements Processor {
    private int cacheSize;
    private Processor inner;

    public CompleteMissingProcessor(int cacheSize, Processor inner) {
        this.cacheSize = cacheSize;
        this.inner = inner;
    }

    /**
     * Takes an average from a list using the given map function.
     * Using floats here because they can be coerced to ints and we don't want
     * the same method twice _everywhere_.
     */
    private float average(List<Measurement> sample, Function<Measurement, Float> mapper) {
        float average = 0.0f;
        for (Measurement m : sample) {
            average += mapper.apply(m);
        }
        return (average / sample.size());
    }

    /**
     * Compares one field of the given measurement to the previous sample by
     * applying a map function to all of them, and then updates the field if
     * it seems out of place using the given consumer function.
     */
    private void replaceIfNecessary(
        Measurement m,
        List<Measurement> sample,
        Function<Measurement, Float> mapper,
        BiConsumer<Measurement, Float> consumer
    ) {
        float avg = this.average(sample, mapper);
        Float value = mapper.apply(m);
        if (value == null) {
            consumer.accept(m, avg);
            return;
        }
        float d = Math.abs(value - avg);
        if (d / value > 0.2 /* 20% */) {
            // set to average initially
            consumer.accept(m, avg);
            return;
        }
    }

    public void processMeasurements(List<Measurement> measurements) throws ProcessorException {
        for (Measurement m : measurements) {
            List<Measurement> prev = MeasurementsCache.getCache(m.getStation());
            synchronized(prev) {
                // TODO clean this up slightly, somehow…
                this.replaceIfNecessary(m, prev,
                    p -> p.getTemperature(),
                    (measurement, temp) -> measurement.setTemperature(temp));
                this.replaceIfNecessary(m, prev,
                    p -> p.getDewPoint(),
                    (measurement, dewp) -> measurement.setDewPoint(dewp));
                this.replaceIfNecessary(m, prev,
                    p -> p.getStationPressure(),
                    (measurement, stp) -> measurement.setStationPressure(stp));
                this.replaceIfNecessary(m, prev,
                    p -> p.getSeaLevelPressure(),
                    (measurement, slp) -> measurement.setSeaLevelPressure(slp));
                this.replaceIfNecessary(m, prev,
                    p -> p.getVisibility(),
                    (measurement, vis) -> measurement.setVisibility(vis));
                this.replaceIfNecessary(m, prev,
                    p -> p.getWindSpeed(),
                    (measurement, wind) -> measurement.setWindSpeed(wind));
                this.replaceIfNecessary(m, prev,
                    p -> p.getPrecipitation(),
                    (measurement, prec) -> measurement.setPrecipitation(prec));
                this.replaceIfNecessary(m, prev,
                    p -> p.getSnowDepth(),
                    (measurement, snow) -> measurement.setSnowDepth(snow));
                this.replaceIfNecessary(m, prev,
                    p -> p.getCloudCover(),
                    // https://www.youtube.com/watch?v=9GOCwqSeRLs
                    (measurement, clc) -> measurement.setCloudCover(clc));
                // Wind Direction is stored as an integer, so we "floatify" it
                // when mapping and round it to an integer when storing it.
                this.replaceIfNecessary(m, prev,
                    p -> (float) p.getWindDirection(),
                    (measurement, dir) -> measurement.setWindDirection(Math.round(dir)));

                prev.add(m);
                if (prev.size() > this.cacheSize) {
                    prev.remove(0);
                }
            }
        }
        inner.processMeasurements(measurements);
    }
}

@@ -2004,65 +2004,8 @@
 ) %7B%0A
-                // TODO clean this up slightly, somehow%E2%80%A6%0A
     
@@ -2069,23 +2069,29 @@
         
-p -%3E p.
+Measurement::
 getTempe
@@ -2100,67 +2100,23 @@
 ture
-(),%0A                    (measurement, temp) -%3E measurement.
+, Measurement::
 setT
@@ -2129,14 +2129,8 @@
 ture
-(temp)
 );%0A 
@@ -2201,74 +2201,35 @@
     
-p -%3E p.getDewPoint(),%0A                    (measurement, dewp) -%3E m
+Measurement::getDewPoint, M
 easu
@@ -2234,17 +2234,18 @@
 surement
-.
+::
 setDewPo
@@ -2251,14 +2251,8 @@
 oint
-(dewp)
 );%0A 
@@ -2315,31 +2315,37 @@
             
-p -%3E p.
+Measurement::
 getStationPr
@@ -2354,66 +2354,23 @@
 sure
-(),%0A                    (measurement, stp) -%3E measurement.
+, Measurement::
 setS
@@ -2387,13 +2387,8 @@
 sure
-(stp)
 );%0A 
@@ -2451,31 +2451,37 @@
             
-p -%3E p.
+Measurement::
 getSeaLevelP
@@ -2491,66 +2491,23 @@
 sure
-(),%0A                    (measurement, slp) -%3E measurement.
+, Measurement::
 setS
@@ -2525,13 +2525,8 @@
 sure
-(slp)
 );%0A 
@@ -2597,86 +2597,49 @@
     
-p -%3E p.getVisibility(),%0A                    (measurement, vis) -%3E measurement.
+Measurement::getVisibility, Measurement::
 setV
@@ -2651,13 +2651,8 @@
 lity
-(vis)
 );%0A 
@@ -2723,75 +2723,36 @@
     
-p -%3E p.getWindSpeed(),%0A                    (measurement, wind) -%3E m
+Measurement::getWindSpeed, M
 easu
@@ -2753,25 +2753,26 @@
  Measurement
-.
+::
 setWindSpeed
@@ -2775,14 +2775,8 @@
 peed
-(wind)
 );%0A 
@@ -2843,23 +2843,29 @@
         
-p -%3E p.
+Measurement::
 getPreci
@@ -2876,67 +2876,23 @@
 tion
-(),%0A                    (measurement, prec) -%3E measurement.
+, Measurement::
 setP
@@ -2907,14 +2907,8 @@
 tion
-(prec)
 );%0A 
@@ -2979,75 +2979,36 @@
     
-p -%3E p.getSnowDepth(),%0A                    (measurement, snow) -%3E m
+Measurement::getSnowDepth, M
 easu
@@ -3013,17 +3013,18 @@
 surement
-.
+::
 setSnowD
@@ -3031,14 +3031,8 @@
 epth
-(snow)
 );%0A 
@@ -3103,142 +3103,37 @@
     
-p -%3E p.getCloudCover(),%0A                    // https://www.youtube.com/watch?v=9GOCwqSeRLs%0A                    (measurement, clc) -%3E m
+Measurement::getCloudCover, M
 easu
@@ -3138,17 +3138,18 @@
 surement
-.
+::
 setCloud
@@ -3157,13 +3157,8 @@
 over
-(clc)
 );%0A 

Simplify replaceIfNecessary calls using Method References