#pragma once

#ifndef __INTELLISENSE__
#	define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
#	define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
#else
#	define MODULE_EXPORT
#	define MODULE_WASM_MODNAME(mod)
#	define __attribute__(x)
#	define __restrict__
#endif

/********************************* Headers *********************************/
// MSFS headers
#include <MSFS/MSFS_WindowsTypes.h>
#include <SimConnect.h>
// C headers
#include <math.h>
// C++ headers
#include <algorithm>
// Own headers
#include "shared.h"

/******************************** Constants ********************************/
// 2x 190lbs default
#define FRONT_CREW_WEIGHT(IS_IMPERIAL) ((IS_IMPERIAL) ? (760.0) : (344.0))
// 2x 190lbs default
#define REAR_CREW_WEIGHT(IS_IMPERIAL) ((IS_IMPERIAL) ? (760.0) : (344.0))
// All actual Business seats
#define MAX_BUSINESS_1 18
// First 5 rows of economy
#define MAX_BUSINESS_2 45
// Next 10 rows
#define MAX_ECONOMY_1 86
// Remaining rows
// 12 x 9
// 6 x 8
// 4 x 2
#define MAX_ECONOMY_2 164
// Total
#define MAX_PAX 313
// Max ZFW
#define MAX_PAX_ZFW(IS_IMPERIAL) ((IS_IMPERIAL) ? (400000) : (181437))
// Arms
#define ARM_PAX_CABIN_CREW_FRONT 792.0
#define ARM_PAX_BUSINESS1_LEFT 540.0
#define ARM_PAX_BUSINESS1_CENTER 540.0
#define ARM_PAX_BUSINESS1_RIGHT 540.0
#define ARM_PAX_BUSINESS2_LEFT 300.0
#define ARM_PAX_BUSINESS2_CENTER 300.0
#define ARM_PAX_BUSINESS2_RIGHT 300.0
#define ARM_PAX_ECONOMY1_LEFT -240.0
#define ARM_PAX_ECONOMY1_CENTER -240.0
#define ARM_PAX_ECONOMY1_RIGHT -240.0
#define ARM_PAX_ECONOMY2_LEFT -600.0
#define ARM_PAX_ECONOMY2_CENTER -600.0
#define ARM_PAX_ECONOMY2_RIGHT -600.0
#define ARM_PAX_CABIN_CREW_REAR -660.0

/***************************** Data structures *****************************/
typedef struct {
    // SimConnect mapped
    double pilot;
    double firstOfficer;
    double engineer;
    double cabinCrewFront;
    double business1Left;
    double business1Center;
    double business1Right;
    double business2Left;
    double business2Center;
    double business2Right;
    double economy1Left;
    double economy1Center;
    double economy1Right;
    double economy2Left;
    double economy2Center;
    double economy2Right;
    double cabinCrewRear;
    double forwardCargo;
    double rearCargo;
    double leftAux;
    double rightAux;

    // Additional properties
    double empty;
    double total;
    double CGTarget;
    double ZFWCG;
    double TOCG;
    struct paxCount {
        unsigned char business1;
        unsigned char business2;
        unsigned char economy1;
        unsigned char economy2;
        unsigned short total;
    } paxCount;
    struct sbPlanned {
        double ZFW;
        double GW;
        unsigned short pax;
        unsigned int cargo;
    } sbPlanned;
} paxPayloadData_t;

typedef struct {
    double pilot;
    double firstOfficer;
    double engineer;
    double cabinCrewFront;
    double business1Left;
    double business1Center;
    double business1Right;
    double business2Left;
    double business2Center;
    double business2Right;
    double economy1Left;
    double economy1Center;
    double economy1Right;
    double economy2Left;
    double economy2Center;
    double economy2Right;
    double cabinCrewRear;
    double forwardCargo;
    double rearCargo;
    double leftAux;
    double rightAux;
} paxPayloadDataSet_t;

/******************************** Functions ********************************/
//ZFW Entry, fill pax first (pax+bag), rest is cargo
void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, const double ZFWTarget, const UserData_t* const userData,
                const UserOptions_t* const userOptions);
//SimBrief Entry, SB pax count and total cargo
void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned short numPax, unsigned int cargo, const UserData_t* const userData,
                const UserOptions_t* const userOptions);
// Updates pax stations with their respective weights
// Used internally and used for Station Entry (pax only, cargo is ste directly)
// STATION WEIGHTS ARE NOT NORMALISED TO POUNDS
void generatePayload(paxPayloadData_t* const targetPayload, const bool isImperial, const UserOptions_t* const userOptions);
// Normalise to Pounds
// For Station Entry: CALL AFTER `generatePayload`
void normalisePayload(paxPayloadData_t* const targetPayload, const bool isImperial);
void calculateCGs(const paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, double* const ZFWCG, double* const TOCG, const bool isImperial);
void load(const paxPayloadData_t* const targetPayload, const HANDLE simConnect, const bool isImperial);
void unload(const HANDLE simConnect, const bool isER);
// Based on ICAO/LH findings
const double PAX_WEIGHT(const bool isImperial, const UserOptions_t* const options);
// Based on ICAO/LH findings
const double BAG_WEIGHT(const bool isImperial, const UserOptions_t* const options);