package com.team254.lib.util.gyro;

import com.team254.lib.util.Util;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Thread which is responsible for reading the gyro
 */
public class GyroThread {

    private static final int K_READING_RATE = 200;
    private static final int K_ZEROING_SAMPLES = 6 * K_READING_RATE;
    private static final int K_STARTUP_SAMPLES = 2 * K_READING_RATE;

    // synchronized access object
    private final Timer mTimer = new Timer("Gyro");
    // owned by the background thread
    private final GyroInterface mGyroInterface = new GyroInterface();

    // thread communication variables
    private volatile boolean mVolatileHasData = false;
    private volatile double mVolatileAngle = 0;
    private volatile double mVolatileRate = 0;
    private volatile boolean mVolatileShouldReZero = true;

    // owned by the accesser of this object
    private double mZeroHeading = 0;

    public void start() {
        synchronized (mTimer) {
            mTimer.schedule(new InitTask(), 0);
        }
    }

    public boolean hasData() {
        return mVolatileHasData;
    }

    public double getAngle() {
        return mVolatileAngle - mZeroHeading;
    }

    public double getRate() {
        return mVolatileRate;
    }

    public void rezero() {
        mVolatileShouldReZero = true;
    }

    public void reset() {
        mZeroHeading = mVolatileAngle;
    }

    /**
     * Initializes the gyro, verifying the its self-test results
     */
    private class InitTask extends TimerTask {
        @Override
        public void run() {
            boolean initialized = false;
            while (!initialized) {
                try {
                    mGyroInterface.initializeGyro();
                    initialized = true;
                } catch (GyroInterface.GyroException e) {
                    System.out.println("Gyro failed to initialize: " + e.getMessage());
                }
                if (!initialized) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("gyo initialized, part ID: 0x" + Integer.toHexString(mGyroInterface.readPartId()));
            synchronized (mTimer) {
                mTimer.schedule(new UpdateTask(), 0, (int) (1000.0 / K_READING_RATE));
            }
        }
    }

    /**
     * Updates the actual gyro data (zeroing and accumulation)
     */
    private class UpdateTask extends TimerTask {
        private int mRemainingStartupCycles = K_STARTUP_SAMPLES;
        private boolean mIsZerod = false;
        private double[] mZeroRateSamples = new double[K_ZEROING_SAMPLES];
        private int mZeroRateSampleIndex = 0;
        private boolean mHasEnoughZeroingSamples;
        private double mZeroBias;
        private double mAngle = 0;
        private double mLastAngle = 0;
        private double mLastTime = 0;

        @Override
        public void run() {
            int reading;
            try {
                reading = mGyroInterface.getReading();
            } catch (GyroInterface.GyroException e) {
                System.out.println("Gyro read failed: " + e.getMessage());
                return;
            }
            GyroInterface.StatusFlag status = GyroInterface.extractStatus(reading);
            List<GyroInterface.ErrorFlag> errors = GyroInterface.extractErrors(reading);
            if (GyroInterface.StatusFlag.VALID_DATA != status || !errors.isEmpty()) {
                System.out.println("Gyro read failed. Status: " + status + ". Errors: " + Util.joinStrings(", ", errors));
                return;
            }

            if (mRemainingStartupCycles > 0) {
                mRemainingStartupCycles--;
                return;
            }

            if (mVolatileShouldReZero) {
                mVolatileShouldReZero = false;
                mVolatileHasData = false;
                mIsZerod = false;
            }

            double unbiasedAngleRate = GyroInterface.extractAngleRate(reading);
            mZeroRateSamples[mZeroRateSampleIndex] = unbiasedAngleRate;
            mZeroRateSampleIndex++;
            if (mZeroRateSampleIndex >= K_ZEROING_SAMPLES) {
                mZeroRateSampleIndex = 0;
                mHasEnoughZeroingSamples = true;
            }
            if (!mIsZerod) {
                if (!mHasEnoughZeroingSamples) {
                    return;
                }
                mZeroBias = 0;
                for (Double sample : mZeroRateSamples) {
                    mZeroBias += sample / K_ZEROING_SAMPLES;
                }
                mAngle = 0;
                mVolatileAngle = 0;
                GyroThread.this.reset();
                mIsZerod = true;
                return;
            }

            double now = edu.wpi.first.wpilibj.Timer.getFPGATimestamp();
            double timeElapsed = mLastTime == 0 ? 1.0 / K_READING_RATE : now - mLastTime;
            mLastTime = now;

            mVolatileRate = unbiasedAngleRate - mZeroBias;
            mAngle += mVolatileRate * timeElapsed;
            mVolatileAngle = mAngle;
            mVolatileHasData = true;
        }
    }
}

@@ -62,16 +62,103 @@
 il.Util;
+%0Aimport edu.wpi.first.wpilibj.Notifier;%0Aimport edu.wpi.first.wpilibj.TimerEventHandler;
 %0A%0Aimport
@@ -699,16 +699,150 @@
 ace();%0A%0A
+    private UpdateHandler mUpdateHandler = new UpdateHandler();%0A    private Notifier mNotifier = new Notifier(mUpdateHandler, this);%0A%0A
     // t
@@ -2575,35 +2575,38 @@
  synchronized (m
-Tim
+Notifi
 er) %7B%0A          
@@ -2616,55 +2616,32 @@
    m
-Timer.schedule(new UpdateTask(), 0, (int) (1000
+Notifier.startPeriodic(1
 .0 /
@@ -2656,17 +2656,16 @@
 NG_RATE)
-)
 ;%0A      
@@ -2792,38 +2792,52 @@
 s Update
-Task extend
+Handler implement
 s Timer
-Task
+EventHandler
  %7B%0A     
@@ -3286,36 +3286,51 @@
     public void 
-run(
+update(Object param
 ) %7B%0A            

Use notifier for gyro