#pragma once

#include <dxgi1_2.h>
#include <d3d11.h>
#include <vector>
#include <unordered_map>
#include <map>

#include "VentuzCommonEnums.h"

typedef unsigned long long uint64;
typedef signed long long int64;
typedef unsigned int uint;
typedef unsigned short uint16;
typedef signed short int16;
typedef unsigned char uint8;
typedef signed char int8;

#define UE4ComName L"Ventuz_6_UE4_Com_V1"
#define MAX_UE4_STREAMS 16
#define MAX_UE4_TEXTURESHARES 16

#define UE4ServerMonitorName (UE4ComName L"_ServerMonitorMutex")

template<> struct std::hash<GUID>
{
    std::uint64_t operator()( GUID const& s ) const noexcept
    {
        unsigned int* g = (unsigned int*)&s;
        return ( uint64_t( g[ 0 ] ) | ( uint64_t( g[ 1 ] ) << 32 ) ) ^ ( uint64_t( g[ 2 ] ) | ( uint64_t( g[ 3 ] ) << 32 ) );
    }
};

namespace Ventuz
{

// needs to be in sync with UE4Interface.cs Ventuz.Kernel.E2E.FieldType and VentuzDataGetterNode.h::VentuzType
enum class E2EFieldType : uint
{
    Float = 0,
    Integer = 1,
    Boolean = 2,
    String = 3,
    FloatArray = 4,
    IntegerArray = 5,
    BooleanArray = 6,
    StringArray = 7,
};

namespace UE4
{

#define vFOURCC(a, b, c, d)  ( ((a&255)<<0)|((b&255)<<8)|((c&255)<<16)|((d&255)<<24) )

enum class AncID : uint
{
    Null                = 0,
    CameraMatrix        = vFOURCC('C', 'A', 'M', 'R'), // 4x4 matrix, camera space to world space
    ProjectionMatrix    = vFOURCC('P', 'R', 'O', 'J'), // 4x4 matrix, world space to screen space
    InputData           = vFOURCC('I', 'T', 'A', 'C'), // input blob
    Event               = vFOURCC('E', 'V', 'N', 'T'), // event trigger
    IntValue            = vFOURCC('V', 'I', 'N', 'T'), // int value
    FloatValue          = vFOURCC('V', 'F', 'L', 'T'), // float value
    StringValue         = vFOURCC('V', 'S', 'T', 'R'), // string value
    BoolValue           = vFOURCC('V', 'B', 'O', 'L'), // bool value
    TransformValue      = vFOURCC('M', 'T', 'X', 'V'), // transform matrix value
    SetResolution       = vFOURCC('R', 'E', 'S', 'N'), // change image resolution
    TextureShareNames   = vFOURCC('T', 'S', 'H', 'R'), // current texture share name list
    RequestNodeFields   = vFOURCC('F', 'L', 'D', '?'), // request the field list of the specified node
    NodeFieldDescriptor = vFOURCC('F', 'L', 'D', 'S'), // describe fields of a node
    FrameCount          = vFOURCC('F', 'R', 'M', 'E'), // current frame id
    IntArray            = vFOURCC('A', 'I', 'N', 'T'), // int Array
    FloatArray          = vFOURCC('A', 'F', 'L', 'T'), // float Array
    StringArray         = vFOURCC('A', 'S', 'T', 'R'), // string Array
    BoolArray           = vFOURCC('A', 'B', 'O', 'L'), // bool Array
    RenderQuality       = vFOURCC('Q', 'L', 'T', 'Y'), // render quality
    ClusterClock        = vFOURCC('C', 'L', 'C', 'K'), // cluster clock
    Viewports           = vFOURCC('V', 'I', 'E', 'W'), // viewport data for the textures
};

enum class RenderQuality : uint
{
    Default = 0,
    Low,
    Medium,
    High,
    Epic,
    Cinematic
};

enum class ConnectionError
{
    OK,
    ServerNotFound,
    NoConnection,
    ServerFull,
    InvalidStream,
    SharedResourceError,
    AncOverflow,
    InvalidState,
};

enum class ConnectionState
{
    Disconnected,
    ConnectionRequest,
    Connecting,
    Connected,
	Disconnecting,
};

enum VioConsts
{
    MaxAncBlobs = 1024,
    MaxAncBytes = 0x8000,
};

struct FromVentuzEvent
{
    uint paramHash;
    uint argument;
};

struct FromVentuzInt
{
    uint paramHash;
    int value;
};

struct FromVentuzFloat
{
    uint paramHash;
    float value;
};

struct FromVentuzBool
{
    uint paramHash;
    bool value;
};

struct FromVentuzMatrix
{
    uint paramHash;
    GUID nodeID;
    float value[ 16 ];
};

struct FromVentuzClock
{
    uint64 clusterClock;
    float time;
    int divident;
    int divisor;
};

struct ComChannel
{
    ConnectionState state = ConnectionState::Disconnected;
    GUID internalComID;

    int pluginVersion;

    GUID ventuzLaunchID{};
    bool isLiveLink = false;
    bool isPlaying = false;
};

struct ComShared
{
    uint magic;
    int version;
    int sizeofShared = sizeof(ComShared);

    ComChannel channels[MAX_UE4_STREAMS];
};

struct MatrixHelper
{
    float m00, m10, m20, m30;
    float m01, m11, m21, m31;
    float m02, m12, m22, m32;
    float m03, m13, m23, m33;
};

struct ViewportData
{
    int x1, y1, x2, y2; // color buffer viewport
    int gViewWidth, gViewHeight; // gbuffer data viewport dimensions
};

struct SharedDatabase
{
    bool matricesUpdated = false;
    float cameraMatrix[ 16 ];
    float projectionMatrix[ 16 ];

    ViewportData viewportData{};

    std::vector<std::string> inputBundles;

    std::vector<FromVentuzEvent> events;
    std::unordered_map<uint, int> intValues;
    std::unordered_map<uint, float> floatValues;
    std::unordered_map<uint, std::string> stringValues;
    std::unordered_map<uint, bool> boolValues;

    std::unordered_map<uint, std::vector<int>> intArrays;
    std::unordered_map<uint, std::vector<float>> floatArrays;
    std::unordered_map<uint, std::wstring> stringArrays; // the string contains a list of asciiz strings to be unpacked on the client side
    std::unordered_map<uint, std::vector<unsigned char>> boolArrays;

    std::unordered_map<uint, unsigned int> colorValues;
    std::map<uint, float[ 16 ]> transformValues;
    std::unordered_map<GUID, MatrixHelper> transformValuesByGUID;

    std::vector<GUID> updatedTransformValues;

    bool HandleMessage(unsigned char* message, int length, uint& hash);

    void Reset();
};

}
}
