package no.javazone;

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.japi.pf.ReceiveBuilder;
import akka.pattern.Patterns;
import akka.routing.RoundRobinPool;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static no.javazone.util.Timer.time;

public class Akka {

    private static final int numRuns = 10_000;

    private static class TaskActor extends AbstractActor {

        public TaskActor() {
            receive(
                ReceiveBuilder
                    .matchEquals("run", msg -> sender().tell(Big.task(), self()))
                    .build()
            );
        }
    }

    private static class TaskRunner extends AbstractActor {

        private ActorRef taskActor;
        private ActorRef originator;

        private int activeTasks = 0;
        private long currentSum = 0;

        public TaskRunner() {
            this.taskActor =
                context().actorOf(
                    Props.create(TaskActor.class).withRouter(new RoundRobinPool(10)),
                    "task"
                );

            receive(
                ReceiveBuilder
                    .matchEquals("start", msg -> {
                        for (int i = 0; i < numRuns; i++) {
                            taskActor.tell("run", self());
                            activeTasks++;
                        }
                        originator = sender();
                    })
                    .match(Long.class, msg -> {
                        currentSum += msg;
                        activeTasks--;
                        if (activeTasks == 0) {
                            originator.tell(currentSum, self());
                        } else if (activeTasks % 1000 == 0) {
                            printProgress(activeTasks);
                        }
                    })
                    .build()
            );
        }
    }

    public static void main(String[] args) throws Exception {
        ActorSystem system = ActorSystem.create();

        time(() -> {
            ActorRef taskRunner = system.actorOf(Props.create(TaskRunner.class), "runner");

            Object result = Await.result(Patterns.ask(taskRunner, "start", 30000), Duration.create(30, TimeUnit.SECONDS));

            printProgress(0);
            System.out.println("\nDone result: " + result);
        });
        system.shutdown();
    }

    private static void printProgress(int activeTasks) {
        int mem = (int) ((Runtime.getRuntime().totalMemory() * 100) / Runtime.getRuntime().maxMemory());
        StringBuilder builder = new StringBuilder();
        IntStream.range(0, mem / 2).forEach(i -> builder.append('\u2588'));
        builder.append(" ").append(mem).append("%");
        IntStream.range(Math.max(0, 50 - (50 - builder.length())), 50).forEach(i -> builder.append(" "));
        long progress = ((numRuns - activeTasks) * 100) / numRuns;
        String line = "\rMemory usage: " + builder.toString() + " (progress: " + progress + "%)";
        System.out.print(line);
        System.out.flush();
    }
}

@@ -307,16 +307,65 @@
 uration;
+%0Aimport scala.concurrent.duration.FiniteDuration;
 %0A%0Aimport
@@ -399,43 +399,8 @@
 nit;
-%0Aimport java.util.stream.IntStream;
 %0A%0Aim
@@ -1864,148 +1864,110 @@
    %7D
- else if (activeTasks %25 1000 == 0) %7B%0A                            printProgress(activeTasks);%0A                        %7D%0A                    %7D
+%0A                    %7D)%0A                    .matchEquals(%22progress%22, msg -%3E printProgress(activeTasks)
 )%0A  
@@ -2156,24 +2156,60 @@
 ime(() -%3E %7B%0A
+            printProgress(numRuns);%0A
             
@@ -2285,24 +2285,351 @@
 %22runner%22);%0A%0A
+            FiniteDuration halfSecond = Duration.create(100, TimeUnit.MILLISECONDS);%0A            system.scheduler().schedule(%0A                halfSecond,%0A                halfSecond,%0A                taskRunner,%0A                %22progress%22,%0A                system.dispatcher(),%0A                ActorRef.noSender()%0A            );%0A%0A
             
@@ -2657,16 +2657,33 @@
 .result(
+%0A                
 Patterns
@@ -2715,16 +2715,32 @@
  30000),
+%0A               
  Duratio
@@ -2769,16 +2769,29 @@
 SECONDS)
+%0A            
 );%0A%0A    
@@ -3096,451 +3096,231 @@
     
-StringBuilder builder = new StringBuilder();%0A        IntStream.range(0, mem / 2).forEach(i -%3E builder.append('%5Cu2588'));%0A        builder.append(%22 %22).append(mem).append(%22%25%22);%0A        IntStream.range(Math.max(0, 50 - (50 - builder.length())), 50).forEach(i -%3E builder.append(%22 %22));%0A        long progress = ((numRuns - activeTasks) * 100) / numRuns;%0A        String line = %22%5CrMemory usage: %22 + builder.toString() + %22 (progress: %22 + progress + %22%25)%22
+long progress = ((numRuns - activeTasks) * 100) / numRuns;%0A        String line = String.format(%0A            %22%5Cr(memory: %25d%25%25) (progress: %25d%25%25) (active threads: %25d)%22,%0A            mem, progress, Thread.activeCount()%0A        )
 ;%0A  

Move progress to external message.