package hu.supercluster.gameoflife.app.activity.automaton;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.SurfaceHolder;

import com.squareup.otto.Subscribe;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import hu.supercluster.gameoflife.game.cellularautomaton.CellularAutomaton;
import hu.supercluster.gameoflife.game.cell.Cell;
import hu.supercluster.gameoflife.game.cell.CellStateChange;
import hu.supercluster.gameoflife.game.grid.Grid;
import hu.supercluster.gameoflife.util.EventBus;
import hugo.weaving.DebugLog;

class AutomatonThread extends Thread {
    private final CellularAutomaton automaton;
    private final SurfaceHolder surfaceHolder;
    private final int cellSizeInPixels;
    private final int timeForAFrame;
    private boolean isRunning;
    private final Paint paintAlive;
    private final Paint paintExtra;
    private final Paint paintDead;
    private long cycleTime;
    private List<CellStateChange> cellStateChanges;
    private int cnt;
    private Bitmap buffCanvasBitmap;
    private Canvas buffCanvas;

    public AutomatonThread(CellularAutomaton automaton, SurfaceHolder surfaceHolder, int cellSizeInPixels, int fps) {
        EventBus.getInstance().register(this);
        cellStateChanges = new LinkedList<>();
        this.automaton = automaton;
        this.surfaceHolder = surfaceHolder;
        this.cellSizeInPixels = cellSizeInPixels;
        timeForAFrame = 1000 / fps;

        paintAlive = createPaint("#ff9900");
        paintExtra = createPaint("#99ff00");
        paintDead = createPaint("#000000");

        buffCanvasBitmap = Bitmap.createBitmap(automaton.getSizeX() * cellSizeInPixels, automaton.getSizeY() * cellSizeInPixels, Bitmap.Config.ARGB_8888);
        buffCanvas = new Canvas();
        buffCanvas.setBitmap(buffCanvasBitmap);
    }

    private Paint createPaint(String color) {
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.parseColor(color));
        
        return paint;
    }

    @Subscribe
    synchronized public void onEvent(CellStateChange cellStateChange) {
        cellStateChanges.add(cellStateChange);
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

    @Override
    public void run() {
        this.automaton.randomFill(0.10f, Cell.STATE_ALIVE);

        while (isRunning) {
            Canvas canvas = null;

            try {
                canvas = surfaceHolder.lockCanvas();
                if (canvas != null) {
                    cycle(canvas);
                    sleepToKeepFps();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();

            } finally {
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }

    protected void cycle(Canvas canvas) {
        synchronized (surfaceHolder) {
            long t0 = System.currentTimeMillis();
            cycleCore(canvas);
            long t1 = System.currentTimeMillis();
            cycleTime = t1 - t0;
        }
    }

    @DebugLog
    private void cycleCore(Canvas canvas) {
        draw(canvas);
        stepAutomaton();
    }

    @DebugLog
    private void stepAutomaton() {
        automaton.step();
    }

    @DebugLog
    private void draw(Canvas canvas) {
        for (CellStateChange change : cellStateChanges) {
            paintCell(buffCanvas, change.x, change.y, change.stateSnapshot);
        }

        cellStateChanges.clear();
        canvas.drawBitmap(buffCanvasBitmap, 0, 0, null);
    }

    private void paintCell(Canvas canvas, int i, int j, int cellState) {
        Paint paint = getPaint(cellState);
        Rect rect = new Rect(
                i*cellSizeInPixels,
                j*cellSizeInPixels,
                (i+1)*cellSizeInPixels,
                (j+1)*cellSizeInPixels
        );

        if (canvas != null) {
            canvas.drawRect(rect, paint);
        }
    }

    private Paint getPaint(int state) {
        return state == Cell.STATE_ALIVE ? paintAlive : paintDead;
    }

    @DebugLog
    private void sleepToKeepFps() throws InterruptedException {
        long sleepTime = timeForAFrame - cycleTime;

        if (sleepTime > 0) {
            sleep(sleepTime);
        }
    }
}

@@ -286,36 +286,8 @@
 e;%0A%0A
-import java.util.ArrayList;%0A
 impo
@@ -526,58 +526,8 @@
 ge;%0A
-import hu.supercluster.gameoflife.game.grid.Grid;%0A
 impo
@@ -883,44 +883,8 @@
 ve;%0A
-    private final Paint paintExtra;%0A
     
@@ -998,29 +998,8 @@
 es;%0A
-    private int cnt;%0A
     
@@ -1491,53 +1491,8 @@
 %22);%0A
-        paintExtra = createPaint(%22#99ff00%22);%0A
     
@@ -2253,24 +2253,110 @@
 oid run() %7B%0A
+        initAutomaton();%0A        doRun();%0A    %7D%0A%0A    protected void initAutomaton() %7B%0A
         this
@@ -2403,16 +2403,51 @@
 ALIVE);%0A
+    %7D%0A%0A    protected void doRun() %7B
 %0A       
@@ -2593,120 +2593,29 @@
     
-if (canvas != null) %7B%0A                    cycle(canvas);%0A                    sleepToKeepFps();%0A                %7D
+oneGameCycle(canvas);
 %0A%0A  
@@ -2737,38 +2737,48 @@
         
-if (canvas != null) %7B%0A
+unlockCanvasAndPost(canvas);%0A   
         
@@ -2778,24 +2778,26 @@
         
+%7D%0A
         
    surfa
@@ -2792,85 +2792,192 @@
     
+%7D%0A
    
-surfaceHolder.unlockCanvasAndPost(canvas);%0A                %7D%0A            %7D
+ %7D%0A%0A    protected void oneGameCycle(Canvas canvas) throws InterruptedException %7B%0A        if (canvas != null) %7B%0A            measuredCycleCore(canvas);%0A            sleepToKeepFps();
 %0A   
@@ -3009,20 +3009,32 @@
 ed void 
-cycl
+measuredCycleCor
 e(Canvas
@@ -3326,30 +3326,8 @@
 ) %7B%0A
-        draw(canvas);%0A
     
@@ -3343,24 +3343,46 @@
 utomaton();%0A
+        draw(canvas);%0A
     %7D%0A%0A    @
@@ -4472,11 +4472,167 @@
  %7D%0A    %7D
+%0A%0A    private void unlockCanvasAndPost(Canvas canvas) %7B%0A        if (canvas != null) %7B%0A            surfaceHolder.unlockCanvasAndPost(canvas);%0A        %7D%0A    %7D
 %0A%7D%0A

Refactor AutomatonThread