/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 */
package com.ericsson.ssa.sip.timer;

import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledFuture;


/**
 * Common base class for Timer implementations.
 *
 *
 *
 * @author qmaghes
 * @since 2007 nov 21
 *
 */
public abstract class GeneralTimerBase implements GeneralTimer, Callable {
    /**
      * Whether this timer has been successfully cancelled. Used for debugging.
      */
    @SuppressWarnings("unused")
    protected Boolean iscanceled = null;

    /**
      * Lock that prevents simultaneous execution. Note! Do NOT call methods in
      * the associated sip session within synchronization of this lock since there
      * may be a dead lock.
      */
    protected final Object SIPP_APPLICATION_LOCK = new Object();
    
    public Object call() throws Exception {
        try {
            timeout();
        } finally {
            if (getPFieldIsRepeatingTimer()) {
                estimateNextExecution();
            } else {
                // this non-repeating timer is now "ready"
                // and should not be included in the list of active timers
                // The application may already have canceled() the timer though
                cancel(); // dont bother about return value....
            }
        }
        return null;
    }

    protected abstract void timeout();

    /**
      * Setter to set what should be executed.
      *
      * @param f
      */
    public void setFuture(ScheduledFuture<?> f) {
        synchronized (SIPP_APPLICATION_LOCK) {
            setPFieldFuture(f);
        }
    }

    /**
      * Helper to calculate when next execution time is.
      *
      */
    private void estimateNextExecution() {
        synchronized (SIPP_APPLICATION_LOCK) {
            if (getPFieldFixedDelay()) {
                setPFieldScheduledExecutionTime(getPFieldPeriod() +
                    System.currentTimeMillis());
            } else {
                if (getPFieldFirstExecution() == 0) {
                    // save timestamp of first execution
                    setPFieldFirstExecution(getPFieldScheduledExecutionTime());
                }

                setPFieldScheduledExecutionTime(getPFieldFirstExecution() +
                    (incrementAndGetPFieldNumInvocations() * getPFieldPeriod()));
            }
        }
    }

    /**
      * Getter for delay property.
      *
      * @return
      */
    public long getDelay() {
        return getPFieldDelay();
    }

    /**
      * Getter for period property
      *
      * @return
      */
    public long getPeriod() {
        return getPFieldPeriod();
    }

    public long scheduledExecutionTime() {
        synchronized (SIPP_APPLICATION_LOCK) {
            return getPFieldScheduledExecutionTime();
        }
    }

    public void cancel() {
        cancel(false);
    }

    /**
      *
      * Cancel this timer, possibly by also interrupting the thread (from the
      * thread pool) running the task. Note that interupting the thread may have
      * undesired consequences.
      *
      * @param mayInterruptIfRunning
      */
    public void cancel(boolean mayInterruptIfRunning) {
        synchronized (SIPP_APPLICATION_LOCK) {
            ScheduledFuture<?> future = getPFieldFuture();

            if (future != null) {
                // need to force cancel to get rid of
                // the task which is currently scheduled
                boolean res = future.cancel(mayInterruptIfRunning);
                // used for debugging/optimizeIt purpose
                // kan be kept in production code since object should
                // be due for gc anyway....
                iscanceled = new Boolean(res);
                setPFieldFuture(null);
            }
        }
    }

    protected abstract ScheduledFuture<?> getPFieldFuture();

    protected abstract void setPFieldFuture(ScheduledFuture<?> future);

    protected abstract boolean getPFieldIsRepeatingTimer();

    protected abstract long getPFieldFirstExecution();

    protected abstract void setPFieldFirstExecution(long firstExecutionTime);

    protected abstract boolean getPFieldFixedDelay();

    protected abstract long getPFieldPeriod();

    protected abstract long getPFieldScheduledExecutionTime();

    protected abstract void setPFieldScheduledExecutionTime(
        long scheduledExecutionTime);

    protected abstract long incrementAndGetPFieldNumInvocations();

    protected abstract long getPFieldDelay();

    /**
      * Predicate for isRepeating property.
      *
      * @return
      */
    protected boolean isRepeatingTimer() {
        return getPFieldIsRepeatingTimer();
    }

    /**
      * Predicate for fixed delay property
      *
      * @return
      */
    protected boolean isFixedDelay() {
        return getPFieldFixedDelay();
    }
}
