package org.netmelody.cieye.witness;

import static com.google.common.collect.Lists.newArrayList;
import static org.netmelody.cieye.domain.Build.buildAt;
import static org.netmelody.cieye.domain.Percentage.percentageOf;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import org.netmelody.cieye.domain.Build;
import org.netmelody.cieye.domain.Feature;
import org.netmelody.cieye.domain.Status;
import org.netmelody.cieye.domain.Target;
import org.netmelody.cieye.domain.TargetGroup;
import org.netmelody.cieye.persistence.Detective;

import com.google.common.base.Function;
import com.google.common.collect.MapMaker;

public final class DemoModeWitness implements Witness {

    private final Map<String, TargetGroup> groupMap;

    public DemoModeWitness(final Detective detective) {
        final Map<String, TargetGroupGenerator> generatorMap =
            new MapMaker().makeComputingMap(new Function<String, TargetGroupGenerator>() {
                @Override
                public TargetGroupGenerator apply(String featureName) {
                    return new TargetGroupGenerator(detective, featureName);
                }
            });
        
        groupMap = new MapMaker()
            .expireAfterWrite(1, TimeUnit.SECONDS)
            .makeComputingMap(new Function<String, TargetGroup>() {
                @Override
                public TargetGroup apply(String featureName) {
                    return generatorMap.get(featureName).updatedValue();
                }
            });
    }
    
    @Override
    public TargetGroup statusOf(Feature feature) {
        return groupMap.get(feature.name());
    }
    

    @Override
    public long millisecondsUntilNextUpdate(Feature feature) {
        return 0L;
    }
    
    @Override
    public boolean takeNoteOf(String targetId, String note) {
        return true;
    }
    
    private static final class TargetGroupGenerator {
        
        private final Detective detective;
        private final Status[] statuses;
        private TargetGroup group;
        
        private TargetGroupGenerator(Detective detective, String featureName) {
            this.detective = detective;

            statuses = EnumSet.of(Status.BROKEN, Status.GREEN)
                              .toArray(new Status[EnumSet.of(Status.BROKEN, Status.GREEN).size()]);
            
            group = new TargetGroup(newArrayList(randomTarget(featureName + " - Smoke"),
                                                 randomTarget(featureName + " - Integration"),
                                                 randomTarget(featureName + " - Acceptance"),
                                                 randomTarget(featureName + " - Release")));
        }
        
        private TargetGroup updatedValue() {
            final Random random = new Random();
            final List<Target> newTargets = newArrayList();
            for (Target target : group.targets()) {
                final List<Build> builds = target.builds();
                final List<Build> newBuilds = newArrayList();
                Status newStatus = target.status();
                
                if (builds.isEmpty()) {
                    if (random.nextInt(Status.GREEN.equals(target.status()) ? 30 : 10) == 0) {
                        newBuilds.add(buildAt(percentageOf(0), Status.GREEN));
                    }
                }
                else {
                    int minProgress = 100;
                    for (Build build : builds) {
                        Build newBuild = build.advancedBy(2);
                        final int progress = newBuild.progress().value();
                        minProgress = Math.min(minProgress, progress);
                        if (progress >= 60 && progress <= 90 && random.nextInt(50) == 0) {
                            newBuild = newBuild.withStatus(Status.BROKEN);
                        }
                        if (progress == 100) {
                            newStatus = Status.BROKEN.equals(newBuild.status()) ? Status.BROKEN : Status.GREEN;
                        }
                        else {
                            newBuilds.add(newBuild);
                        }
                    }
                    if (minProgress > 50 && random.nextInt(100) == 0) {
                        newBuilds.add(buildAt(percentageOf(0), Status.GREEN));
                    }
                }
                newTargets.add(target.withBuilds(newBuilds).withStatus(newStatus));
            }
            group = new TargetGroup(newTargets);
            return group;
        }

        private Target randomTarget(String name) {
            final Random random = new Random();
            
            final Status status = statuses[random.nextInt(statuses.length)];
            final ArrayList<Build> builds = newArrayList();
            
            if (random.nextBoolean()) {
                builds.add(buildAt(percentageOf(random.nextInt(101)), Status.GREEN));
            }
            
            return new Target(name, "http://www.example.com/", name, status, 0L, builds, detective.sponsorsOf("dracula"));
        }
    }
}

@@ -289,34 +289,88 @@
 til.
-List;%0Aimport java.util.Map
+HashSet;%0Aimport java.util.List;%0Aimport java.util.Map;%0Aimport java.util.Map.Entry
 ;%0Aim
@@ -392,16 +392,38 @@
 Random;%0A
+import java.util.Set;%0A
 import j
@@ -860,16 +860,82 @@
 ness %7B%0A%0A
+    private final Map%3CString, TargetGroupGenerator%3E generatorMap;%0A
     priv
@@ -1047,48 +1047,8 @@
     
- final Map%3CString, TargetGroupGenerator%3E
  gen
@@ -1843,21 +1843,16 @@
 );%0A    %7D
-%0A    
 %0A%0A    @O
@@ -2040,85 +2040,664 @@
     
-return true;%0A    %7D%0A    %0A    private static final class TargetGroupGenerator %7B
+final Set%3CEntry%3CString, TargetGroup%3E%3E entries = groupMap.entrySet();%0A        for (Entry%3CString, TargetGroup%3E entry : entries) %7B%0A            final TargetGroup group = entry.getValue();%0A            final List%3CTarget%3E targets = group.targets();%0A            for (Target target : targets) %7B%0A                if (targetId.equals(target.id())) %7B%0A                    generatorMap.get(entry.getKey()).noteReceivedFor(target.id());%0A                    return true;%0A                %7D%0A            %7D%0A        %7D%0A        return false;%0A    %7D%0A    %0A    private static final class TargetGroupGenerator %7B%0A        %0A        private final Set%3CString%3E notes = new HashSet%3CString%3E();
 %0A   
@@ -3509,32 +3509,119 @@
      %7D%0A        %0A
+        public void noteReceivedFor(String id) %7B%0A            notes.add(id);%0A        %7D%0A%0A
         private 
@@ -3992,32 +3992,217 @@
                %0A
+                if (notes.remove(target.id()) && !Status.GREEN.equals(target.status())) %7B%0A                    newStatus = Status.UNDER_INVESTIGATION;%0A                %7D%0A                %0A
                 

add demo mode support for marking builds.