// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/HeadTrackingRequestCodes.java
// public class HeadTrackingRequestCodes {
// 
//     public static int Disconnect = 255;
// 
//     public static int ChangeState = 254;
//     public static int Recenter = 50;
// 
//     public static int SendPoseMatrixRotationOnly = 0;
//     public static int SendPoseMatrixFull = 6;
//     public static int SendRotationMatrix = 1;
//     public static int SendRadRotationAndPosition = 3;
//     public static int SendQuatRotationAndPosition = 4;
//     public static int SendPositionOnly = 5;
// 
//     public static int RequestSyncOffset = 100;
//     public static int RequestReadOnlyPose = 199;
//     public static int RequestReadOnlyPhonePose = 200;
// 
//     public static int SetYawOffset = 201;
//     public static int ResetYawOffset = 21;
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/HeadTrackingResponseCodes.java
// public class HeadTrackingResponseCodes {
// 
//     public static int AcceptedYourData = 0;
// 
//     public static int SendingCurrentTrackedPose = 2;
// 
//     public static int PhoneDataTimeout = 253;
//     public static int BadRequest = 254;
//     public static int Disconnecting = 255;
// 
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/TrackedDeviceStatus.java
// public class TrackedDeviceStatus {
//     public static final byte Active = 0;
//     public static final byte TempUnavailable = 1;
//     public static final byte Disabled = 2;
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/APILogger.java
// public class APILogger {
// 
//     private static LinkedList<ILog> loggers = new LinkedList<ILog>();
// 
//     public static void AddLogListener(ILog logger){
//         loggers.add(logger);
//     }
// 
//     public static void debug(String s){
//         for (ILog log : loggers) {
//             log.debug(s);
//         }
//     }
// 
//     public static void zmq(String s) {
//         //debug("ZMQ: " + s);
//     }
// 
//     public static void error(String s) {
//         for (ILog log : loggers) {
//             log.error(s);
//         }
//     }
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/SerializationUtils.java
// public class SerializationUtils {
// 
//     public static ByteString byteStringFromFloats(float...args){
//         return ByteString.copyFrom(byteArrayFromFloats(args));
//     }
// 
//     public static byte[] byteArrayFromFloats(float... args){
//         ByteBuffer data = ByteBuffer.allocate(args.length * 4);
//         data.order(ByteOrder.LITTLE_ENDIAN);
//         for (float arg : args) {
//             data.putFloat(arg);
//         }
// 
//         return data.array();
//     }
// 
//     public static ByteString byteStringFromFloatArray(float[] array){
//         ByteBuffer data = ByteBuffer.allocate(array.length * 4);
//         data.order(ByteOrder.LITTLE_ENDIAN);
//         for (float arg : array) {
//             data.putFloat(arg);
//         }
// 
//         return ByteString.copyFrom(data.array());
//     }
// }


import com.google.protobuf.ByteString;
import com.riftcat.vridge.api.client.java.codes.HeadTrackingRequestCodes;
import com.riftcat.vridge.api.client.java.codes.HeadTrackingResponseCodes;
import com.riftcat.vridge.api.client.java.codes.TrackedDeviceStatus;
import com.riftcat.vridge.api.client.java.proto.HeadTrackingRequest;
import com.riftcat.vridge.api.client.java.proto.HeadTrackingResponse;
import com.riftcat.vridge.api.client.java.proto.TrackedPose;
import com.riftcat.vridge.api.client.java.utils.APILogger;
import com.riftcat.vridge.api.client.java.utils.SerializationUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.TimeoutException;

        HeadTrackingRequest request = HeadTrackingRequest
                .newBuilder()
                .setVersion(CurrentVersion)
                .setTaskType(HeadTrackingRequestCodes.RequestReadOnlyPose)
                .build();

        HeadTrackingResponse reply = sendMessage(request);

        if(reply.getReplyCode() == HeadTrackingResponseCodes.SendingCurrentTrackedPose){
            return reply.getTrackedPose();
        }
        else{
            return null;
        }
    }

    public void setYawOffset(float yaw) throws TimeoutException {
        HeadTrackingRequest request = HeadTrackingRequest
                .newBuilder()
                .setVersion(CurrentVersion)
                .setTaskType(HeadTrackingRequestCodes.SetYawOffset)
                .setData(SerializationUtils.byteStringFromFloats(yaw))
                .build();

        sendMessage(request);
    }

    public void changeTrackingState(boolean isInTrackingRange) throws TimeoutException {
        byte[] state = new byte[1];
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/HeadTrackingRequestCodes.java
// public class HeadTrackingRequestCodes {
// 
//     public static int Disconnect = 255;
// 
//     public static int ChangeState = 254;
//     public static int Recenter = 50;
// 
//     public static int SendPoseMatrixRotationOnly = 0;
//     public static int SendPoseMatrixFull = 6;
//     public static int SendRotationMatrix = 1;
//     public static int SendRadRotationAndPosition = 3;
//     public static int SendQuatRotationAndPosition = 4;
//     public static int SendPositionOnly = 5;
// 
//     public static int RequestSyncOffset = 100;
//     public static int RequestReadOnlyPose = 199;
//     public static int RequestReadOnlyPhonePose = 200;
// 
//     public static int SetYawOffset = 201;
//     public static int ResetYawOffset = 21;
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/HeadTrackingResponseCodes.java
// public class HeadTrackingResponseCodes {
// 
//     public static int AcceptedYourData = 0;
// 
//     public static int SendingCurrentTrackedPose = 2;
// 
//     public static int PhoneDataTimeout = 253;
//     public static int BadRequest = 254;
//     public static int Disconnecting = 255;
// 
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/TrackedDeviceStatus.java
// public class TrackedDeviceStatus {
//     public static final byte Active = 0;
//     public static final byte TempUnavailable = 1;
//     public static final byte Disabled = 2;
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/APILogger.java
// public class APILogger {
// 
//     private static LinkedList<ILog> loggers = new LinkedList<ILog>();
// 
//     public static void AddLogListener(ILog logger){
//         loggers.add(logger);
//     }
// 
//     public static void debug(String s){
//         for (ILog log : loggers) {
//             log.debug(s);
//         }
//     }
// 
//     public static void zmq(String s) {
//         //debug("ZMQ: " + s);
//     }
// 
//     public static void error(String s) {
//         for (ILog log : loggers) {
//             log.error(s);
//         }
//     }
// }
// 
// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/SerializationUtils.java
// public class SerializationUtils {
// 
//     public static ByteString byteStringFromFloats(float...args){
//         return ByteString.copyFrom(byteArrayFromFloats(args));
//     }
// 
//     public static byte[] byteArrayFromFloats(float... args){
//         ByteBuffer data = ByteBuffer.allocate(args.length * 4);
//         data.order(ByteOrder.LITTLE_ENDIAN);
//         for (float arg : args) {
//             data.putFloat(arg);
//         }
// 
//         return data.array();
//     }
// 
//     public static ByteString byteStringFromFloatArray(float[] array){
//         ByteBuffer data = ByteBuffer.allocate(array.length * 4);
//         data.order(ByteOrder.LITTLE_ENDIAN);
//         for (float arg : array) {
//             data.putFloat(arg);
//         }
// 
//         return ByteString.copyFrom(data.array());
//     }
// }


// Path: src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/proxy/HeadTrackingProxy.java
import com.google.protobuf.ByteString;
import com.riftcat.vridge.api.client.java.codes.HeadTrackingRequestCodes;
import com.riftcat.vridge.api.client.java.codes.HeadTrackingResponseCodes;
import com.riftcat.vridge.api.client.java.codes.TrackedDeviceStatus;
import com.riftcat.vridge.api.client.java.proto.HeadTrackingRequest;
import com.riftcat.vridge.api.client.java.proto.HeadTrackingResponse;
import com.riftcat.vridge.api.client.java.proto.TrackedPose;
import com.riftcat.vridge.api.client.java.utils.APILogger;
import com.riftcat.vridge.api.client.java.utils.SerializationUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.TimeoutException;


        HeadTrackingRequest request = HeadTrackingRequest
                .newBuilder()
                .setVersion(CurrentVersion)
                .setTaskType(HeadTrackingRequestCodes.RequestReadOnlyPose)
                .build();

        HeadTrackingResponse reply = sendMessage(request);

        if(reply.getReplyCode() == HeadTrackingResponseCodes.SendingCurrentTrackedPose){
            return reply.getTrackedPose();
        }
        else{
            return null;
        }
    }

    public void setYawOffset(float yaw) throws TimeoutException {
        HeadTrackingRequest request = HeadTrackingRequest
                .newBuilder()
                .setVersion(CurrentVersion)
                .setTaskType(HeadTrackingRequestCodes.SetYawOffset)
                .setData(SerializationUtils.byteStringFromFloats(yaw))
                .build();

        sendMessage(request);
    }

    public void changeTrackingState(boolean isInTrackingRange) throws TimeoutException {
        byte[] state = new byte[1];
        state[0] = isInTrackingRange ? TrackedDeviceStatus.Active : TrackedDeviceStatus.TempUnavailable;