package org.netmelody.cieye.spies.demo;

import static java.util.Collections.unmodifiableList;

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

public final class DemoModeFakeCiServer {

    private final Map<String, BuildTarget> targets = new HashMap<String, BuildTarget>();

    public DemoModeFakeCiServer(String featureName) {
        targets.put(featureName + " - Smoke", new BuildTarget(featureName + " - Smoke"));
        targets.put(featureName + " - Integration", new BuildTarget(featureName + " - Integration"));
        targets.put(featureName + " - Acceptance", new BuildTarget(featureName + " - Acceptance"));
        targets.put(featureName + " - Release", new BuildTarget(featureName + " - Release"));
    }

    public List<String> getTargetNames() {
        final List<String> result = new ArrayList<String>();
        for (BuildTarget target : targets.values()) {
            result.add(target.getName());
        }
        return result;
    }
    
    public void addNote(String targetName, String note) {
        final BuildTarget buildTarget = targets.get(targetName);
        if (null != buildTarget) {
            buildTarget.addNote(note);
        }
    }
    
    public TargetData getDataFor(String targetName) {
        final BuildTarget buildTarget = targets.get(targetName);
        synchronized(buildTarget) {
            buildTarget.recalculate();
            final List<BuildData> buildData = new ArrayList<BuildData>();
            for (ActiveBuild build : buildTarget.builds) {
                buildData.add(new BuildData(build.progress, build.green, "dracula"));
            }
            return new TargetData(buildTarget.url, buildTarget.green, buildTarget.note, buildData);
        }
    }

    public static final class TargetData {
        public final String url;
        public final boolean green;
        public final List<BuildData> builds;
        public final String note;
        public TargetData(String url, boolean green, String note, List<BuildData> buildData) {
            this.url = url;
            this.green = green;
            this.note = note;
            this.builds = unmodifiableList(new ArrayList<BuildData>(buildData));
        }
    }
    
    public static final class BuildData {
        public final int progress;
        public final boolean green;
        public final String checkinComments;
        public BuildData(int progress, boolean green, String comment) {
            this.progress = progress;
            this.green = green;
            this.checkinComments = comment;
        }
    }
    
    private static final class BuildTarget {
        private final Random random = new Random();
        private final String targetName;
        private final String url = "http://www.example.com/";
        
        private boolean green;
        private String note = "";
        private final List<ActiveBuild> builds = new ArrayList<ActiveBuild>();
        private long lastUpdateTime = System.currentTimeMillis();

        public BuildTarget(String targetName) {
            this.targetName = targetName;
            green = random.nextInt(5) != 0;
            
            if (random.nextInt(5) == 0) {
                builds.add(new ActiveBuild());
            }
        }

        public void recalculate() {
            final long currentTime = System.currentTimeMillis();
            final long ticksPassed = (currentTime - lastUpdateTime) / 1000;
            advanceBy((int)ticksPassed);
            this.lastUpdateTime  = currentTime;
        }

        public void addNote(String note) {
            if (!green) {
                this.note = note;
            }
        }

        public String getName() {
            return targetName;
        }
        
        public void advanceBy(int percent) {
            Iterator<ActiveBuild> buildIterator = builds.iterator();
            while (buildIterator.hasNext()) {
                ActiveBuild build = buildIterator.next();
                build.advanceBy(percent);
                if (build.progress == 100) {
                    buildIterator.remove();
                    green = build.green;
                    note = "";
                }
            }
            
            if (builds.isEmpty()) {
                if (random.nextInt(green ? 30 : 10) == 0) {
                    builds.add(new ActiveBuild(2));
                }
            }
        }
    }
    
    private static final class ActiveBuild {
        private final Random random = new Random();
        private boolean green = true;
        private int progress;
        
        public ActiveBuild() {
            progress = random.nextInt(101);
        }

        public ActiveBuild(int initialProgress) {
            progress = initialProgress;
        }

        public void advanceBy(int percent) {
            final int oldProgress;
            
            synchronized(this) {
                oldProgress = progress;
                progress = Math.min(100, progress + percent);
            }
            
            if (oldProgress > 90 || progress < 60) {
                return;
            }
            
            int prob = Math.max(Math.min(progress, 90) - 60, 0) - Math.min(oldProgress - 60, 0);
            if (random.nextInt(100) < prob) {
                green = false;
            }
        }
    }
}

@@ -2844,36 +2844,8 @@
  url
- = %22http://www.example.com/%22
 ;%0A  
@@ -3144,32 +3144,95 @@
 e = targetName;%0A
+            this.url = %22http://www.example.com/%22 + targetName;%0A
             gree
@@ -4165,23 +4165,18 @@
 ild.
-progress == 100
+complete()
 ) %7B%0A
@@ -4789,32 +4789,128 @@
 01);%0A        %7D%0A%0A
+        public synchronized boolean complete() %7B%0A            return progress %3E= 100;%0A        %7D%0A%0A
         public A
@@ -5055,29 +5055,20 @@
     
-final int oldProgress
+int prob = 0
 ;%0A  
@@ -5102,16 +5102,17 @@
 hronized
+ 
 (this) %7B
@@ -5128,16 +5128,26 @@
         
+final int 
 oldProgr
@@ -5234,33 +5234,27 @@
         
-  %7D%0A
       
-  
+%0A
     
-%0A
         
@@ -5310,24 +5310,28 @@
             
+   
+ 
 return;%0A    
@@ -5330,34 +5330,41 @@
 rn;%0A            
-%7D%0A
+    %7D%0A   
             %0A   
@@ -5351,32 +5351,33 @@
 %0A               
+ 
 %0A            int
@@ -5365,35 +5365,35 @@
    %0A            
-int
+   
  prob = Math.max
@@ -5458,16 +5458,43 @@
 60, 0);%0A
+            %7D%0A            %0A
         

tidy up