package cz.senslog.analytics.utils.validator;


import cz.senslog.analytics.domain.AttributeName;
import cz.senslog.analytics.domain.ValidationResult;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class OnChangeNotifyTrigger implements NotifyTrigger {

    private static final class CacheWrapper {
        private final ValidationResult[] resultsArr;
        private int freeIndex;

        private CacheWrapper(ValidationResult[] resultsArr, int freeIndex) {
            this.resultsArr = resultsArr;
            this.freeIndex = freeIndex;
        }
    }

    private static final int CACHE_LEN = 2;

    private final Map<AttributeName, CacheWrapper> cacheMap;

    private List<ValidationResult> tempToNotify;

    public OnChangeNotifyTrigger() {
        this.cacheMap = new HashMap<>();
    }

    @Override
    public void accept(ValidationResult validationResult) {
        AttributeName attr = validationResult.attribute();
        if (!cacheMap.containsKey(attr)) {
            cacheMap.put(attr, new CacheWrapper(new ValidationResult[CACHE_LEN], 0));
        }
        CacheWrapper cache = cacheMap.get(attr);
        cache.resultsArr[cache.freeIndex] = validationResult;
        cache.freeIndex = nextIndex(cache.freeIndex);
    }

    private static int nextIndex(int index) {
        return  (index + 1) % CACHE_LEN;
    }

    private static int previousIndex(int index) {
        return (index <= 0 ? CACHE_LEN : index) - 1;
    }

    @Override
    public boolean shouldNotify() {
        boolean notify = false;
        tempToNotify = new ArrayList<>(cacheMap.size());
        for (CacheWrapper cache : cacheMap.values()) {
            int currentDataInd = previousIndex(cache.freeIndex);
            ValidationResult currentRes = cache.resultsArr[currentDataInd];
            ValidationResult previousRes = cache.resultsArr[previousIndex(currentDataInd)];
            if ((previousRes == null || previousRes.isValid()) && currentRes.isNotValid()) {
                notify = true; // pass the threshold
                tempToNotify.add(currentRes);
            } else if(currentRes.isValid() && (previousRes != null && previousRes.isNotValid())) {
                notify = true; // leave the threshold
                tempToNotify.add(currentRes);
            }
        }
        return notify;
    }

    @Override
    public ValidationResult[] resultsToNotify() {
        if (tempToNotify == null) { return new ValidationResult[0]; }
        ValidationResult[] res = tempToNotify.toArray(new ValidationResult[0]);
        tempToNotify = null;
        return res;
    }

}
