package org.aanguita.jacuzzi.fsm;

import org.aanguita.jacuzzi.concurrency.timer.TimerAction;
import org.aanguita.jacuzzi.concurrency.timer.Timer;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * FSM with timeout capability
 */
public class TimedFSM<T, Y> extends GenericFSM<T, Y> implements TimerAction {

    private final TimedFSMAction<T, Y> timedFSMAction;

    private final Timer timer;

    private AtomicBoolean timedOut;

    public TimedFSM(TimedFSMAction<T, Y> timedFSMAction, long timeoutMillis) {
        this("unnamedTimedFSM", timedFSMAction, timeoutMillis);
    }

    public TimedFSM(String name, TimedFSMAction<T, Y> timedFSMAction, long timeoutMillis) {
        super(name, timedFSMAction);
        this.timedFSMAction = timedFSMAction;
        timedOut = new AtomicBoolean(false);
        timer = new Timer(timeoutMillis, this, false, name);
    }

    private void stopTimer() {
        timer.stop();
    }

    private void restartTimer() {
        timer.reset();
    }

    @Override
    public synchronized boolean newInput(Y input) {
        if (!timedOut.get()) {
            // we stop the timer during the new input invocation so we don't get timeouts if the method takes long to complete
            stopTimer();
            boolean active = super.newInput(input);
            if (!active) {
                kill();
            } else {
                restartTimer();
            }
            return active;
        } else {
            return false;
        }
    }

    @Override
    public synchronized boolean start() {
        boolean result = super.start();
        if (result) {
            // if it is active after start, initiate the timer. Otherwise, the timer is not initiated and thus it does not need to be killed later
            restartTimer();
        }
        return result;
    }

    public void stop() {
        super.stop();
        kill();
    }

    public void kill() {
        timer.kill();
        synchronized (this) {
            timedOut.set(true);
        }
    }

    @Override
    public synchronized Long wakeUp(Timer timer) {
        // the timer has jumped -> no activity reached this FSM during the specified time, the FSM finishes
        // we first check that the FSM is still active
        if (active.get()) {
            timedOut.set(true);
            timedFSMAction.timedOut(currentState);
        }
        // the timer dies
        return 0L;
    }

    @Override
    public boolean equals(Object obj) {
        return obj.getClass() == TimedFSM.class && id.equals(((GenericFSM) obj).id);
    }
}

@@ -79,22 +79,16 @@
 er.Timer
-Action
 ;%0Aimport
@@ -136,58 +136,13 @@
 imer
-;%0A%0Aimport java.util.concurrent.atomic.AtomicBoolea
+Actio
 n;%0A%0A
@@ -351,45 +351,8 @@
 r;%0A%0A
-    private AtomicBoolean timedOut;%0A%0A
     
@@ -676,53 +676,8 @@
 on;%0A
-        timedOut = new AtomicBoolean(false);%0A
     
@@ -744,68 +744,8 @@
  %7D%0A%0A
-    private void stopTimer() %7B%0A        timer.stop();%0A    %7D%0A%0A
     
@@ -833,45 +833,22 @@
 lic 
-synchronized boolean newInput(Y input
+boolean start(
 ) %7B%0A
@@ -863,20 +863,18 @@
 if (
-!timedOut.ge
+super.star
 t())
@@ -895,119 +895,139 @@
  // 
-we stop the timer during the new input invocation so we don't get timeouts if the method takes long to comple
+if it is active after start, initiate the timer. Otherwise, the timer is not initiated and thus it does not need to be killed la
 te
+r
 %0A   
@@ -1035,20 +1035,23 @@
         
-stop
+restart
 Timer();
@@ -1067,103 +1067,21 @@
     
-boolean active = super.newInput(input);%0A            if (!active) %7B%0A                kill();%0A    
+return true;%0A
     
@@ -1105,32 +1105,26 @@
         
-    
-restartTimer
+timer.kill
 ();%0A    
@@ -1135,189 +1135,110 @@
     
-%7D%0A            return active;%0A        %7D else %7B%0A            return false;%0A        %7D%0A    %7D%0A%0A    @Override%0A    public synchronized boolean start() %7B%0A        boolean result = sup
+return false;%0A        %7D%0A    %7D%0A%0A    @Override%0A    public boolean newInput(Y input) %7B%0A        tim
 er.st
-art
+op
 ();%0A
@@ -1253,164 +1253,102 @@
 if (
-result) %7B%0A            // if it is active after start, initiate the timer. Otherwise, the timer is not initiated and thus it does not need to be killed later
+super.newInput(input)) %7B%0A            restartTimer();%0A            return true;%0A        %7D else %7B
 %0A   
@@ -1356,36 +1356,34 @@
         
-restartTimer
+timer.kill
 ();%0A    
     %7D%0A  
@@ -1370,30 +1370,24 @@
 kill();%0A    
-    %7D%0A
         retu
@@ -1393,22 +1393,45 @@
 urn 
-result;%0A    %7D%0A
+false;%0A        %7D%0A    %7D%0A%0A    @Override
 %0A   
@@ -1486,141 +1486,21 @@
     
+timer.
 kill();
-%0A    %7D%0A%0A    public void kill() %7B%0A        timer.kill();%0A        synchronized (this) %7B%0A            timedOut.set(true);%0A        %7D
 %0A   
@@ -1532,21 +1532,8 @@
 lic 
-synchronized 
 Long
@@ -1740,17 +1740,28 @@
 tive.get
-(
+AndSet(false
 )) %7B%0A   
@@ -1765,24 +1765,57 @@
             
+active.set(false);%0A            //
 timedOut.set

Fix synch issues, simplify