// FrameClock.java
//**********************************************************************************************
//
//
//                              (C) Copyright 2000 by Dipl. Phys. Joerg Plewe, HARDCODE Development
//                                      All rights reserved. Copying, modification,
//                                      distribution or publication without the prior written 
//                                      consent of the author is prohibited.
//
//
//      Filename:               FrameClock.java
//      Author:                 Herkules
//  Created:            20000119
//  Version:            $Version$
//
//      Module:                 $Module$
//  Dependecies:        $Dependencies$
//
//  History:
//  $Date$              $Who$           $What$
//
/*      Purpose:

$Describe purpose of module$
Provide an adjustable (->network) clock 

*/
//**********************************************************************************************

package HARDCODE.Ext.Util;


public class FrameClock
{
	
        /** Constructor
         */
        public FrameClock()
        {
                calibrate();	
        }
	
	
        //-------------------------------------------------------------------------Adjust
        // Herkules
        // Adjust the clock with an offset
        //
    

    public FrameTime getFrameTime()
    {
        return m_frametime;
    }

        public void adjust( int _Adjustment )
        {
                // Shift the adjustment
                adjust -= _Adjustment;
                                        	
                // reset the timer
                reset();
        }


        //-------------------------------------------------------------------------maintainFrameTiming
        public void maintainFrameTiming( )
        {       	
                int currentMillis = (int)System.currentTimeMillis() - adjust;
                int FrameTimeDiff = currentMillis - lastFrameMillis;
                        	
                FrameTimer[ FrameTimerIndex ] = FrameTimeDiff;
                FrameTimerIndex++;
                FrameTimerIndex %= FrameTimerCount;


        	
                if( FrameTimeDiff > 0 )
                {               	
                        m_shiftFromAverage += ( ((countNoTimerChange+1) * averageFrameTime) - FrameTimeDiff );   
                	
                        // Reduce shift so it does not walk away
                        m_shiftFromAverage = (m_shiftFromAverage * 9) / 10;
                	
                        lastFrameMillis = currentMillis;
                        countNoTimerChange = 0;
                }
                else
                        countNoTimerChange++;

        	

                // Remember an average FPS value at reasonables distances
                if( FrameTimerIndex == 0)
        {
                        updateAverages();
//                      System.out.println( getFPS() );
                }

        m_frametime.target = getTimeNextFrame();
        m_frametime.delta = getFrameTimeDelta();

        	
                // Check quality of timer
                if( mono > getTimeNextFrame() )
                {
                        System.out.print( "Timer not monotone! Backstep is " );
                        System.out.println( mono - getTimeNextFrame() );
                }
                mono = getTimeNextFrame();
        }
        static int mono = Integer.MIN_VALUE;

	
        //-------------------------------------------------------------------------getTimeNextFrame
        public int getTimeNextFrame() 
        {
                return lastFrameMillis + m_shiftFromAverage + ( (countNoTimerChange+1) * averageFrameTime ); 	
        }
	

        //-------------------------------------------------------------------------getFPS
        public float getFPS()
        {
                return averageFPS;
        }

	
        //-------------------------------------------------------------------------getAverageFrameTime
        public float getAverageFrameTime()
        {
                return (float)averageFrameTime;
        }

        //-------------------------------------------------------------------------getFrameTimeDelta
        /** Get the time since last frame. Same as getAverageFrameTime()
         * @return normalized time since last frame in milliseconds
         */
        public float getFrameTimeDelta()
        {
                return (float)averageFrameTime;
        }


        /** calibrate the timer by measuring the timer accuracy and setting the internal
         * averaging parameters
         */
        private void calibrate()
        {
                FrameTimerCount = (int)new TimerAccuracy().measure( 200 ) / 2;
        	
                if( FrameTimerCount < 5 )
                        FrameTimerCount = 5;
        	
                if( FrameTimerCount >= maxFrameTimerCount )
                        FrameTimerCount = maxFrameTimerCount - 1;
        	
                FrameTimer = new int[ FrameTimerCount ];

                for( int i = 0; i < FrameTimerCount; ++i )
                {
                        FrameTimer[ i ] = averageFrameTime;
                }
        	
                reset();
                updateAverages();
        }

	
	
        //-------------------------------------------------------------------------reset
        /** Reset timer
         */
        public void reset()
        {
                lastFrameMillis = (int)System.currentTimeMillis() - adjust;
        }
        	
	
        //-------------------------------------------------------------------------updateAverages
        private void updateAverages()
        {
                averageFrameTime = 0;
                for( int i = 0; i < FrameTimerCount; ++i )
                {
                        averageFrameTime += FrameTimer[ i ];
                }
                averageFrameTime /= FrameTimerCount;

                averageFPS = 1000.0f / (float)averageFrameTime;
        	
        }
	
        //*********************************************************************************************
        // Variables
        //*********************************************************************************************

        private int lastSnap = -1;

        private int adjust = 0;

        private final int maxFrameTimerCount = 100;
        private int FrameTimerCount = maxFrameTimerCount;
        private int[] FrameTimer;
        private int lastFrameMillis;
        private int FrameTimerIndex = 0;
        private int averageFrameTime = 40;
        private int countNoTimerChange = 0;
	
        private float averageFPS = 25.0f;

        private FrameTime m_frametime = new FrameTime();
	
        private int m_shiftFromAverage = 0;
}


