#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <ncurses.h>
 
#define BUF_SIZE 500
 
typedef char S8;
typedef unsigned char U8;
typedef unsigned short U16;
typedef int S32;
typedef unsigned int U32;
typedef float F32;
 
typedef struct _sled_ 
{
 // = 1 when race is on. = 0 when in menus/race stopped …
 S32 IsRaceOn;
 
 // Can overflow to 0 eventually
 U32 TimestampMS;
 F32 EngineMaxRpm;
 F32 EngineIdleRpm;
 F32 CurrentEngineRpm;
 
 // In the car's local space; X = right, Y = up, Z = forward
 F32 AccelerationX;
 F32 AccelerationY;
 F32 AccelerationZ;
 
 // In the car's local space; X = right, Y = up, Z = forward
 F32 VelocityX;
 F32 VelocityY;
 F32 VelocityZ;
 
 // In the car's local space; X = pitch, Y = yaw, Z = roll
 F32 AngularVelocityX;
 F32 AngularVelocityY;
 F32 AngularVelocityZ;
 
 F32 Yaw;
 F32 Pitch;
 F32 Roll;
 
 // Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression
 F32 NormalizedSuspensionTravelFrontLeft;
 F32 NormalizedSuspensionTravelFrontRight;
 F32 NormalizedSuspensionTravelRearLeft;
 F32 NormalizedSuspensionTravelRearRight;
 
 // Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip.
 F32 TireSlipRatioFrontLeft;
 F32 TireSlipRatioFrontRight;
 F32 TireSlipRatioRearLeft;
 F32 TireSlipRatioRearRight;
 
 // Wheels rotation speed radians/sec. 
 F32 WheelRotationSpeedFrontLeft;
 F32 WheelRotationSpeedFrontRight;
 F32 WheelRotationSpeedRearLeft;
 F32 WheelRotationSpeedRearRight;
 
 // = 1 when wheel is on rumble strip, = 0 when off.
 S32 WheelOnRumbleStripFrontLeft;
 S32 WheelOnRumbleStripFrontRight;
 S32 WheelOnRumbleStripRearLeft;
 S32 heelOnRumbleStripRearRight;
 
 // = from 0 to 1, where 1 is the deepest puddle
 F32 WheelInPuddleDepthFrontLeft;
 F32 WheelInPuddleDepthFrontRight;
 F32 WheelInPuddleDepthRearLeft;
 F32 WheelInPuddleDepthRearRight;
 
 // Non-dimensional surface rumble values passed to controller force feedback
 F32 SurfaceRumbleFrontLeft;
 F32 SurfaceRumbleFrontRight;
 F32 SurfaceRumbleRearLeft;
 F32 SurfaceRumbleRearRight;
 
 // Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip.
 F32 TireSlipAngleFrontLeft;
 F32 TireSlipAngleFrontRight;
 F32 TireSlipAngleRearLeft;
 F32 TireSlipAngleRearRight;
 
 // Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip.
 F32 TireCombinedSlipFrontLeft;
 F32 TireCombinedSlipFrontRight;
 F32 TireCombinedSlipRearLeft;
 F32 TireCombinedSlipRearRight;
 
 // Actual suspension travel in meters
 F32 SuspensionTravelMetersFrontLeft;
 F32 SuspensionTravelMetersFrontRight;
 F32 SuspensionTravelMetersRearLeft;
 F32 SuspensionTravelMetersRearRight;
 
 // Unique ID of the car make/model
 S32 CarOrdinal;
 
 // Between 0 (D -- worst cars) and 7 (X class -- best cars) inclusive 
 S32 CarClass;
 
 // Between 100 (worst car) and 999 (best car) inclusive
 S32 CarPerformanceIndex;
 
 // 0 = FWD, 1 = RWD, 2 = AWD
 S32 DrivetrainType;
 
 // Number of cylinders in the engine
 S32 NumCylinders;
} __attribute__((packed)) FORZA_SLED;
 
typedef struct _dash_
{
 // = 1 when race is on. = 0 when in menus/race stopped …
 S32 IsRaceOn;
 
 // Can overflow to 0 eventually
 U32 TimestampMS;
 F32 EngineMaxRpm;
 F32 EngineIdleRpm;
 F32 CurrentEngineRpm;
 
 // In the car's local space; X = right, Y = up, Z = forward
 F32 AccelerationX;
 F32 AccelerationY;
 F32 AccelerationZ;
 
 // In the car's local space; X = right, Y = up, Z = forward
 F32 VelocityX;
 F32 VelocityY;
 F32 VelocityZ;
 
 // In the car's local space; X = pitch, Y = yaw, Z = roll
 F32 AngularVelocityX;
 F32 AngularVelocityY;
 F32 AngularVelocityZ;
 
 F32 Yaw;
 F32 Pitch;
 F32 Roll;
 
 // Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression
 F32 NormalizedSuspensionTravelFrontLeft;
 F32 NormalizedSuspensionTravelFrontRight;
 F32 NormalizedSuspensionTravelRearLeft;
 F32 NormalizedSuspensionTravelRearRight;
 
 // Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip.
 F32 TireSlipRatioFrontLeft;
 F32 TireSlipRatioFrontRight;
 F32 TireSlipRatioRearLeft;
 F32 TireSlipRatioRearRight;
 
 // Wheels rotation speed radians/sec. 
 F32 WheelRotationSpeedFrontLeft;
 F32 WheelRotationSpeedFrontRight;
 F32 WheelRotationSpeedRearLeft;
 F32 WheelRotationSpeedRearRight;
 
 // = 1 when wheel is on rumble strip, = 0 when off.
 S32 WheelOnRumbleStripFrontLeft;
 S32 WheelOnRumbleStripFrontRight;
 S32 WheelOnRumbleStripRearLeft;
 S32 heelOnRumbleStripRearRight;
 
 // = from 0 to 1, where 1 is the deepest puddle
 F32 WheelInPuddleDepthFrontLeft;
 F32 WheelInPuddleDepthFrontRight;
 F32 WheelInPuddleDepthRearLeft;
 F32 WheelInPuddleDepthRearRight;
 
 // Non-dimensional surface rumble values passed to controller force feedback
 F32 SurfaceRumbleFrontLeft;
 F32 SurfaceRumbleFrontRight;
 F32 SurfaceRumbleRearLeft;
 F32 SurfaceRumbleRearRight;
 
 // Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip.
 F32 TireSlipAngleFrontLeft;
 F32 TireSlipAngleFrontRight;
 F32 TireSlipAngleRearLeft;
 F32 TireSlipAngleRearRight;
 
 // Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip.
 F32 TireCombinedSlipFrontLeft;
 F32 TireCombinedSlipFrontRight;
 F32 TireCombinedSlipRearLeft;
 F32 TireCombinedSlipRearRight;
 
 // Actual suspension travel in meters
 F32 SuspensionTravelMetersFrontLeft;
 F32 SuspensionTravelMetersFrontRight;
 F32 SuspensionTravelMetersRearLeft;
 F32 SuspensionTravelMetersRearRight;
 
 // Unique ID of the car make/model
 S32 CarOrdinal;
 
 // Between 0 (D -- worst cars) and 7 (X class -- best cars) inclusive 
 S32 CarClass;
 
 // Between 100 (worst car) and 999 (best car) inclusive
 S32 CarPerformanceIndex;
 
 // 0 = FWD, 1 = RWD, 2 = AWD
 S32 DrivetrainType;
 
 // Number of cylinders in the engine
 S32 NumCylinders;
 
 S32 reserved1;
 S32 reserved2;
 S32 reserved3;
 
 // add for DASH
 F32 PositionX;
 F32 PositionY;
 F32 PositionZ;
 F32 Speed;
 F32 Power;
 F32 Torque;
 F32 TireTempFrontLeft;
 F32 TireTempFrontRight;
 F32 TireTempRearLeft;
 F32 TireTempRearRight;
 F32 Boost;
 F32 Fuel;
 F32 DistanceTraveled;
 F32 BestLap;
 F32 LastLap;
 F32 CurrentLap;
 F32 CurrentRaceTime;
 U16 LapNumber;
 U8 RacePosition;
 U8 Accel;
 U8 Brake;
 U8 Clutch;
 U8 HandBrake;
 U8 Gear;
 S8 Steer;
 S8 NormalizedDrivingLine;
 S8 NormalizedAIBrakeDifference;
 
 F32 TireWearFrontLeft;
 F32 TireWearFrontRight;
 F32 TireWearRearLeft;
 F32 TireWearRearRight;
 
 // ID for track
 S32 TrackOrdinal;
} __attribute__((packed)) FORZA_DASH;
 
int last_raceon = 0;
void parse_forza_dbg(unsigned char *message)
{
 int row = 0;
 FORZA_DASH forza;
 memcpy(&forza, message, sizeof(FORZA_DASH));
 
 for(int i = 0; i < 324; i++)
 {
 if(i % 16 == 0)
 printf("\n");
 printf("%02X ",message[i]);
 }
 printf("\n");
 
 printf("NumCylinders %d\n", forza.NumCylinders);
 
 printf("PositionX %f\n", forza.PositionX);
 printf("PositionY %f\n", forza.PositionY);
 printf("PositionZ %f\n", forza.PositionZ);
 
 printf("speed %f\n", forza.Speed);
 
 printf("clutch %d\n", forza.Clutch);
 printf("brake %d\n", forza.Brake);
 printf("accel %d\n", forza.Accel);
 printf("HandBrake %d\n", forza.HandBrake);
}
 
void parse_forza(unsigned char *message)
{
 int row = 0;
 FORZA_DASH forza;
 memcpy(&forza, message, sizeof(FORZA_DASH));
 
 static unsigned int pack_cnt = 0;
 
 if(last_raceon != forza.IsRaceOn)
 {
 clear();
 last_raceon = forza.IsRaceOn;
 }
 
 if(forza.IsRaceOn)
 {
 mvprintw(row++, 0, "let's fly %d\n",pack_cnt++);
 
 mvprintw(row++, 0, "RPM %.0f / %.0f", forza.CurrentEngineRpm, forza.EngineMaxRpm);
 
 mvprintw(row++, 0, "acc X %f", forza.AccelerationX);
 mvprintw(row++, 0, "acc Y %f", forza.AccelerationY);
 mvprintw(row++, 0, "acc Z %f", forza.AccelerationZ);
 
 mvprintw(row++, 0, "vel X %f", forza.VelocityX);
 mvprintw(row++, 0, "vel Y %f", forza.VelocityY);
 mvprintw(row++, 0, "vel Z %f", forza.VelocityZ);
 
 mvprintw(row++, 0, "ang vel X %f", forza.AngularVelocityX);
 mvprintw(row++, 0, "ang vel Y %f", forza.AngularVelocityY);
 mvprintw(row++, 0, "ang vel Z %f", forza.AngularVelocityZ);
 
 mvprintw(row++, 0, "yaw %f", forza.Yaw);
 mvprintw(row++, 0, "roll %f", forza.Pitch);
 mvprintw(row++, 0, "pitch %f", forza.Roll);
 
 mvprintw(row++, 0, "CarOrdinal %5d", forza.CarOrdinal);
 mvprintw(row++, 0, "CarClass %5d", forza.CarClass);
 mvprintw(row++, 0, "CarPerformanceIndex %5d", forza.CarPerformanceIndex);
 mvprintw(row++, 0, "DrivetrainType %5d", forza.DrivetrainType);
 mvprintw(row++, 0, "NumCylinders %5d", forza.NumCylinders);
 
 mvprintw(row++, 0, "clutch[%3d] brake[%3d] accel[%3d] handbrake[%3d] gear[%2d] Steer[%3d]"
 , forza.Clutch
 , forza.Brake
 , forza.Accel
 , forza.HandBrake
 , forza.Gear
 , forza.Steer
 );
 
 mvprintw(row++, 0, "Speed[%5.0f] Power[%7.0f] Torque[%5.0f] Boost[%5.0f] Fuel[%5.0f]"
 , forza.Speed
 , forza.Power
 , forza.Torque
 , forza.Boost
 , forza.Fuel
 );
 
 mvprintw(row++, 0, "Position %f , %f , %f"
 , forza.PositionX
 , forza.PositionY
 , forza.PositionZ
 );
 }
 else mvprintw(row++, 0, "pause");
 
 refresh();
}
 
void error_handling(char *message);
 
int main(int argc, char *argv[]){
 int serv_sock;
 char message[BUF_SIZE];
 int str_len;
 socklen_t clnt_adr_sz;
 
 struct sockaddr_in serv_adr, clnt_adr;
 
 printf("%d sizeof(FORZA_SLED)\n", sizeof(FORZA_SLED));
 printf("%d sizeof(FORZA_DASH)\n", sizeof(FORZA_DASH));
 
 if(argc!=2){
 printf("Usage:%s <port>\n", argv[0]);
 exit(1);
 }
 
 serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
 if(serv_sock == -1)
 error_handling("UDP socket creation error");
 
 memset(&serv_adr, 0, sizeof(serv_adr));
 serv_adr.sin_family=AF_INET;
 serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
 serv_adr.sin_port=htons(atoi(argv[1]));
 
 if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
 error_handling("bind() error");
 
 initscr();
 
 while(1){
 clnt_adr_sz = sizeof(clnt_adr);
 str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
 message[str_len] = 0x00;
 // printf("%d received [%s]\n", str_len, message);
 // printf("%d received, %d sizeof(FORZA_DASH)\n", str_len, sizeof(FORZA_DASH));
 parse_forza(message);
 // parse_forza_dbg(message);
 sendto(serv_sock, message, str_len, 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz);
 }
 close(serv_sock);
 return 0;
}
 
void error_handling(char *message){
 fputs(message, stderr);
 fputc('\n', stderr);
 
 endwin();
 exit(1);
}