text:
// Copyright 2020 The Defold Foundation
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#include "comp_tilegrid.h"
#include "comp_private.h"

#include <new>
#include <dlib/array.h>
#include <dlib/log.h>
#include <dlib/hash.h>
#include <dlib/message.h>
#include <dlib/dstrings.h>
#include <dlib/math.h>
#include <dlib/profile.h>
#include <dlib/time.h>
#include <graphics/graphics.h>
#include <render/render.h>
#include <gameobject/gameobject.h>
#include <gameobject/gameobject_ddf.h>
#include <dmsdk/vectormath/cpp/vectormath_aos.h>

#include "tile_ddf.h"
#include "../gamesys.h"
#include "../gamesys_private.h"
#include "../proto/tile_ddf.h"
#include "../proto/physics_ddf.h"
#include "../resources/res_tilegrid.h"

namespace dmGameSystem
{
    const uint32_t TILEGRID_REGION_SIZE = 32;

    using namespace Vectormath::Aos;

    // A "region" spans all layers (in Z) for a bounding box [(x1,y1), (x2,y2)]
    // where the the box spans TILEGRID_REGION_SIZE tiles in each direction
    struct TileGridRegion
    {
        uint8_t m_Dirty:1;
        uint8_t m_Occupied:1;
        uint8_t :6;
    };

    struct TileGridLayer
    {
        uint8_t m_IsVisible:1;
        uint8_t :7;
    };

    struct TileGridComponent
    {
        struct Flags
        {
            uint16_t    m_FlipHorizontal : 1;
            uint16_t    m_FlipVertical : 1;
            uint16_t    : 14;
        };

        TileGridComponent()
        : m_Instance(0)
        , m_Material(0)
        , m_Cells(0)
        , m_TextureSet(0)
        , m_CellFlags(0)
        , m_Resource(0)
        {
        }

        Vectormath::Aos::Vector3    m_Translation;
        Vectormath::Aos::Quat       m_Rotation;
        Vectormath::Aos::Matrix4    m_World;
        dmGameObject::HInstance     m_Instance;
        uint16_t*                   m_Cells;
        Flags*                      m_CellFlags;
        dmArray<TileGridRegion>     m_Regions;
        dmArray<TileGridLayer>      m_Layers;
        uint32_t                    m_MixedHash;
        CompRenderConstants         m_RenderConstants;
        dmRender::HMaterial         m_Material;
        TextureSetResource*         m_TextureSet;
        TileGridResource*           m_Resource;
        uint16_t                    m_RegionsX; // number of regions in the x dimension
        uint16_t                    m_RegionsY; // number of regions in the y dimension
        uint16_t                    m_Occupied; // Number of occupied regions (regions with visible tiles)
        uint8_t                     m_Enabled : 1;
        uint8_t                     m_AddedToUpdate : 1;
        uint8_t                     : 6;
    };

    struct TileGridVertex
    {
        float x, y, z, u, v;
    };

    struct TileGridWorld
    {
        TileGridWorld()
        {
            memset(this, 0, sizeof(TileGridWorld));
        }

        dmRender::HRenderContext        m_RenderContext;
        dmArray<TileGridComponent*>     m_Components;
        dmArray<dmRender::RenderObject> m_RenderObjects;
        dmGraphics::HVertexDeclaration  m_VertexDeclaration;

        dmGraphics::HVertexBuffer       m_VertexBuffer;
        TileGridVertex*                 m_VertexBufferData;
        TileGridVertex*                 m_VertexBufferDataEnd;
        TileGridVertex*                 m_VertexBufferWritePtr;

        uint32_t                        m_MaxTilemapCount;
        uint32_t                        m_MaxTileCount;
    };

    static void TileGridWorldAllocate(TileGridWorld* world)
    {
        world->m_RenderObjects.SetCapacity(4);

        dmGraphics::HContext graphics_context = dmRender::GetGraphicsContext(world->m_RenderContext);
        // TODO: Everything below here should be move to the "universe" when available
        // and hence shared among all the worlds
        dmGraphics::VertexElement ve[] =
        {
                {"position", 0, 3, dmGraphics::TYPE_FLOAT, false},
                {"texcoord0", 1, 2, dmGraphics::TYPE_FLOAT, false},
        };
        world->m_VertexDeclaration = dmGraphics::NewVertexDeclaration(graphics_context, ve, sizeof(ve) / sizeof(ve[0]));
        world->m_VertexBuffer = dmGraphics::NewVertexBuffer(dmRender::GetGraphicsContext(world->m_RenderContext), 0, 0x0, dmGraphics::BUFFER_USAGE_STREAM_DRAW);
        uint32_t vcount = 6 * world->m_MaxTileCount;
        world->m_VertexBufferData = (TileGridVertex*) malloc(sizeof(TileGridVertex) * vcount);
        world->m_VertexBufferDataEnd = world->m_VertexBufferData + vcount;
    }

    dmGameObject::CreateResult CompTileGridNewWorld(const dmGameObject::ComponentNewWorldParams& params)
    {
        TileGridWorld* world = new TileGridWorld;
        TilemapContext* context = (TilemapContext*)params.m_Context;
        world->m_RenderContext = context->m_RenderContext;

        world->m_MaxTilemapCount = context->m_MaxTilemapCount;
        world->m_MaxTileCount = context->m_MaxTileCount;

        world->m_Components.SetCapacity(world->m_MaxTilemapCount);

        world->m_VertexDeclaration = 0;

        *params.m_World = world;
        return dmGameObject::CREATE_RESULT_OK;
    }

    dmGameObject::CreateResult CompTileGridDeleteWorld(const dmGameObject::ComponentDeleteWorldParams& params)
    {
        TileGridWorld* world = (TileGridWorld*) params.m_World;
        if (world->m_VertexDeclaration)
        {
            dmGraphics::DeleteVertexDeclaration(world->m_VertexDeclaration);
            dmGraphics::DeleteVertexBuffer(world->m_VertexBuffer);
            free(world->m_VertexBufferData);
        }
        delete world;
        return dmGameObject::CREATE_RESULT_OK;
    }

    static inline dmRender::HMaterial GetMaterial(const TileGridComponent* component) {
        return component->m_Material ? component->m_Material : component->m_Resource->m_Material;
    }

    static inline TextureSetResource* GetTextureSet(const TileGridComponent* component) {
        return component->m_TextureSet ? component->m_TextureSet : component->m_Resource->m_TextureSet;
    }

    uint32_t CalculateCellIndex(uint32_t layer, int32_t cell_x, int32_t cell_y, uint32_t column_count, uint32_t row_count)
    {
        return layer * row_count * column_count + (cell_x + cell_y * column_count);
    }

    void GetTileGridBounds(const TileGridComponent* component, int32_t* x, int32_t* y, int32_t* w, int32_t* h)
    {
        TileGridResource* resource = component->m_Resource;
        *x = resource->m_MinCellX;
        *y = resource->m_MinCellY;
        *w = resource->m_ColumnCount;
        *h = resource->m_RowCount;
    }

    void GetTileGridCellCoord(const TileGridComponent* component, int32_t x, int32_t y, int32_t& cell_x, int32_t& cell_y)
    {
        cell_x = x - component->m_Resource->m_MinCellX;
        cell_y = y - component->m_Resource->m_MinCellY;
    }

    uint16_t GetTileGridTile(const TileGridComponent* component, uint32_t layer, int32_t cell_x, int32_t cell_y)
    {
        TileGridResource* resource = component->m_Resource;
        uint32_t cell_index = CalculateCellIndex(layer, cell_x, cell_y, resource->m_ColumnCount, resource->m_RowCount);
        uint16_t cell = (component->m_Cells[cell_index] + 1);
        return cell;
    }

    void SetLayerVisible(TileGridComponent* component, uint32_t layer_index, bool visible)
    {
        TileGridLayer* layer = &component->m_Layers[layer_index];
        layer->m_IsVisible = visible;
    }

    static void SetRegionDirty(TileGridComponent* component, int32_t cell_x, int32_t cell_y)
    {
        uint32_t region_x = cell_x / TILEGRID_REGION_SIZE;
        uint32_t region_y = cell_y / TILEGRID_REGION_SIZE;
        uint32_t region_index = region_y * component->m_RegionsX + region_x;
        TileGridRegion* region = &component->m_Regions[region_index];
        region->m_Dirty = 1;
    }

    void SetTileGridTile(TileGridComponent* component, uint32_t layer, int32_t cell_x, int32_t cell_y, uint32_t tile, bool flip_h, bool flip_v)
    {
        TileGridResource* resource = component->m_Resource;
        uint32_t cell_index = CalculateCellIndex(layer, cell_x, cell_y, resource->m_ColumnCount, resource->m_RowCount);
        component->m_Cells[cell_index] = tile;

        TileGridComponent::Flags* flags = &component->m_CellFlags[cell_index];
        flags->m_FlipHorizontal = flip_h;
        flags->m_FlipVertical = flip_v;

        SetRegionDirty(component, cell_x, cell_y);
    }

    uint16_t GetTileCount(const TileGridComponent* component) {
        return GetTextureSet(component)->m_TextureSet->m_TileCount;
    }

    static void ReHash(TileGridComponent* component)
    {
        HashState32 state;
        TileGridResource* resource = component->m_Resource;

        // NOTE: Use the same order as comp_sprite, since they both use the "tile" tag by default
        dmHashInit32(&state, false);
        dmHashUpdateBuffer32(&state, GetMaterial(component), sizeof(dmRender::HMaterial));
        dmHashUpdateBuffer32(&state, GetTextureSet(component), sizeof(TextureSetResource));
        dmHashUpdateBuffer32(&state, &resource->m_TileGrid->m_BlendMode, sizeof(resource->m_TileGrid->m_BlendMode));
        ReHashRenderConstants(&component->m_RenderConstants, &state);

        component->m_MixedHash = dmHashFinal32(&state);
    }

    static void CreateRegions(TileGridComponent* component, TileGridResource* resource)
    {
        // Round up to closest multiple
        component->m_RegionsX = ((resource->m_ColumnCount + TILEGRID_REGION_SIZE - 1) / TILEGRID_REGION_SIZE);
        component->m_RegionsY = ((resource->m_RowCount + TILEGRID_REGION_SIZE - 1) / TILEGRID_REGION_SIZE);
        uint32_t region_count = component->m_RegionsX * component->m_RegionsY;

        component->m_Regions.SetCapacity(region_count);
        component->m_Regions.SetSize(region_count);
        memset(&component->m_Regions[0], 0xFF, region_count * sizeof(TileGridRegion)); // mark them all dirty
    }

    static uint32_t UpdateRegion(TileGridComponent* component, uint32_t region_x, uint32_t region_y)
    {
        uint32_t index = region_y * component->m_RegionsX + region_x;
        TileGridRegion* region = &component->m_Regions[index];
        if (!region->m_Dirty) {
            return region->m_Occupied;
        }
        region->m_Dirty = 0;

        TileGridResource* resource = component->m_Resource;
        dmGameSystemDDF::TileGrid* tile_grid_ddf = resource->m_TileGrid;

        uint32_t n_layers = tile_grid_ddf->m_Layers.m_Count;

        uint32_t column_count = resource->m_ColumnCount;
        uint32_t row_count = resource->m_RowCount;
        int32_t min_x = resource->m_MinCellX + region_x * TILEGRID_REGION_SIZE;
        int32_t min_y = resource->m_MinCellY + region_y * TILEGRID_REGION_SIZE;
        int32_t max_x = dmMath::Min(min_x + (int32_t)TILEGRID_REGION_SIZE, resource->m_MinCellX + (int32_t)column_count);
        int32_t max_y = dmMath::Min(min_y + (int32_t)TILEGRID_REGION_SIZE, resource->m_MinCellY + (int32_t)row_count);

        region->m_Occupied = 0;

        uint32_t visible_tiles = 0;
        for (uint32_t j = 0; j < n_layers; ++j)
        {
            TileGridLayer* layer = &component->m_Layers[j];
            if (!layer->m_IsVisible)
                continue;

            for (int32_t y = min_y; y < max_y; ++y)
            {
                for (int32_t x = min_x; x < max_x; ++x)
                {
                    uint32_t cell = CalculateCellIndex(j, x - resource->m_MinCellX, y - resource->m_MinCellY, column_count, row_count);
                    uint16_t tile = component->m_Cells[cell];
                    if (tile != 0xffff)
                    {
                        ++visible_tiles;
                        region->m_Occupied = 1;
                        return region->m_Occupied;
                    }
                }
            }
        }

        return region->m_Occupied;
    }

    static uint32_t UpdateRegions(TileGridComponent* component)
    {
        uint32_t occupied = 0;
        for (uint32_t region_y = 0; region_y < component->m_RegionsY; ++region_y) {
            for (uint32_t region_x = 0; region_x < component->m_RegionsX; ++region_x) {
                occupied += UpdateRegion(component, region_x, region_y);
            }
        }
        return occupied;
    }

    static uint32_t CreateTileGrid(TileGridComponent* component)
    {
        TileGridResource* resource = component->m_Resource;
        dmGameSystemDDF::TileGrid* tile_grid_ddf = resource->m_TileGrid;
        uint32_t n_layers = tile_grid_ddf->m_Layers.m_Count;
        uint32_t cell_count = resource->m_ColumnCount * resource->m_RowCount * n_layers;
        if (component->m_Cells != 0x0)
        {
            delete [] component->m_Cells;
        }
        component->m_Cells = new uint16_t[cell_count];
        memset(component->m_Cells, 0xff, cell_count * sizeof(uint16_t));
        if (component->m_CellFlags != 0x0)
        {
            delete [] component->m_CellFlags;
        }
        component->m_CellFlags = new TileGridComponent::Flags[cell_count];
        memset(component->m_CellFlags, 0, cell_count * sizeof(TileGridComponent::Flags));
        int32_t min_x = resource->m_MinCellX;
        int32_t min_y = resource->m_MinCellY;
        uint32_t column_count = resource->m_ColumnCount;
        uint32_t row_count = resource->m_RowCount;

        component->m_Layers.SetCapacity(n_layers);
        component->m_Layers.SetSize(n_layers);

        for (uint32_t i = 0; i < n_layers; ++i)
        {
            dmGameSystemDDF::TileLayer* layer_ddf = &tile_grid_ddf->m_Layers[i];

            component->m_Layers[i].m_IsVisible = layer_ddf->m_IsVisible;

            uint32_t n_cells = layer_ddf->m_Cell.m_Count;
            for (uint32_t j = 0; j < n_cells; ++j)
            {
                dmGameSystemDDF::TileCell* cell = &layer_ddf->m_Cell[j];
                uint32_t cell_index = CalculateCellIndex(i, cell->m_X - min_x, cell->m_Y - min_y, column_count, row_count);
                component->m_Cells[cell_index] = (uint16_t)cell->m_Tile;

                TileGridComponent::Flags* flags = &component->m_CellFlags[cell_index];
                flags->m_FlipHorizontal = cell->m_HFlip;
                flags->m_FlipVertical = cell->m_VFlip;
            }
        }

        CreateRegions(component, resource);
        component->m_Occupied = UpdateRegions(component);
        return n_layers;
    }

    dmGameObject::CreateResult CompTileGridCreate(const dmGameObject::ComponentCreateParams& params)
    {
        TileGridWorld* world = (TileGridWorld*) params.m_World;
        if (world->m_Components.Full())
        {
            dmLogError("Tilemap could not be created since the tilemap buffer is full (%d). You can change this with the config setting tilemap.max_count", world->m_Components.Capacity());
            return dmGameObject::CREATE_RESULT_UNKNOWN_ERROR;
        }

        if (world->m_VertexDeclaration == 0) {
            TileGridWorldAllocate(world);
        }

        TileGridResource* resource = (TileGridResource*) params.m_Resource;
        TileGridComponent* component = new TileGridComponent();
        component->m_Instance = params.m_Instance;
        component->m_Resource = resource;
        component->m_Translation = Vector3(params.m_Position);
        component->m_Rotation = params.m_Rotation;
        component->m_Enabled = 1;

        uint32_t layer_count = CreateTileGrid(component);
        if (layer_count == 0)
        {
            return dmGameObject::CREATE_RESULT_UNKNOWN_ERROR;
        }

        world->m_Components.Push(component);
        *params.m_UserData = (uintptr_t) component;

        ReHash(component);
        return dmGameObject::CREATE_RESULT_OK;
    }

    dmGameObject::CreateResult CompTileGridDestroy(const dmGameObject::ComponentDestroyParams& params)
    {
        TileGridComponent* tile_grid = (TileGridComponent*) *params.m_UserData;
        TileGridWorld* world = (TileGridWorld*) params.m_World;
        for (uint32_t i = 0; i < world->m_Components.Size(); ++i)
        {
            if (world->m_Components[i] == tile_grid)
            {
                if (tile_grid->m_Material) {
                    dmResource::Release(dmGameObject::GetFactory(params.m_Instance), tile_grid->m_Material);
                }
                if (tile_grid->m_TextureSet) {
                    dmResource::Release(dmGameObject::GetFactory(params.m_Instance), tile_grid->m_TextureSet);
                }

                delete [] tile_grid->m_Cells;
                delete [] tile_grid->m_CellFlags;
                world->m_Components.EraseSwap(i);
                delete tile_grid;
                return dmGameObject::CREATE_RESULT_OK;
            }
        }
        assert(false);
        return dmGameObject::CREATE_RESULT_UNKNOWN_ERROR;
    }

    static inline void CalculateCellBounds(int32_t cell_x, int32_t cell_y, int32_t cell_width, int32_t cell_height, float out_v[4])
    {
        out_v[0] = cell_x * cell_width;
        out_v[1] = cell_y * cell_height;
        out_v[2] = (cell_x + 1) * cell_width;
        out_v[3] = (cell_y + 1) * cell_height;
    }

    dmGameObject::CreateResult CompTileGridAddToUpdate(const dmGameObject::ComponentAddToUpdateParams& params)
    {
        TileGridComponent* component = (TileGridComponent*) *params.m_UserData;
        component->m_AddedToUpdate = 1;
        return dmGameObject::CREATE_RESULT_OK;
    }

    dmGameObject::UpdateResult CompTileGridUpdate(const dmGameObject::ComponentsUpdateParams& params, dmGameObject::ComponentsUpdateResult& update_result)
    {
        TileGridWorld* world = (TileGridWorld*)params.m_World;
        dmArray<TileGridComponent*>& components = world->m_Components;
        uint32_t n = components.Size();
        for (uint32_t i = 0; i < n; ++i)
        {
            TileGridComponent* component = components[i];
            if (!component->m_Enabled || !component->m_AddedToUpdate) {
                continue;
            }

            component->m_Occupied = UpdateRegions(component);
            if (!component->m_Occupied) {
                continue;
            }

            Matrix4 local(component->m_Rotation, component->m_Translation);
            const Matrix4& go_world = dmGameObject::GetWorldMatrix(component->m_Instance);
            if (dmGameObject::ScaleAlongZ(component->m_Instance))
            {
                component->m_World = go_world * local;
            }
            else
            {
                component->m_World = dmTransform::MulNoScaleZ(go_world, local);
            }
        }
        return dmGameObject::UPDATE_RESULT_OK;
    }

    static inline uint64_t EncodeRegionInfo(uint32_t tile_grid, uint32_t layer, uint32_t region_x, uint32_t region_y)
    {
        return (uint64_t)( (tile_grid & 0xFFFF) | ((layer & 0xFFFF) << 16) | ((uint64_t)region_x << 32) | ((uint64_t)region_y << 48) );
    }

    static inline void DecodeGridAndLayer(uint64_t ptr, uint32_t& tile_grid, uint32_t& layer, uint32_t& region_x, uint32_t& region_y)
    {
        tile_grid = ptr & 0xFFFF;
        layer = (ptr >> 16) & 0xFFFF;
        region_x = (ptr >> 32) & 0xFFFF;
        region_y = (ptr >> 48) & 0xFFFF;
    }

    TileGridVertex* CreateVertexData(TileGridWorld* world, TileGridVertex* where, TextureSetResource* texture_set, dmRender::RenderListEntry* buf, uint32_t* begin, uint32_t* end)
    {
        DM_PROFILE(TileGrid, "CreateVertexData");
        static int tex_coord_order[] = {
            0,1,2,2,3,0,
            3,2,1,1,0,3,    //h
            1,0,3,3,2,1,    //v
            2,3,0,0,1,2     //hv
        };

        dmGameSystemDDF::TextureSet* texture_set_ddf = texture_set->m_TextureSet;
        const float* tex_coords = (const float*) texture_set_ddf->m_TexCoords.m_Data;

        uint32_t tile_width = texture_set_ddf->m_TileWidth;
        uint32_t tile_height = texture_set_ddf->m_TileHeight;

        for (uint32_t* i = begin; i != end; ++i)
        {
            uint32_t index, layer, region_x, region_y;
            DecodeGridAndLayer(buf[*i].m_UserData, index, layer, region_x, region_y);

            const TileGridComponent* component = world->m_Components[index];
            const TileGridResource* resource = component->m_Resource;
            dmGameSystemDDF::TileGrid* tile_grid_ddf = resource->m_TileGrid;
            dmGameSystemDDF::TileLayer* layer_ddf = &tile_grid_ddf->m_Layers[layer];

            const Matrix4& w = component->m_World;
            const float z = layer_ddf->m_Z;

            uint32_t column_count = resource->m_ColumnCount;
            uint32_t row_count = resource->m_RowCount;

            int32_t min_x = resource->m_MinCellX + region_x * TILEGRID_REGION_SIZE;
            int32_t min_y = resource->m_MinCellY + region_y * TILEGRID_REGION_SIZE;
            int32_t max_x = dmMath::Min(min_x + (int32_t)TILEGRID_REGION_SIZE, resource->m_MinCellX + (int32_t)column_count);
            int32_t max_y = dmMath::Min(min_y + (int32_t)TILEGRID_REGION_SIZE, resource->m_MinCellY + (int32_t)row_count);

            for (int32_t y = min_y; y < max_y; ++y)
            {
                for (int32_t x = min_x; x < max_x; ++x)
                {
                    uint32_t cell = CalculateCellIndex(layer, x - resource->m_MinCellX, y - resource->m_MinCellY, column_count, row_count);
                    uint16_t tile = component->m_Cells[cell];
                    if (tile == 0xffff)
                    {
                        continue;
                    }

                    if( where >= world->m_VertexBufferDataEnd )
                    {
                        dmLogError("Out of tiles to render (%zu). You can change this with the game.project setting tilemap.max_tile_count", (size_t)((world->m_VertexBufferDataEnd - world->m_VertexBufferData) / 6));
                        return world->m_VertexBufferDataEnd;
                    }

                    float p[4];
                    CalculateCellBounds(x, y, 1, 1, p);
                    const float* puv = &tex_coords[tile * 8];
                    uint32_t flip_flag = 0;

                    TileGridComponent::Flags flags = component->m_CellFlags[cell];
                    if (flags.m_FlipHorizontal)
                    {
                        flip_flag = 1;
                    }
                    if (flags.m_FlipVertical)
                    {
                        flip_flag |= 2;
                    }
                    const int* tex_lookup = &tex_coord_order[flip_flag * 6];

                    #define SET_VERTEX(_I, _X, _Y, _Z, _U, _V) \
                        { \
                            const Vector4 v = w * Point3(_X * tile_width, _Y * tile_height, _Z); \
                            where[_I].x = v.getX(); \
                            where[_I].y = v.getY(); \
                            where[_I].z = v.getZ(); \
                            where[_I].u = _U; \
                            where[_I].v = _V; \
                        }

                    SET_VERTEX(0, p[0], p[1], z, puv[tex_lookup[0] * 2], puv[tex_lookup[0] * 2 + 1]);
                    SET_VERTEX(1, p[0], p[3], z, puv[tex_lookup[1] * 2], puv[tex_lookup[1] * 2 + 1]);
                    SET_VERTEX(2, p[2], p[3], z, puv[tex_lookup[2] * 2], puv[tex_lookup[2] * 2 + 1]);
                    SET_VERTEX(3, p[2], p[3], z, puv[tex_lookup[3] * 2], puv[tex_lookup[3] * 2 + 1]);
                    SET_VERTEX(4, p[2], p[1], z, puv[tex_lookup[4] * 2], puv[tex_lookup[4] * 2 + 1]);
                    SET_VERTEX(5, p[0], p[1], z, puv[tex_lookup[5] * 2], puv[tex_lookup[5] * 2 + 1]);

                    where += 6;

                    #undef SET_VERTEX
                }
            }
        }
        return where;
    }

    static void RenderBatch(TileGridWorld* world, dmRender::HRenderContext render_context, dmRender::RenderListEntry *buf, uint32_t* begin, uint32_t* end)
    {
        DM_PROFILE(TileGrid, "RenderBatch");

        uint32_t index, layer, region_x, region_y;
        DecodeGridAndLayer(buf[*begin].m_UserData, index, layer, region_x, region_y);
        const TileGridComponent* first = world->m_Components[index];
        assert(first->m_Enabled);

        TileGridResource* resource = first->m_Resource;
        TextureSetResource* texture_set = GetTextureSet(first);

        dmRender::RenderObject& ro = *world->m_RenderObjects.End();
        world->m_RenderObjects.SetSize(world->m_RenderObjects.Size()+1);

        // Fill in vertex buffer
        TileGridVertex* vb_begin = world->m_VertexBufferWritePtr;
        world->m_VertexBufferWritePtr = CreateVertexData(world, vb_begin, texture_set, buf, begin, end);

        ro.Init();
        ro.m_VertexDeclaration = world->m_VertexDeclaration;
        ro.m_VertexBuffer = world->m_VertexBuffer;
        ro.m_PrimitiveType = dmGraphics::PRIMITIVE_TRIANGLES;
        ro.m_VertexStart = vb_begin - world->m_VertexBufferData;
        ro.m_VertexCount = (world->m_VertexBufferWritePtr - vb_begin);
        ro.m_Material = GetMaterial(first);
        ro.m_Textures[0] = texture_set->m_Texture;

        const dmRender::Constant* constants = first->m_RenderConstants.m_RenderConstants;
        uint32_t size = first->m_RenderConstants.m_ConstantCount;
        for (uint32_t i = 0; i < size; ++i)
        {
            const dmRender::Constant& c = constants[i];
            dmRender::EnableRenderObjectConstant(&ro, c.m_NameHash, c.m_Value);
        }

        dmGameSystemDDF::TileGrid::BlendMode blend_mode = resource->m_TileGrid->m_BlendMode;
        switch (blend_mode)
        {
            case dmGameSystemDDF::TileGrid::BLEND_MODE_ALPHA:
                ro.m_SourceBlendFactor = dmGraphics::BLEND_FACTOR_ONE;
                ro.m_DestinationBlendFactor = dmGraphics::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
            break;

            case dmGameSystemDDF::TileGrid::BLEND_MODE_ADD:
            case dmGameSystemDDF::TileGrid::BLEND_MODE_ADD_ALPHA:
                ro.m_SourceBlendFactor = dmGraphics::BLEND_FACTOR_ONE;
                ro.m_DestinationBlendFactor = dmGraphics::BLEND_FACTOR_ONE;
            break;

            case dmGameSystemDDF::TileGrid::BLEND_MODE_MULT:
                ro.m_SourceBlendFactor = dmGraphics::BLEND_FACTOR_DST_COLOR;
                ro.m_DestinationBlendFactor = dmGraphics::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
            break;

            default:
                dmLogError("Unknown blend mode: %d\n", blend_mode);
                assert(0);
            break;
        }

        ro.m_SetBlendFactors = 1;

        dmRender::AddToRender(render_context, &ro);
    }

    static void RenderListDispatch(dmRender::RenderListDispatchParams const &params)
    {
        TileGridWorld* world = (TileGridWorld*) params.m_UserData;

        switch (params.m_Operation)
        {
        case dmRender::RENDER_LIST_OPERATION_BEGIN:
            world->m_VertexBufferWritePtr = world->m_VertexBufferData;
            world->m_RenderObjects.SetSize(0);
            break;

        case dmRender::RENDER_LIST_OPERATION_END:
            dmGraphics::SetVertexBufferData(world->m_VertexBuffer, 0, 0, dmGraphics::BUFFER_USAGE_STATIC_DRAW);
            dmGraphics::SetVertexBufferData(world->m_VertexBuffer, sizeof(TileGridVertex) * (world->m_VertexBufferWritePtr - world->m_VertexBufferData),
                                            world->m_VertexBufferData, dmGraphics::BUFFER_USAGE_STATIC_DRAW);
            DM_COUNTER("TileGridVertexBuffer", (world->m_VertexBufferWritePtr - world->m_VertexBufferData) * sizeof(TileGridVertex));
            DM_COUNTER("TileGridTileCount", (world->m_VertexBufferWritePtr - world->m_VertexBufferData));
            break;

        case dmRender::RENDER_LIST_OPERATION_BATCH:
            assert(params.m_Operation == dmRender::RENDER_LIST_OPERATION_BATCH);
            RenderBatch(world, params.m_Context, params.m_Buf, params.m_Begin, params.m_End);
        }
    }

    // Estimates the number of render entries needed
    static uint32_t CalcNumVisibleRegions(TileGridComponent** components, uint32_t num_components)
    {
        uint32_t num_render_entries = 0;
        for (uint32_t i = 0; i < num_components; ++i)
        {
            const TileGridComponent* component = components[i];
            if (!component->m_Enabled || !component->m_AddedToUpdate || !component->m_Occupied) {
                continue;
            }
            uint32_t n_layers = component->m_Layers.Size();
            for (uint32_t l = 0; l < n_layers; ++l)
            {
                const TileGridLayer* layer = &component->m_Layers[l];
                if (!layer->m_IsVisible)
                    continue;

                num_render_entries += component->m_RegionsY * component->m_RegionsX;
            }
        }
        return num_render_entries;
    }

    dmGameObject::UpdateResult CompTileGridRender(const dmGameObject::ComponentsRenderParams& params)
    {
        TilemapContext* context = (TilemapContext*)params.m_Context;
        TileGridWorld* world = (TileGridWorld*)params.m_World;

        dmArray<TileGridComponent*>& components = world->m_Components;
        uint32_t n = components.Size();
        if( n == 0 )
        {
            return dmGameObject::UPDATE_RESULT_OK;
        }

        uint32_t num_render_entries = CalcNumVisibleRegions(&components[0], n);

        // We need to calculate this before actually pushing render object references to the renderer
        // This however means we need to make this array potentially oversized, but this allocation should only occur
        // occasionally after a new tilegrid is added for rendering
        if (num_render_entries > world->m_RenderObjects.Capacity()) {
            world->m_RenderObjects.SetCapacity(num_render_entries);
        }

        dmRender::HRenderContext render_context = context->m_RenderContext;
        dmRender::RenderListEntry* render_list = dmRender::RenderListAlloc(render_context, num_render_entries);
        dmRender::HRenderListDispatch dispatch = dmRender::RenderListMakeDispatch(render_context, &RenderListDispatch, world);
        dmRender::RenderListEntry* write_ptr = render_list;

        for (uint32_t i = 0; i < n; ++i)
        {
            TileGridComponent* component = components[i];
            if (!component->m_Enabled || !component->m_AddedToUpdate || !component->m_Occupied) {
                continue;
            }

            if (dmGameSystem::AreRenderConstantsUpdated(&component->m_RenderConstants))
            {
                ReHash(component);
            }

            TileGridResource* resource = component->m_Resource;
            dmGameSystemDDF::TextureSet* texture_set_ddf = GetTextureSet(component)->m_TextureSet;
            dmGameSystemDDF::TileGrid* tile_grid_ddf = resource->m_TileGrid;

            uint32_t tile_width = texture_set_ddf->m_TileWidth;
            uint32_t tile_height = texture_set_ddf->m_TileHeight;

            uint32_t n_layers = tile_grid_ddf->m_Layers.m_Count;
            for (uint32_t l = 0; l < n_layers; ++l)
            {
                TileGridLayer* layer = &component->m_Layers[l];
                if (!layer->m_IsVisible)
                    continue;

                dmGameSystemDDF::TileLayer* layer_ddf = &tile_grid_ddf->m_Layers[l];
                for (uint32_t y = 0, region_index = 0; y < component->m_RegionsY; ++y) {
                    for (uint32_t x = 0; x < component->m_RegionsX; ++x, ++region_index) {

                        TileGridRegion* region = &component->m_Regions[region_index];
                        if (!region->m_Occupied) {
                            continue;
                        }

                        Vector4 trans = component->m_World * Point3(x * tile_width, y * tile_height, layer_ddf->m_Z);

                        write_ptr->m_WorldPosition = Point3(trans.getXYZ());
                        write_ptr->m_UserData = EncodeRegionInfo(i, l, x, y);
                        write_ptr->m_TagMask = dmRender::GetMaterialTagMask(GetMaterial(component));
                        write_ptr->m_BatchKey = component->m_MixedHash;
                        write_ptr->m_Dispatch = dispatch;
                        write_ptr->m_MinorOrder = 0;
                        write_ptr->m_MajorOrder = dmRender::RENDER_ORDER_WORLD;
                        ++write_ptr;
                    }
                }
            }
        }

        if (render_list != write_ptr)
            dmRender::RenderListSubmit(render_context, render_list, write_ptr);
        return dmGameObject::UPDATE_RESULT_OK;
    }

    uint32_t GetLayerIndex(const TileGridComponent* component, dmhash_t layer_id)
    {
        dmGameSystemDDF::TileGrid* tile_grid_ddf = component->m_Resource->m_TileGrid;
        uint32_t layer_count = tile_grid_ddf->m_Layers.m_Count;
        for (uint32_t i = 0; i < layer_count; ++i)
        {
            if (layer_id == tile_grid_ddf->m_Layers[i].m_IdHash)
            {
                return i;
            }
        }
        return ~0u;
    }

    static void CompTileGridSetConstantCallback(void* user_data, dmhash_t name_hash, uint32_t* element_index, const dmGameObject::PropertyVar& var);

    dmGameObject::UpdateResult CompTileGridOnMessage(const dmGameObject::ComponentOnMessageParams& params)
    {
        TileGridComponent* component = (TileGridComponent*) *params.m_UserData;

        // NOTE: this message is deprecated and undocumented
        if (params.m_Message->m_Id == dmGameSystemDDF::SetTile::m_DDFDescriptor->m_NameHash)
        {
            dmGameSystemDDF::SetTile* st = (dmGameSystemDDF::SetTile*) params.m_Message->m_Data;
            uint32_t layer_index = GetLayerIndex(component, st->m_LayerId);
            if (layer_index == ~0u)
            {
                dmLogError("Could not find layer %s when handling message %s.", dmHashReverseSafe64(st->m_LayerId), dmGameSystemDDF::SetTile::m_DDFDescriptor->m_Name);
                return dmGameObject::UPDATE_RESULT_UNKNOWN_ERROR;
            }
            dmGameObject::HInstance instance = component->m_Instance;
            dmTransform::Transform inv_world(dmTransform::Inv(dmGameObject::GetWorldTransform(instance)));
            Point3 cell = st->m_Position;
            if (dmGameObject::ScaleAlongZ(instance))
            {
                cell = dmTransform::Apply(inv_world, cell);
            }
            else
            {
                cell = dmTransform::ApplyNoScaleZ(inv_world, cell);
            }

            TileGridResource* resource = component->m_Resource;
            dmGameSystemDDF::TextureSet* texture_set_ddf = GetTextureSet(component)->m_TextureSet;
            cell = mulPerElem(cell, Point3(1.0f / texture_set_ddf->m_TileWidth, 1.0f / texture_set_ddf->m_TileHeight, 0.0f));
            int32_t cell_x = (int32_t)floor(cell.getX()) + st->m_Dx - resource->m_MinCellX;
            int32_t cell_y = (int32_t)floor(cell.getY()) + st->m_Dy - resource->m_MinCellY;
            if (cell_x < 0 || cell_x >= (int32_t)resource->m_ColumnCount || cell_y < 0 || cell_y >= (int32_t)resource->m_RowCount)
            {
                dmLogError("Could not set the tile since the supplied tile was out of range.");
                return dmGameObject::UPDATE_RESULT_UNKNOWN_ERROR;
            }

            /*
             * NOTE AND BEWARE: Empty tile is encoded as 0xffffffff
             * That's why tile-index is subtracted by 1
             * See B2GRIDSHAPE_EMPTY_CELL in b2GridShape.h
             */
            uint32_t tile = st->m_Tile - 1;

            SetTileGridTile(component, layer_index, cell_x, cell_y, tile, false, false);

            // Broadcast to any collision object components
            // TODO Filter broadcast to only collision objects
            dmPhysicsDDF::SetGridShapeHull set_hull_ddf;
            set_hull_ddf.m_Shape = layer_index;
            set_hull_ddf.m_Column = cell_x;
            set_hull_ddf.m_Row = cell_y;
            set_hull_ddf.m_Hull = tile;
            dmhash_t message_id = dmPhysicsDDF::SetGridShapeHull::m_DDFDescriptor->m_NameHash;
            uintptr_t descriptor = (uintptr_t)dmPhysicsDDF::SetGridShapeHull::m_DDFDescriptor;
            uint32_t data_size = sizeof(dmPhysicsDDF::SetGridShapeHull);
            dmMessage::URL receiver = params.m_Message->m_Receiver;
            receiver.m_Fragment = 0;
            dmMessage::Result result = dmMessage::Post(&params.m_Message->m_Receiver, &receiver, message_id, 0, descriptor, &set_hull_ddf, data_size, 0);
            if (result != dmMessage::RESULT_OK)
            {
                LogMessageError(params.m_Message, "Could not send %s to components, result: %d.", dmPhysicsDDF::SetGridShapeHull::m_DDFDescriptor->m_Name, result);
                return dmGameObject::UPDATE_RESULT_UNKNOWN_ERROR;
            }
        }
        else if (params.m_Message->m_Id == dmGameSystemDDF::SetConstantTileMap::m_DDFDescriptor->m_NameHash)
        {
            dmGameSystemDDF::SetConstantTileMap* ddf = (dmGameSystemDDF::SetConstantTileMap*)params.m_Message->m_Data;
            SetRenderConstant(&component->m_RenderConstants, GetMaterial(component), ddf->m_NameHash, 0, ddf->m_Value);
            ReHash(component);
        }
        else if (params.m_Message->m_Id == dmGameSystemDDF::ResetConstantTileMap::m_DDFDescriptor->m_NameHash)
        {
            dmGameSystemDDF::ResetConstantTileMap* ddf = (dmGameSystemDDF::ResetConstantTileMap*)params.m_Message->m_Data;
            ClearRenderConstant(&component->m_RenderConstants, ddf->m_NameHash);
        }
        else if (params.m_Message->m_Id == dmGameObjectDDF::Enable::m_DDFDescriptor->m_NameHash)
        {
            component->m_Enabled = 1;
        }
        else if (params.m_Message->m_Id == dmGameObjectDDF::Disable::m_DDFDescriptor->m_NameHash)
        {
            component->m_Enabled = 0;
        }

        return dmGameObject::UPDATE_RESULT_OK;
    }

    void CompTileGridOnReload(const dmGameObject::ComponentOnReloadParams& params)
    {
        TileGridComponent* component = (TileGridComponent*)*params.m_UserData;
        if (!CreateTileGrid(component))
        {
            dmLogError("Could not recreate tile grid component, not reloaded.");
        }
    }

    static bool CompTileGridGetConstantCallback(void* user_data, dmhash_t name_hash, dmRender::Constant** out_constant)
    {
        TileGridComponent* component = (TileGridComponent*)user_data;
        return GetRenderConstant(&component->m_RenderConstants, name_hash, out_constant);
    }

    static void CompTileGridSetConstantCallback(void* user_data, dmhash_t name_hash, uint32_t* element_index, const dmGameObject::PropertyVar& var)
    {
        TileGridComponent* component = (TileGridComponent*)user_data;
        SetRenderConstant(&component->m_RenderConstants, GetMaterial(component), name_hash, element_index, var);
        ReHash(component);
    }

    dmGameObject::PropertyResult CompTileGridGetProperty(const dmGameObject::ComponentGetPropertyParams& params, dmGameObject::PropertyDesc& out_value)
    {
        TileGridComponent* component = (TileGridComponent*)*params.m_UserData;
        if (params.m_PropertyId == PROP_MATERIAL)
        {
            return GetResourceProperty(dmGameObject::GetFactory(params.m_Instance), GetMaterial(component), out_value);
        }
        if (params.m_PropertyId == PROP_TILE_SOURCE)
        {
            return GetResourceProperty(dmGameObject::GetFactory(params.m_Instance), GetTextureSet(component), out_value);
        }
        return GetMaterialConstant(GetMaterial(component), params.m_PropertyId, out_value, true, CompTileGridGetConstantCallback, component);
    }

    dmGameObject::PropertyResult CompTileGridSetProperty(const dmGameObject::ComponentSetPropertyParams& params)
    {
        TileGridComponent* component = (TileGridComponent*)*params.m_UserData;
        if (params.m_PropertyId == PROP_MATERIAL)
        {
            return SetResourceProperty(dmGameObject::GetFactory(params.m_Instance), params.m_Value, MATERIAL_EXT_HASH, (void**)&component->m_Material);
        }
        if (params.m_PropertyId == PROP_TILE_SOURCE)
        {
            return SetResourceProperty(dmGameObject::GetFactory(params.m_Instance), params.m_Value, TEXTURE_SET_EXT_HASH, (void**)&component->m_TextureSet);
        }
        return SetMaterialConstant(GetMaterial(component), params.m_PropertyId, params.m_Value, CompTileGridSetConstantCallback, component);
    }
}

max_stars_repo_name: Krzlfx/defold

max_issues_repo_name: Krzlfx/defold

max_forks_repo_name: Krzlfx/defold


text:
/*
 * Part of HTTPP.
 *
 * Distributed under the 2-clause BSD licence (See LICENCE.TXT file at the
 * project root).
 *
 * Copyright (c) 2013 Thomas Sanchez.  All rights reserved.
 *
 */

#ifndef _HTTPP_HTPP_CONNECTION_HPP_
#define _HTTPP_HTPP_CONNECTION_HPP_

#include <atomic>
#include <functional>
#include <mutex>
#include <string>

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

#include <commonpp/core/LoggingInterface.hpp>
#include <commonpp/thread/ThreadPool.hpp>

#include "Request.hpp"
#include "Response.hpp"
#include "helper/ReadWholeRequest.hpp"

namespace HTTPP
{

class HttpServer;

namespace HTTP
{

namespace connection_detail
{
FWD_DECLARE_LOGGER(conn_logger_, commonpp::core::Logger);
}

class Connection
{
    friend class ::HTTPP::HttpServer;

    using DefaultSocket = boost::asio::ip::tcp::socket;
    using SSLSocket = boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>;
    using ThreadPool = commonpp::thread::ThreadPool;

public:
    static constexpr size_t BUF_SIZE = BUFSIZ;
    using Callback = std::function<void()>;

    Connection(HTTPP::HttpServer& handler,
               boost::asio::io_service& service,
               boost::asio::ssl::context* ctx = nullptr);

private:
    ~Connection();

public:
    Connection(const Connection&) = delete;
    Connection& operator=(const Connection&) = delete;

    static void releaseFromHandler(Connection* connection);
    std::string source() const;

    Response& response() noexcept
    {
        return response_;
    }

    const Response& response() const noexcept
    {
        return response_;
    }

    const Request& request() const noexcept
    {
        return request_;
    }

    template <typename Callable>
    void read(size_t size, char* buffer, Callable&& callable)
    {
        if (!own())
        {
            throw std::logic_error("Invalid connection state");
        }

        size_t offset = 0;

        if (not body_buffer_.empty())
        {
            if (size <= body_buffer_.size())
            {
                std::copy(begin(body_buffer_), begin(body_buffer_) + size, buffer);
                body_buffer_.erase(begin(body_buffer_),
                                   begin(body_buffer_) + size);
                disown();
                callable(boost::system::error_code());
                return;
            }
            else
            {
                std::copy(begin(body_buffer_), end(body_buffer_), buffer);
                size -= body_buffer_.size();
                offset += body_buffer_.size();
                body_buffer_.clear();
            }
        }

        async_read(boost::asio::buffer(buffer + offset, size),
                   [buffer, size, callable,
                    this](const boost::system::error_code& ec, size_t) {
                       disown();

                       if (ec)
                       {
                           LOG(connection_detail::conn_logger_, error)
                               << "Error detected while reading the body";
                           callable(ec);
                           return;
                       }

                       callable(boost::system::error_code());
                   });
    }

    template <typename Callable, size_t BUFFER_SIZE = BUF_SIZE>
    void read(size_t body_size, Callable&& callable)
    {
        if (!own())
        {
            throw std::logic_error("Invalid connection state");
        }

        if (not body_buffer_.empty())
        {
            if (body_size <= body_buffer_.size())
            {
                callable(boost::system::error_code(), body_buffer_.data(),
                         body_size);
                body_buffer_.erase(begin(body_buffer_),
                                   begin(body_buffer_) + body_size);
                body_size = 0;
            }
            else
            {
                callable(boost::system::error_code(), body_buffer_.data(),
                         body_buffer_.size());
                body_size -= body_buffer_.size();
                body_buffer_.clear();
            }
        }

        if (!body_size)
        {
            disown();
            callable(boost::system::error_code(), nullptr, 0);
            return;
        }

        auto buf_size = std::min(BUFFER_SIZE, body_size);

        body_buffer_.resize(buf_size);

        async_read_some(boost::asio::buffer(body_buffer_),
                        [body_size, callable,
                         this](const boost::system::error_code& ec, size_t size) {
                            disown();

                            if (ec)
                            {
                                LOG(connection_detail::conn_logger_, error)
                                    << "Error detected while reading the body";
                                callable(ec, nullptr, 0);
                                return;
                            }

                            body_buffer_.resize(size);
                            read(body_size, callable);
                        });
    }

    void sendResponse();
    void sendContinue(Callback&& cb);

private:
    static void release(Connection* connection);

    void start();

    void disown();
    bool own() noexcept;
    bool shouldBeDeleted() const noexcept;
    void markToBeDeleted();

    void cancel() noexcept;
    void close() noexcept;

    void read_request();
    void recycle();

    template <typename... Args>
    void async_read_some(Args&&... a)
    {
        std::unique_lock<std::mutex> lock(mutex_);
        if (ssl_socket_)
        {
            ssl_socket_->async_read_some(std::forward<Args>(a)...);
        }
        else
        {
            socket_.async_read_some(std::forward<Args>(a)...);
        }
    }

    template <typename... Args>
    void async_read(Args&&... a)
    {
        std::unique_lock<std::mutex> lock(mutex_);
        if (ssl_socket_)
        {
            boost::asio::async_read(*ssl_socket_, std::forward<Args>(a)...);
        }
        else
        {
            boost::asio::async_read(socket_, std::forward<Args>(a)...);
        }
    }

    void sendResponse(Callback&& cb);

private:
    HTTPP::HttpServer& handler_;
    // On construction, the HttpServer is the owner
    std::atomic_bool is_owned_ = {false};
    bool should_be_deleted_ = {false};
    std::vector<char> request_buffer_;
    std::vector<char> body_buffer_;
    size_t size_ = 0;

    std::mutex mutex_;

    DefaultSocket socket_;
    bool need_handshake_ = true;
    std::unique_ptr<SSLSocket> ssl_socket_;

    Request request_;
    Response response_;
};

} // namespace HTTP
} // namespace HTTPP
#endif // ! _HTTPP_HTPP_CONNECTION_HPP_

ext: hpp

max_stars_repo_name: dohse/httpp

max_issues_repo_name: dohse/httpp

max_forks_repo_name: dohse/httpp


text:
// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/tuple.hpp>

#include <type_traits>
namespace hana = boost::hana;

//
// This test checks that we can NOT construct a tuple holding array members,
// per the standard.
//

int main() {
    static_assert(!std::is_constructible<hana::tuple<int[3]>, int[3]>{}, "");
    static_assert(!std::is_constructible<hana::tuple<int[3], float[4]>, int[3], float[4]>{}, "");
}

ext: cpp


text:
/* Copyright Institute of Sound and Vibration Research - All rights reserved */

#ifndef VISR_RBBL_BIQUAD_COEFFICIENT_HPP_INCLUDED
#define VISR_RBBL_BIQUAD_COEFFICIENT_HPP_INCLUDED

#include <boost/property_tree/ptree_fwd.hpp>

#include "export_symbols.hpp"

#include <algorithm>
#include <array>
#include <initializer_list>
#include <iosfwd>
#include <stdexcept>
#include <vector>

namespace visr
{
namespace rbbl
{

/**
 * A FIFO-type message queue template class for storing and passing message data.
 * @tparam CoeffType Type of the contained elements.
 * @note This class does provide the same level of thread safety as, e.g., the STL.
 * I.e., calling code from different thread must ensure that concurrent accesses
 * to the same instances are appropriately secured against race conditions.
 */
template< typename CoeffType >
class VISR_RBBL_LIBRARY_SYMBOL BiquadCoefficient
{
public:
  /**
   * The number of coefficients to describe one biquad section.
   */
  static const std::size_t cNumberOfCoeffs = 5;

  /**
   * Create an default object corresponding to a flat EQ.
   */
  BiquadCoefficient()
  {
    mCoeffs.fill( static_cast<CoeffType>(0.0) );
    mCoeffs[0] = static_cast<CoeffType>(1.0);
  }

  BiquadCoefficient( BiquadCoefficient<CoeffType> const & rhs ) = default;

  /**
   * Create a BiquadCoefficient objects from JSON and XML representations.
   */
  //@{
  static BiquadCoefficient fromJson( boost::property_tree::ptree const & tree );

  static BiquadCoefficient fromJson( std::basic_istream<char> & stream );

  static BiquadCoefficient fromXml( boost::property_tree::ptree const & tree );

  static BiquadCoefficient fromXml( std::basic_istream<char> & stream );
  //@}

  BiquadCoefficient( CoeffType b0, CoeffType b1, CoeffType b2,
                   CoeffType a1, CoeffType a2 )
  {
    mCoeffs[0] = b0;
    mCoeffs[1] = b1;
    mCoeffs[2] = b2;
    mCoeffs[3] = a1;
    mCoeffs[4] = a2;
  }

  BiquadCoefficient( std::initializer_list< CoeffType > const & coeffs )
  {
    if( coeffs.size() != mCoeffs.size() )
    {
      throw std::invalid_argument( "BiquadCoefficient: Initialisation vector has wrong number of elements." );
    }
    std::copy( coeffs.begin(), coeffs.end(), mCoeffs.begin() );
  }

  // BiquadCoefficient( const BiquadCoefficient & rhs ) = default;

  BiquadCoefficient & operator=( BiquadCoefficient const & rhs )
  {
    mCoeffs = rhs.mCoeffs;
    return *this;
  }

  CoeffType const * data() const { return &mCoeffs[0]; }

  CoeffType * data() { return &mCoeffs[0]; }

  CoeffType const & operator[]( std::size_t idx ) const
  {
    return mCoeffs[ idx ];
  }

  CoeffType & operator[]( std::size_t idx )
  {
    return mCoeffs[ idx ];
  }

  CoeffType const & at( std::size_t idx ) const
  {
    return mCoeffs.at( idx );
  }

  CoeffType & at( std::size_t idx )
  {
    return mCoeffs.at( idx );
  }

  CoeffType const & b0() const { return mCoeffs[0]; }

  CoeffType const & b1() const { return mCoeffs[1]; }

  CoeffType const & b2() const { return mCoeffs[2]; }

  CoeffType const & a1() const { return mCoeffs[3]; }

  CoeffType const & a2() const { return mCoeffs[4]; }

  CoeffType& b0() { return mCoeffs[0]; }

  CoeffType& b1() { return mCoeffs[1]; }

  CoeffType& b2() { return mCoeffs[2]; }

  CoeffType& a1() { return mCoeffs[3]; }

  CoeffType& a2() { return mCoeffs[4]; }

  void loadJson( boost::property_tree::ptree const & tree );

  void loadJson( std::basic_istream<char> & );

  void loadXml( boost::property_tree::ptree const & tree );

  void loadXml( std::basic_istream<char> & );

  /**
   *
   */
  //@{
  void writeJson( boost::property_tree::ptree & tree ) const;

  void writeJson( std::basic_ostream<char> & stream ) const;

  void writeJson( std::string & str ) const;

  void writeXml( boost::property_tree::ptree & tree ) const;

  void writeXml ( std::basic_ostream<char> & stream ) const;

  void writeXml ( std::string & str ) const;

  //@}
private:
  /**
   * The internal data representation.
   */
  std::array<CoeffType,5> mCoeffs;
};

template< class CoeffType >
class VISR_RBBL_LIBRARY_SYMBOL BiquadCoefficientList
{
public:
  using Container = typename std::vector< BiquadCoefficient< CoeffType > >;
  using iterator = typename Container::iterator;
  using const_iterator = typename Container::const_iterator;

  /**
   * Default constructor, creates an empty list of biquad parameters.
   */
  BiquadCoefficientList() = default;

  explicit BiquadCoefficientList( const std::size_t initialSize )
    : mBiquads( initialSize )
  {
  }

  /**
   * Default copy constructor (required for use within aSTL data structure as, for instance, in BiquadCoefficientList.
   */
  BiquadCoefficientList( BiquadCoefficientList const & rhs ) = default;

  BiquadCoefficientList( std::initializer_list<BiquadCoefficient<CoeffType> > const & initList )
    : mBiquads( initList )
  {
  }

  static BiquadCoefficientList fromJson( boost::property_tree::ptree const & tree );

  static BiquadCoefficientList fromJson( std::basic_istream<char> & stream );

  static BiquadCoefficientList fromJson( std::string const & str );

  static BiquadCoefficientList fromXml( boost::property_tree::ptree const & tree );

  static BiquadCoefficientList fromXml( std::basic_istream<char> & stream );

  static BiquadCoefficientList fromXml( std::string const & str );

  /**
   * Assign the content from another BiquadCoefficientList with consistent size.
   * @throw std::invalid_argument If \p rhs has a different size.
   */
  BiquadCoefficientList & operator=(BiquadCoefficientList const & rhs)
  {
    if( rhs.size() != size() )
    {
      std::invalid_argument( "BiquadCoefficientList: Size of argument to be assigned does not match." );
    }
    mBiquads = rhs.mBiquads;
    return *this;
  }

  std::size_t size() const { return mBiquads.size(); }

  void resize( std::size_t newSize )
  {
    mBiquads.resize( newSize, BiquadCoefficient<CoeffType>() );
  }

  BiquadCoefficient<CoeffType> const & operator[]( std::size_t idx ) const { return mBiquads[idx]; }
  BiquadCoefficient<CoeffType> & operator[]( std::size_t idx ) { return mBiquads[idx]; }

  BiquadCoefficient<CoeffType> const & at( std::size_t idx ) const { return mBiquads.at( idx ); }
  BiquadCoefficient<CoeffType> & at( std::size_t idx ) { return mBiquads.at( idx ); }

  iterator begin() { return mBiquads.begin(); }
  iterator end() { return mBiquads.end(); }

  const_iterator begin() const { return mBiquads.begin(); }
  const_iterator end() const { return mBiquads.end(); }

  /**
   * DE-serialization from text formats.
   */
  //@{
  /**
   * Initialise from a JSON representation, provided as a Boost property tree ptree object.
   */
  void loadJson( boost::property_tree::ptree const & tree );

  /**
  * Initialise from a JSON representation, provided as an input stream containing JSON text data.
  */
  void loadJson( std::basic_istream<char> & stream );

  /**
  * Initialise from a JSON representation, provided as a string containing JSON text data.
  */
  void loadJson( std::string const & str );

  /**
   * Initialise from an XML representation, provided as a Boost property tree ptree object.
   */
  void loadXml( boost::property_tree::ptree const & tree );

  /**
   * Initialise from an XML representation, provided as an input stream containing JSON text data.
   */
  void loadXml( std::basic_istream<char> & stream );

  /**
   * Initialise from an XML representation, provided as a string containing JSON text data.
   */
  void loadXml( std::string const & str );
  //@}

  /**
   * Serialisation into textual formats.
   */
  //@{
  /**
   * Write to a boost property tree object (ptree) that can be serialised to an XML document (or part thereof)
   * @note the ptree representations for XML and JSON differ slightly, so different implementations are needed.
   */
  void writeJson( boost::property_tree::ptree & tree ) const;

  /**
   * Write an XML representation to an output stream.
   */
  void writeJson( std::basic_ostream<char> & stream ) const;

  /**
   * Write an XML representation into a string.
   */
  void writeJson( std::string & str ) const;

  /**
  * Write to a boost property tree object (ptree) that can be serialised to an XML document (or part thereof)
  * @note the ptree representations for XML and JSON differ slightly, so different implementations are needed.
  */
  void writeXml( boost::property_tree::ptree & tree ) const;

  /**
  * Write an XML representation to an output stream.
  */
  void writeXml( std::basic_ostream<char> & stream ) const;

  /**
  * Write an XML representation into a string.
  */
  void writeXml( std::string & str ) const;
  //@}

private:
  Container mBiquads;
};

template<typename CoeffType >
class VISR_RBBL_LIBRARY_SYMBOL BiquadCoefficientMatrix
{
public:
  explicit BiquadCoefficientMatrix( std::size_t numberOfFilters, std::size_t numberOfBiquads );

  ~BiquadCoefficientMatrix();

  static BiquadCoefficientMatrix fromJson( boost::property_tree::ptree const & tree );

  static BiquadCoefficientMatrix fromJson( std::basic_istream<char> & stream );

  static BiquadCoefficientMatrix fromJson( std::string const & str );

  static BiquadCoefficientMatrix fromXml( boost::property_tree::ptree const & tree );

  static BiquadCoefficientMatrix fromXml( std::basic_istream<char> & stream );

  static BiquadCoefficientMatrix fromXml( std::string const & str );

  std::size_t numberOfFilters() const { return mRows.size(); }
  std::size_t numberOfSections() const { return mRows.empty() ? 0 : mRows[0].size(); }

  void resize( std::size_t numberOfFilters, std::size_t numberOfBiquads );
  BiquadCoefficientList<CoeffType> const & operator[]( std::size_t rowIdx ) const { return mRows[rowIdx]; }
  BiquadCoefficientList<CoeffType> & operator[]( std::size_t rowIdx ) { return mRows[rowIdx]; }

  BiquadCoefficient<CoeffType> const & operator()( std::size_t rowIdx, std::size_t colIdx ) const { return mRows[rowIdx][colIdx]; }
  BiquadCoefficient<CoeffType> & operator()( std::size_t rowIdx, std::size_t colIdx ) { return mRows[rowIdx][colIdx]; }

  /**
   * Set the biquad sections for a complete filter specification (a row in the matrix)
   * If \p newFilter has fewer sections than the matrix, the rest is filled with default values.
   * @throw std::out_of_range If \p filterIdx exceeds the number of biquad sections.
   * @throw std::invalid_argument If \p nnewFilters has more elements than the column number of the matrix.
   */
  void setFilter( std::size_t filterIdx, BiquadCoefficientList<CoeffType> const & newFilter );

  /**
  * Deserialisation from text formats.
  */
  //@{
  /**
  * Initialise from a JSON representation, provided as a Boost property tree ptree object.
  */
  void loadJson( boost::property_tree::ptree const & tree );

  /**
  * Initialise from a JSON representation, provided as an input stream containing JSON text data.
  */
  void loadJson( std::basic_istream<char> & stream );

  /**
  * Initialise from a JSON representation, provided as a string containing JSON text data.
  */
  void loadJson( std::string const & str );

  /**
  * Initialise from an XML representation, provided as a Boost property tree ptree object.
  */
  void loadXml( boost::property_tree::ptree const & tree );

  /**
  * Initialise from an XML representation, provided as an input stream containing JSON text data.
  */
  void loadXml( std::basic_istream<char> & stream );

  /**
  * Initialise from an XML representation, provided as a string containing JSON text data.
  */
  void loadXml( std::string const & str );
  //@}

  /**
  * Serialisation into textual formats.
  */
  //@{
  /**
  * Write to a boost property tree object (ptree) that can be serialised to an XML document (or part thereof)
  * @note the ptree representations for XML and JSON differ slightly, so different implementations are needed.
  */
  void writeJson( boost::property_tree::ptree & tree ) const;

  /**
  * Write an XML representation to an output stream.
  */
  void writeJson( std::basic_ostream<char> & stream ) const;

  /**
  * Write an XML representation into a string.
  */
  void writeJson( std::string & str ) const;

  /**
  * Write to a boost property tree object (ptree) that can be serialised to an XML document (or part thereof)
  * @note the ptree representations for XML and JSON differ slightly, so different implementations are needed.
  */
  void writeXml( boost::property_tree::ptree & tree ) const;

  /**
  * Write an XML representation to an output stream.
  */
  void writeXml( std::basic_ostream<char> & stream ) const;

  /**
  * Write an XML representation into a string.
  */
  void writeXml( std::string & str ) const;
  //@}

private:
  using ContainerType = std::vector< BiquadCoefficientList<CoeffType> >;

  ContainerType mRows;
};

} // namespace rbbl
} // namespace visr

#endif // VISR_RBBL_BIQUAD_COEFFICIENT_HPP_INCLUDED


text:
#include "neuralnet.h"
#include "version.h"
#include "sync.h"
#include "util.h"

#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp>

#include <functional>
#include <future>
#include <fstream>
#include <map>
#include <array>
#include <cstdio>
#include <string>

// Old VB based NeuralNet.
extern std::string qtGetNeuralHash(std::string data);
extern std::string qtGetNeuralContract(std::string data);
extern double qtExecuteGenericFunction(std::string function,std::string data);
extern std::string qtExecuteDotNetStringFunction(std::string function,std::string data);
extern void qtSyncWithDPORNodes(std::string data);
int64_t IsNeural();

// While transitioning to dotnet the NeuralNet implementation has been split
// into 3 implementations; Win32 with Qt, Win32 without Qt and the rest.
// After the transition both Win32 implementations can be removed.
namespace NN
{
    // Win32 with Qt enabled.
    bool IsEnabled()
    {
        return GetArgument("disableneuralnetwork", "false") == "false";
    }

    std::string GetNeuralVersion()
    {
        int neural_id = static_cast<int>(IsNeural());
        return std::to_string(CLIENT_VERSION_MINOR) + "." + std::to_string(neural_id);
    }

    std::string GetNeuralHash()
    {
        return qtGetNeuralHash("");
    }

    std::string GetNeuralContract()
    {
        return qtGetNeuralContract("");
    }

    bool SynchronizeDPOR(const std::string& data)
    {
        qtSyncWithDPORNodes(data);
        return true;
    }

    std::string ExecuteDotNetStringFunction(std::string function, std::string data)
    {
        return qtExecuteDotNetStringFunction(function, data);
    }

    int64_t IsNeuralNet()
    {
       return IsNeural();
    }
}

max_stars_repo_name: smoe/Gridcoin-Research

max_issues_repo_name: smoe/Gridcoin-Research

max_forks_repo_name: smoe/Gridcoin-Research


text:
#pragma once

#include <boost/variant/static_visitor.hpp>

#include <iostream>
#include <string>

class print_visitor : public boost::static_visitor<>
{
protected:
  static constexpr unsigned int INDENT_SPACES = 2;

public:
  print_visitor()
    : _indent( 0 )
  {
  }

  print_visitor( unsigned int indent )
    : _indent( indent )
    , _indent_string( indent, ' ' )
  {
  }

protected:

  std::ostream& indent() const
  {
    return std::cout << _indent_string << "* ";
  }

  const unsigned int _indent;
  const std::string _indent_string;
};

max_stars_repo_name: mrwonko/perseus

max_issues_repo_name: mrwonko/perseus

max_forks_repo_name: mrwonko/perseus