#include <stdio.h>
#include <string.h>
#include <vector>
#include "htra_api.h"
#include <fstream>
#include<iostream>
#include <filesystem>
#include"msgpack.h"

using namespace std;

//16-bit unsigned integer byte order reversal.
uint16_t DET_swap_16(uint16_t val)
{
    return ((val & 0x00ff) << 8) | ((val & 0xff00) >> 8);
}
//Big-endian to little-endian for uint64_t.
uint64_t swap_64(uint64_t* val)
{
    return ((*val) & ((uint64_t)0x00000000000000ffULL << 56) |

        (((uint64_t)(*val) & (uint64_t)0x000000000000ff00ULL) << 40) |
        
		(((uint64_t)(*val) & (uint64_t)0x0000000000ff0000ULL) << 24) |
 
		(((uint64_t)(*val) & (uint64_t)0x00000000ff000000ULL) << 8) |
        
		(((uint64_t)(*val) & (uint64_t)0x000000ff00000000ULL) >> 8) |
            
		(((uint64_t)(*val) & (uint64_t)0x0000ff0000000000ULL) >> 24) |
            
		(((uint64_t)(*val) & (uint64_t)0x00ff000000000000ULL) >> 40) |
            
		(((uint64_t)(*val) & (uint64_t)0xff00000000000000ULL) >> 56));
}
//Big-endian to little-endian for uint32_t.
uint32_t swap_32(uint32_t* val)
{

	return ((0xff000000 & *val << 24) \
		| (0x00ff0000 & *val << 8) \
		| (0x0000ff00 & *val >> 8) \
		| (0x000000ff & *val >> 24));
}

//Big-endian to little-endian for float type.
float DET_convertEndianFloat(float value) {                                 
    union {
        float f;
        uint32_t i;
    } u;
    u.f = value;
    u.i = swap_32(&u.i);
    return u.f;
}
//Big-endian to little-endian for double type.
double DET_convertEndianDouble(double value) {
    union {
        double d;
        uint64_t l;
    } u;
    u.d = value;
    u.l = swap_64(&u.l);
    return u.d;
}

int Device_GetDETDataInfo(const char* FilePath, DET_Profile_TypeDef* DET_Profile, DET_StreamInfo_TypeDef* DET_StreamInfo)
{
    int Status = 0;                                  // Used to return the function's status.
    fstream File;                                    // Create a file stream object to read the file.
    string FilePath_s = FilePath;                    // FilePath should be the address passed in.
    File.open(FilePath_s, ios::in | ios::binary);     // Open the file in binary read mode.

    if (!File.is_open()) // Check if the file was successfully opened.
    {
        cout << "The file failed to be opened, please check whether there is a record file under the path!" << endl;
        return Status;
    }
    char DataFormat_temp[2]; // Array to read two bytes.
    uint16_t DataByte = 0;   // Used to store the uint16_t type value.

    // Read DET_Profile and DET_StreamInfo.
    File.seekp(108, ios::beg);
    File.read(DataFormat_temp, sizeof(DataFormat_temp));
    memcpy(&DataByte, DataFormat_temp, sizeof(DataFormat_temp));
    DataByte = ((DataByte & 0x00ff) << 8) | ((DataByte & 0xff00) >> 8); // The byte size for DET_Profile and DET_StreamInfo.

    char* coninfo = new char[DataByte]; // Allocate array for the byte size of DET_Profile and DET_StreamInfo.
    vector<char> packdata(DataByte);    // Used to store the data after endian conversion for DET_Profile and DET_StreamInfo.
    File.read(coninfo, packdata.size());
    memcpy(packdata.data(), coninfo, packdata.size());

    msgpack_unpacked msg;
    msgpack_unpacked_init(&msg);
    size_t off = 0;

    msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off); // Decompress the corresponding structure parameters.
    DET_Profile->CenterFreq_Hz = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->RefLevel_dBm = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->DecimateFactor = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->RxPort = (RxPort_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->BusTimeout_ms = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerSource = (IQS_TriggerSource_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerEdge = (TriggerEdge_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerMode = (TriggerMode_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerLength = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerOutMode = (TriggerOutMode_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerOutPulsePolarity = (TriggerOutPulsePolarity_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerLevel_dBm = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerLevel_SafeTime = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerDelay = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->PreTriggerTime = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerTimerSync = (TriggerTimerSync_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->TriggerTimer_Period = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->EnableReTrigger = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->ReTrigger_Period = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->ReTrigger_Count = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->Detector = (Detector_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->DetectRatio = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->GainStrategy = (GainStrategy_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->Preamplifier = (PreamplifierState_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->AnalogIFBWGrade = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->IFGainGrade = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->EnableDebugMode = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->ReferenceClockSource = (ReferenceClockSource_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->ReferenceClockFrequency = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->EnableReferenceClockOut = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->SystemClockSource = (SystemClockSource_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->ExternalSystemClockFrequency = msg.data.via.f64;


	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->Atten = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->DCCancelerMode = (DCCancelerMode_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->QDCMode = (QDCMode_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->QDCIGain = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->QDCQGain = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->QDCPhaseComp = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->DCCIOffset = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->DCCQOffset = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_Profile->LOOptimization = (LOOptimization_TypeDef)msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_StreamInfo->PacketCount = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_StreamInfo->StreamSamples = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_StreamInfo->StreamDataSize = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_StreamInfo->PacketSamples = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_StreamInfo->PacketDataSize = msg.data.via.u64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_StreamInfo->TimeResolution = msg.data.via.f64;

	msgpack_unpack_next(&msg, packdata.data(), (size_t)packdata.size(), &off);
	DET_StreamInfo->GainParameter = msg.data.via.u64;

	File.close();
	return Status;
}

int Device_GetOneCountDataInfo(const char* FilePath, DET_TriggerInfo_TypeDef* DET_TriggerInfo, MeasAuxInfo_TypeDef* MeasAuxInfo, float NormalizedPowerStream[], uint32_t* PackIndex, float* ScalToV)
{
    cout << "Reading the " << (*PackIndex) << " packet " << endl;

    int Status = 0;                                   // Initialize status code, 0 means success.
    fstream File;                                     // Create fstream object File for file operations.
    string FilePath_s = FilePath;                     // Convert file path to string type.
    File.open(FilePath_s, ios::in | ios::binary);     // Attempt to open the file in binary input mode.

    // If file failed to open.
    if (!File.is_open())
    {
        cout << "The file failed to be opened, please check whether there is a record file under the path!" << endl;
        return Status;
    }

    uint16_t DataByte = 0;
    char Type[1];              // Used to read one byte of data.
    char DataFormat_temp[2];   // Used to read two bytes of data.
    char TrigSize[4];          // Used to read four bytes of data.
    char trig[8];              // Used to read eight bytes of data.

    File.seekp(408 + (*PackIndex) * (389), ios::beg);              // Set file pointer to 408 bytes position, ios::beg indicates from the beginning of the file.
    File.read(DataFormat_temp, sizeof(DataFormat_temp));           // DeviceState struct size.
    memcpy(&DataByte, DataFormat_temp, sizeof(DataFormat_temp));   // Store into structure.
    DataByte = DET_swap_16(DataByte);

    File.read(trig, sizeof(trig));                                                                                     // Read DET_TriggerInfo counting information.
    memcpy(&DET_TriggerInfo->SysTimerCountOfFirstDataPoint, trig, sizeof(trig));                                        // Store into DET_TriggerInfo structure.
    DET_TriggerInfo->SysTimerCountOfFirstDataPoint = swap_64(&DET_TriggerInfo->SysTimerCountOfFirstDataPoint);          // Byte order conversion.

    // Read data format information and save to structure.
    File.read(DataFormat_temp, sizeof(DataFormat_temp));                                                                 // Read data format size.
    memcpy(&DET_TriggerInfo->InPacketTriggeredDataSize, DataFormat_temp, sizeof(DataFormat_temp));                      // Store into DET_TriggerInfo structure.
    DET_TriggerInfo->InPacketTriggeredDataSize = ((DET_TriggerInfo->InPacketTriggeredDataSize & 0x00ff) << 8) | ((DET_TriggerInfo->InPacketTriggeredDataSize & 0xff00) >> 8); // Byte order conversion.

    File.read(DataFormat_temp, sizeof(DataFormat_temp));                                                                 // Read data format size.
    memcpy(&DET_TriggerInfo->InPacketTriggerEdges, DataFormat_temp, sizeof(DataFormat_temp));                            // Store into DET_TriggerInfo structure.
    DET_TriggerInfo->InPacketTriggerEdges = ((DET_TriggerInfo->InPacketTriggerEdges & 0x00ff) << 8) | ((DET_TriggerInfo->InPacketTriggerEdges & 0xff00) >> 8); // Byte order conversion.

    for (int i = 0; i < 25; i++) // Read 25 DET_TriggerInfo start data indices and store in structure.
    {
        File.read(TrigSize, sizeof(TrigSize));  
        memcpy(&DET_TriggerInfo->StartDataIndexOfTriggerEdges[i], TrigSize, sizeof(TrigSize));
    }

    for (int i = 0; i < 25; i++) // Read 25 DET_TriggerInfo.SysTimerCountOfEdges and store in structure.
    {
        File.read(trig, sizeof(trig));
        memcpy(&DET_TriggerInfo->SysTimerCountOfEdges[i], trig, sizeof(trig));
    }

    for (int i = 0; i < 25; i++) // Read 25 DET_TriggerInfo->EdgeType and store in structure.
    {
        File.read(Type, sizeof(Type));
        memcpy(&DET_TriggerInfo->EdgeType[i], Type, sizeof(Type));
    }

    File.read(DataFormat_temp, sizeof(DataFormat_temp));  // Move file pointer, skip 2 bytes.

    // Continue comments as above.
    File.read(TrigSize, sizeof(TrigSize));
    memcpy(&MeasAuxInfo->MaxIndex, TrigSize, sizeof(TrigSize));
    MeasAuxInfo->MaxIndex = swap_32(&MeasAuxInfo->MaxIndex);

    File.read(TrigSize, sizeof(TrigSize));
    memcpy(&MeasAuxInfo->MaxPower_dBm, TrigSize, sizeof(TrigSize));
    MeasAuxInfo->MaxPower_dBm = DET_convertEndianFloat(MeasAuxInfo->MaxPower_dBm);

    File.read(DataFormat_temp, sizeof(DataFormat_temp));
    memcpy(&MeasAuxInfo->Temperature, DataFormat_temp, sizeof(DataFormat_temp));
    MeasAuxInfo->Temperature = ((MeasAuxInfo->Temperature & 0x00ff) << 8) | ((MeasAuxInfo->Temperature & 0xff00) >> 8);

    File.read(DataFormat_temp, sizeof(DataFormat_temp));
    memcpy(&MeasAuxInfo->RFState, DataFormat_temp, sizeof(DataFormat_temp));
    MeasAuxInfo->RFState = ((MeasAuxInfo->RFState & 0x00ff) << 8) | ((MeasAuxInfo->RFState & 0xff00) >> 8);

    File.read(DataFormat_temp, sizeof(DataFormat_temp));
    memcpy(&MeasAuxInfo->BBState, DataFormat_temp, sizeof(DataFormat_temp));
    MeasAuxInfo->BBState = ((MeasAuxInfo->BBState & 0x00ff) << 8) | ((MeasAuxInfo->BBState & 0xff00) >> 8);

    File.read(DataFormat_temp, sizeof(DataFormat_temp));
    memcpy(&MeasAuxInfo->GainPattern, DataFormat_temp, sizeof(DataFormat_temp));
    MeasAuxInfo->GainPattern = ((MeasAuxInfo->GainPattern & 0x00ff) << 8) | ((MeasAuxInfo->GainPattern & 0xff00) >> 8);

    File.read(TrigSize, sizeof(TrigSize));
    memcpy(&MeasAuxInfo->ConvertPattern, TrigSize, sizeof(TrigSize));
    MeasAuxInfo->ConvertPattern = swap_32(&MeasAuxInfo->ConvertPattern);

    File.read(trig, sizeof(trig));
    memcpy(&MeasAuxInfo->SysTimeStamp, trig, sizeof(trig));
    MeasAuxInfo->SysTimeStamp = DET_convertEndianDouble(MeasAuxInfo->SysTimeStamp);

    File.read(trig, sizeof(trig));
    memcpy(&MeasAuxInfo->AbsoluteTimeStamp, trig, sizeof(trig));
    MeasAuxInfo->AbsoluteTimeStamp = DET_convertEndianDouble(MeasAuxInfo->AbsoluteTimeStamp);

    File.read(TrigSize, sizeof(TrigSize));
    memcpy(&MeasAuxInfo->Latitude, TrigSize, sizeof(TrigSize));
    MeasAuxInfo->Latitude = DET_convertEndianFloat(MeasAuxInfo->Latitude);

    File.read(TrigSize, sizeof(TrigSize));
    memcpy(&MeasAuxInfo->Longitude, TrigSize, sizeof(TrigSize));
    MeasAuxInfo->Longitude = DET_convertEndianFloat(MeasAuxInfo->Longitude);

    File.read(TrigSize, sizeof(TrigSize));
    memcpy(ScalToV, TrigSize, sizeof(TrigSize));
    *ScalToV = DET_convertEndianFloat(*ScalToV);

    // Read DET data.
    uint32_t SamplePoints = 0; // Total number of points.
    uint32_t DataSize = 0;     // Store data size.

    File.seekp(25 * 1024 * 1024 + 404, ios::beg);   // Locate to specific position in file, 25MB + 404 bytes.
    File.read(TrigSize, sizeof(TrigSize));          // Read data into TrigSize array.
    memcpy(&DataSize, TrigSize, sizeof(TrigSize));  // Extract data size from TrigSize array, DataSize stores the read size.

    SamplePoints = DataSize / sizeof(int16_t) / 2;   // Calculate SamplePoints, assuming each data packet contains two int16_t elements.

    File.seekp(25 * 1024 * 1024 + 408 + ((*PackIndex) * 64968), ios::beg); // According to current packet index, locate to specific position in file.

    char DET_temp[4];        // Define a temporary buffer to store 4 bytes of data read from file.
    if (SamplePoints <= 16242) // If SamplePoints is less than or equal to 16242, indicating single packet of data.
    {
        for (uint32_t i = 0; i < SamplePoints; i++) // Read data one by one and store into NormalizedPowerStream.
        {
            File.read(DET_temp, sizeof(DET_temp));
            memcpy(&NormalizedPowerStream[i], DET_temp, sizeof(DET_temp));
        }
    }
	else                                            // If SamplePoints is greater than 16242, it indicates that the data packet consists of multiple packets.
	{
		uint32_t packcount = SamplePoints / 16242;  // Calculate the number of packets, assuming each packet contains 16242 points.
		if (*PackIndex < packcount)                 // If the current packet index is less than the total number of packets, continue reading data.
		{
			for (uint32_t i = 0; i < 16242; i++)    // Read one complete packet of data (16242 points).
			{
				File.read(DET_temp, sizeof(DET_temp));
				memcpy(&NormalizedPowerStream[i], DET_temp, sizeof(DET_temp)); // Store the read data into the NormalizedPowerStream array.
			}
		}
		else
		{
			cout << "The package index has exceeded the total number of packages!" << endl;
		}
	}
	
	(*PackIndex)++;  // Increment the packet index to prepare for reading the next packet.
	File.close();    // Close the file.
	return Status;
}
	
int DETMode_PlayBack()
{
    int Status = 0; // Define the return value variable for the function.
    char FilePath[50];

    string s = "./data/0029_20241231_170636.part1.det.wav"; // Modify the record file name as needed, e.g., 0022_20241225_171801.part1.det.wav.
    strcpy_s(FilePath, s.c_str());

    string FilePath_last = "./data/";          // File save path is ./data/.
    if (!(filesystem::exists(FilePath_last)))  // Check if the directory exists, if not, create it.
    {
        filesystem::create_directories(FilePath_last); // Try to create the directory and its parent directories.
        cout << "dataFolder address :Htra_RecordingandPlayBack\\data" << endl;
        if (filesystem::create_directories(FilePath_last)) {
            cout << "Failed to create a directory: \"" << FilePath_last << "\" " << endl;
            return 0;
        }
    }

    DET_Profile_TypeDef  DET_Profile;          // Store the input configuration of the record file, including center frequency, decimation factor, reference level, etc.
    DET_StreamInfo_TypeDef  DET_StreamInfo;    // Store the DET data-related information under the configuration of the record file, including the number of DET data points, byte size, etc.
    DET_TriggerInfo_TypeDef   DET_TriggerInfo; // Store the trigger-related information under the configuration of the record file.
    MeasAuxInfo_TypeDef  MeasAuxInfo;          // Store auxiliary measurement information from the record file, including: max power index, max power, device temperature, latitude and longitude, absolute timestamp, etc.
    float ScaleToV = 0;                        // Store the scale factor for absolute amplitude (in V) from the record file.
    uint32_t PackIndex = 0;                    // Current packet index.

    Device_GetDETDataInfo(FilePath, &DET_Profile, &DET_StreamInfo); // Get the configuration for this streaming record.

    vector<float> NormalizedPowerStream(DET_StreamInfo.StreamSamples); // Store the DET normalized data.

    char FileName[255];                             // Define a character array for the file name.
    sprintf_s(FileName, "./data/DETMode_Data.txt"); // Format the file name as "DETMode_Data.txt".
    fstream File1;                                  // Create a fstream object for file operations.
    File1.open(FileName, ios::out);                 // Open the file in write mode.

    for (int i = 0; i < DET_StreamInfo.PacketCount; i++)
    {
        Device_GetOneCountDataInfo(FilePath, &DET_TriggerInfo, &MeasAuxInfo, NormalizedPowerStream.data(), &PackIndex, &ScaleToV); // Call to get one packet of data and corresponding information each time.

        for (int i = 0; i < DET_StreamInfo.PacketSamples; i++)
        {
            NormalizedPowerStream[i] = 10 * log10(20 * pow(NormalizedPowerStream[i] * ScaleToV, 2)); // Convert DET data units to dBm.
            File1 << NormalizedPowerStream[i] << "\n"; // Save the dBm-formatted DET data into File1.
        }
    }

    File1.close(); // Close the file stream, save, and release resources.

    return 0;
}






