package com.pr0gramm.app.ab;

import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;

import com.google.common.base.Enums;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.pr0gramm.app.Track;

import rx.functions.Action1;

/**
 */
@Singleton
public class ExperimentService {
    private final Object lock = new Object();

    private final Application application;
    private final SharedPreferences preferences;

    @Inject
    public ExperimentService(Application app) {
        this.application = app;
        this.preferences = app.getSharedPreferences("ExperimentService", Context.MODE_PRIVATE);
    }

    public <Case extends Enum<Case>, Action extends Enum<Action>>
    void participate(Experiment<Case, Action> experiment,
                     Action1<? super Case> onParticipate) {

        if (!experiment.canParticipate(application)) {
            clear(experiment);
            return;
        }

        Case selectedCase;
        synchronized (lock) {
            selectedCase = currentCaseOf(experiment).or(() -> participate(experiment));
        }

        onParticipate.call(selectedCase);
    }

    public <Case extends Enum<Case>, Action extends Enum<Action>>
    void report(Experiment<Case, Action> experiment, Action action) {
        Optional<Case> currentCase = currentCaseOf(experiment);
        if (currentCase.isPresent()) {
            String caseName = enumName(currentCase.get());
            String actionName = enumName(action);
            Track.experimentEvent(experiment.getName(), caseName, actionName);
        }
    }

    private <Case extends Enum<Case>, Action extends Enum<Action>>
    Case participate(Experiment<Case, Action> experiment) {
        ImmutableList<Case> cases = experiment.getCases();
        Case selectedCase = cases.get((int) (Math.random() * cases.size()));

        preferences.edit().putString(experiment.getName(), selectedCase.name()).apply();
        return selectedCase;
    }

    private <Case extends Enum<Case>, Action extends Enum<Action>>
    Optional<Case> currentCaseOf(Experiment<Case, Action> experiment) {
        String caseName = preferences.getString(experiment.getName(), null);
        if (caseName == null)
            return Optional.absent();

        return Enums.getIfPresent(experiment.getCaseType(), caseName);
    }

    private <Case extends Enum<Case>, Action extends Enum<Action>>
    void clear(Experiment<Case, Action> experiment) {
        synchronized (lock) {
            preferences.edit().remove(experiment.getName()).apply();
        }
    }

    /**
     * Removes all participation information for all experiments.
     */
    public void clear() {
        synchronized (lock) {
            preferences.edit().clear().apply();
        }
    }

    private static <E extends Enum<E>> String enumName(E value) {
        return value.name().toLowerCase().replace("_", " ");
    }
}

@@ -1194,17 +1194,22 @@
 r(() -%3E 
-p
+startP
 articipa
@@ -1209,17 +1209,19 @@
 rticipat
-e
+ing
 (experim
@@ -1807,17 +1807,22 @@
    Case 
-p
+startP
 articipa
@@ -1814,33 +1814,35 @@
  startParticipat
-e
+ing
 (Experiment%3CCase
@@ -1998,24 +1998,239 @@
 .size()));%0A%0A
+        // track the info, that a user starts participating in this experiment%0A        Track.experimentEvent(experiment.getName(), %22_Participating%22, enumName(selectedCase));%0A%0A        // store info for the next time%0A
         pref
@@ -2243,16 +2243,33 @@
 s.edit()
+%0A                
 .putStri
@@ -2309,24 +2309,41 @@
 Case.name())
+%0A                
 .apply();%0A  
@@ -2332,32 +2332,33 @@
       .apply();%0A
+%0A
         return s

Add a participating event to the A/B testing service.