#ifndef BTYPES_H
#define BTYPES_H

#ifdef QT_CORE_LIB
#include <Qt>
#include <QStringList>

typedef quint8      uint8_t;
typedef quint16     uint16_t;
typedef quint32     uint32_t;
typedef qint8       int8_t;
typedef qint16      int16_t;
typedef qint32      int32_t;
typedef float       port_float_t;
typedef quint8     port_bool_t;

#define pFALSE      ((port_bool_t) 0)
#define pTRUE       ((port_bool_t) ~0)

#define to8bit()    toUtf8()
#define from8bit(x)  QString::fromUtf8(x)


typedef enum
{
    Refused, Closed
} CommunicationError_t;
#define COMMUNICATION_ERROR_TABLE \
    trUtf8("Nelze navázat spojení") \
    << trUtf8("Spojení bylo přerušeno")

namespace BaO
{
typedef enum
{
    Usb, Ethernet
} CommunicationInterface_t;

#define COMM_INTERFACE_LIST "USB" << "ETHERNET"


#define INFRA_COMMANDS_LIST "Volume UP" \
    << "Volume DOWN" << "Mute"\
    << "Device 1 asym" << "Device 2" << "Device 1 sym"
#define SIDE_LIST_QSTRING trUtf8("Center") \
    << trUtf8("Subwoofer") \
    << trUtf8("Levý zadní") \
    << trUtf8("Levý přední") << trUtf8("Pravý zadní") \
    << trUtf8("Pravý přední") << trUtf8("nic")

#define SIDE_LIST_SHORT_QSTRING \
    trUtf8("Cen") << trUtf8("Sub") \
    << trUtf8("LZ") << trUtf8("LP") << trUtf8("PZ") \
    << trUtf8("PP") << trUtf8("nic")

#define BAND_LIST "60Hz" << "150Hz" \
    << "400Hz" << "1KHz" \
    << "2.4KHz" << "6KHz" << "15KHz"

#define TEMPERATURE_NAMES_LIST trUtf8("satelit")\
    << trUtf8("zesilovač") \
    << trUtf8("dalši zesilovač") \
    << trUtf8("zeslabovač") << trUtf8("konec")

#if (QT_VERSION < 0x050000)
    #define setSectionResizeMode setResizeMode
#endif
}

#define INI_FILE "bang.ini"

#else
#include "ch.h"
#include "hal.h"

typedef float       port_float_t;
typedef uint8_t     port_bool_t;

#endif

#ifdef QT_CORE_LIB
namespace BaO
{
#endif

typedef enum {ANALOG_UP_1, ANALOG_DW_1, MUTE, CIFFER_1, 
    CIFFER_2, CIFFER_3} remoteCommand_t;

#ifdef QT_CORE_LIB
}
#endif

#define CONFIGURATION_TCP_PORT          15152
#define UDP_PORT                        25001
#define SAMPLE_COUNT                    336
#define UDP_HEADER_LENGTH               16
#define PACKET_LENGTH                              \
    ((SAMPLE_COUNT * 4) + UDP_HEADER_LENGTH)

#define GET_MASTER                "GET_MASTER"
#define GET_SLAVECOUNT            "GET_SLAVECOUNT"
#define GET_SLAVE                 "GET_SLAVEN"
#define SEND_GPIO                 "SEND_GPIO"
/// asynchronní - posiláš podle sebe
#define SEND_TEMPERATURE          "SEND_TEMP"
    // SERIAL4bytyTEPLOTY2byty*počet
#define SEND_OUTPUTSTREAM         "SEND_OUTSTREAM"
#define SEND_INFRACOMMAND         "SEND_INFRACOMMAND"
    // SERIAL4byjtyCOMMAND2byty
#define SEND_SLAVE_EQUALISER      "SEND_EQUALISER"
// SERIAL4bytyGLOBAL1byteENALED1byteSIZEOF(Bequaliser)
#define SET_OUTPUTSTREAM          "SET_OUTPUTSTREAM"
#define SET_INPUTSTREAM           "SET_INPUTSTREAM"
#define SET_GPIO                  "SET_GPIO"
#define SET_TEMPERATURE           "SET_TEMPERATURE"
#define SET_SLAVE_EQUALISER       "SET_EQUALISER"
// SERIAL4bytyGLOBAL1byteENALED1byteSIZEOF(bequaliser)

//pouzije slave...

#define GIVE_INPUTSTREAM 	  "GIVE_INPUTSTREAM"
#define GIVE_STOPINPUTSTREAM "GIVE_STOPINPUTSTREAM"
#define GIVE_OUTPUTSTREAM	  "GIVE_OUTPUTSTREAM"
#define GIVE_STOPOUTPUTSTREAM "GIVE_STOPOUTPUTSTREAM"

#define SET_SLAVENAME            "SET_SLAVENAME"
    // SERIAL4bytyNAME16bytů
#define SET_SLAVEINPUT           "SET_SLAVEINPUT"
    // SERIAL4bytyUID2byty

#define SEND_SLAVEREFRESH        "SEND_SLAVEREFRESH"
    //SERIAL4bytyCISLOuTebeVPolu4byty

#define EQUALISER_COEF_COUNT            7
#define TEMPERATURE_COUNT               5

typedef enum
{
    NO_SIDE = 6, SIDE_CENTER = 0, SIDE_SUBWOOFER =1,
	LEFT_REAR = 2, LEFT_FRONT = 3, RIGHT_REAR = 4, 
    RIGHT_FRONT = 5 , SIDE_ENUM_LAST
} side_enum_t;

typedef char name_t[16];

/******************************************************
 * input stream
 ******************************************************/
struct BInputStream_t
{
	/// tímhle jenom vynásobit data z A/D
    port_float_t Gain;                  //readwrite
    /// má vzorkovat nebo ne
    port_bool_t  Active;                //readwrite
    /// fixní nastavíš jenom na začátku programu
    uint16_t UID;                       //readonly
    /// nadřazený balík vstupních streamů 
      //(např CD bude mít číslo 100)
    uint16_t DeviceUID;                 //readwrite
    /// uživatelský název vstupu (např CD left)
    name_t name;                     //readwrite in PC
    /// uživatelský název balíku vstupů (např CD)
    name_t DeviceName;
    /// side_enum_t
    uint8_t Side;
}  __attribute__ ((__packed__));

typedef struct BInputStream_t BInputStream_t;

/******************************************************
 * output stream
 ******************************************************/
struct BEqualiser_t
{
    uint8_t Coef[EQUALISER_COEF_COUNT]; //readwrite
    port_bool_t Active;                 //readwrite
}   __attribute__ ((__packed__));

typedef struct BEqualiser_t BEqualiser_t;

/// nastavení výstupu může pro každý slave jinak
/// slave - celý systém pro jednu místnost (např 5+1)
// output stream = reprák
struct BoutputStream_t
{
    BEqualiser_t Equaliser;            //readwrite

    port_bool_t Mute;                   //readwrite
    port_float_t Volume;                //readwrite
    /// připojený vstup z A/D do konkrétního repráku
    uint16_t InputStreamConnectionUID;  //readwrite
    /// nadřazená jednotka v místnosti 
      //(hlavní jednotka - subwoofer)
    uint32_t SlaveSerialNumber;         //readOnly
    /// tohle nastaví PC - nezajimá tě
    uint8_t Side;                       //readOnly
 /// fixní identifikátor reproduktoru (např 100-103)
    uint16_t UID;                       //readonly
} __attribute__ ((__packed__));

typedef struct BoutputStream_t BoutputStream_t;

/******************************************************
 * Slave
 ******************************************************/
struct BSlave_t
{
    uint32_t IP;                //readonly in PC
    uint32_t SerialNumber;      //readonly
    uint32_t FW_Version;        //readonly
    uint32_t HW_Version;        //readonly
     //readonly in PC
    int16_t  Temperatures[TEMPERATURE_COUNT];
    uint16_t GPIO_In;           //readonly in PC
    uint16_t GPIO_Out;          //readwrite
    //30
    name_t   name;                //readwrite in PC
    /// vstupní připojené zařízení
    uint16_t ConnectDeviceUID;

    BEqualiser_t Equaliser;            //readwrite
    //výběr jesli použít lokální nebo globální eq
    port_bool_t useGlobalEq;
    //globálně vypne nebo zapne eqalizéry
    port_bool_t EqualisersEnabled;

    /// seznam výstupních streamů
    BoutputStream_t ** Outputs; //readonly in PC
    uint16_t OutputCount;       //readonly in PC
} __attribute__ ((__packed__));

typedef struct BSlave_t BSlave_t;

#ifdef QT_CORE_LIB
struct BSlaves_t
{
    uint16_t Count;
    BSlave_t ** Slaves;
} __attribute__ ((__packed__));

typedef struct BSlaves_t BSlaves_t;
#endif

/******************************************************
 * Master
 ******************************************************/
struct BMaster_t
{
    uint32_t IP;            //readonly in PC
    uint32_t SerialNumber;  //readonly
    uint32_t FW_Version;    //readonly
    uint32_t HW_Version;    //readonly
}  __attribute__ ((__packed__));

typedef struct BMaster_t BMaster_t;

/******************************************************
 * Main
 ******************************************************/
typedef struct
{
    BMaster_t * Master;
#ifndef QT_CORE_LIB
    BSlave_t ** Slaves;
    // ještě si sem přihodit počet slave jednotek
    uint8_t SlaveCount;
#else
    BSlaves_t * Slaves;
#endif

    BInputStream_t ** InputStreams;
    uint16_t InputStreamCount;

    BoutputStream_t ** OutputStreams;
    uint16_t OutputStreamCount;

} BMain_t;


#define MEMBER_SIZE(type, member)                      \
    (sizeof(((type *)0)->member))

/******************************************************
 * Plnění UDP hlavičky
 ******************************************************/
/******************************************************
 * 0xFF FF FF F(F/E vstup/vystup) | 2byty INPUT UID |
 * 2byty OUTPUT UID | 4byty slave serial number |
 * 4byty timemarker
 ******************************************************/

#define _UDP_FILL(ptr,data,offset,size)                \
    {memcpy(ptr + offset,&data,size);}
#define _UDP_EXTRACT(ptr,retVal,offset,size)           \
    {memcpy(&retVal,ptr+offset,size);}
#define TIME_MARKER_SIZE 4

#define STREAM_TYPE_INPUT 1
#define STREAM_TYPE_OUTPUT 0

//pokud se bude posila input stream tak bych chtěl
//obě uid stejny
//nejde pouzit s konstantou jako argument ale musi 
//to byt promenna
#define UDP_FILL_PREAMBLE(ptr) \
    {memset(ptr,0xff,3); ptr[3] |= 0xFE;}
#define UDP_FILL_STREAM_TYPE(ptr,type0vystup1vstup) \
    {ptr[3] &= 0xFE ;ptr[3] |= \
    (type0vystup1vstup & 1);}
#define UDP_FILL_INPUT_UID(ptr,uid) \
    {_UDP_FILL(ptr, uid, 4, \
    MEMBER_SIZE(BInputStream_t,UID))}
#define UDP_FILL_OUTPUT_UID(ptr,uid) \
    {_UDP_FILL(ptr ,uid, 6, \
    MEMBER_SIZE(BoutputStream_t,UID))}
#define UDP_FILL_SERIAL_NUMBER(ptr,number) \
    {_UDP_FILL(ptr ,number ,8, \
    MEMBER_SIZE(BSlave_t,SerialNumber))}
#define UDP_FILL_TIME_MARKER(ptr,marker) \
    {_UDP_FILL(ptr, marker,12, TIME_MARKER_SIZE)}

#define UDP_EXTRACT_STREAM_TYPE(ptr,retVal8) \
    {retVal8 = ptr[3] & 0x1;}
#define UDP_EXTRACT_INPUT_UID(ptr,retVal16) \
    {_UDP_EXTRACT(ptr,retVal16,4, \
    MEMBER_SIZE(BInputStream_t,UID))}
#define UDP_EXTRACT_OUTPUT_UID(ptr,retVal16) \
    {_UDP_EXTRACT(ptr,retVal16,6, \
    MEMBER_SIZE(BoutputStream_t,UID))}
#define UDP_EXTRACT_SERIAL_NUMBER(ptr,retVal32) \
    {_UDP_EXTRACT(ptr,retVal32,8, \
    MEMBER_SIZE(BSlave_t,SerialNumber))}
#define UDP_EXTRACT_TIME_MARKER(ptr,retVal32) \
    {_UDP_EXTRACT(ptr,retVal32,12,TIME_MARKER_SIZE)}


#endif // BTYPES_H
