// Petsc initialization
#pragma once

#include <hollow/petsc/config.h>
#include <geode/utility/debug.h>
#include <vector>
#include <string>
// Work around configuration issue in old PETSc versions
#define PETSC_RESTRICT PETSC_CXX_RESTRICT
#include <petsc.h>
namespace hollow {

using std::string;
using std::vector;

// Initialize petsc, and automatically finalize on application exit
HOLLOW_EXPORT void petsc_initialize(const string& help, const vector<string>& args);

// Check if petsc is initialized
HOLLOW_EXPORT bool petsc_initialized();

// Ensure that petsc is initialized.  Can be called multiple times, but no help or args support.
HOLLOW_EXPORT void petsc_reinitialize();

// Explicitly finalize petsc.  This happens automatically at program exit if petsc_initialize
// is called, but sometime it is useful to do it sooner.
HOLLOW_EXPORT void petsc_finalize();

// Reset petsc options to the given list.  Any petsc objects already allocated will not change.
HOLLOW_EXPORT void petsc_set_options(const vector<string>& args);

// Add new petsc options without removing old options.  Like argv[0], the first option is ignored.
HOLLOW_EXPORT void petsc_add_options(const vector<string>& args);

}


// The various memcached commands that we support.

#include "commands.h"
#include "connections.h"
#include "locking.h"
#include "protocol.h"
#include "server.h"
#include "threads.h"
#include "utils.h"

#include <assert.h>
#include <gsl/gsl_randist.h>
#include <stdio.h>
#include <stdlib.h>

static const char* default_key = "skeleton";

/* // process a memcached get(s) command. (we don't support CAS). This function */
/* // performs the request parsing and setup of backend RPC's. */
/* void process_get_command(conn *c, token_t *tokens, size_t ntokens, */
/*                                 bool return_cas) { */
/* 	char *key; */
/* 	size_t nkey; */
/* 	int i = 0; */
/* 	item *it; */
/* 	token_t *key_token = &tokens[KEY_TOKEN]; */
/* 	char *suffix; */
/* 	worker_thread_t *t = c->thread; */
/* 	memcached_t *mc; */
/*  */
/* 	assert(c != NULL); */
/*  */
/* 	key  = key_token->value; */
/* 	nkey = key_token->length; */
/*  */
/* 	if (config.use_dist) { */
/* 		long size = config.dist_arg1 + gsl_ran_gaussian(config.r, config.dist_arg2); */
/* 		if (config.verbose > 1) { */
/* 			fprintf(stderr, "allocated blob: %ld\n", size); */
/* 		} */
/* 		c->mem_blob = malloc(sizeof(char) * size); */
/* 	} */
/*  */
/* 	if(nkey > KEY_MAX_LENGTH) { */
/* 		error_response(c, "CLIENT_ERROR bad command line format"); */
/* 		return; */
/* 	} */
/*  */
/* 	// lookup key-value. */
/* 	it = item_get(key, nkey); */
/*  */
/* 	// hit. */
/* 	if (it) { */
/* 		if (i >= c->isize && !conn_expand_items(c)) { */
/* 			item_remove(it); */
/* 			error_response(c, "SERVER_ERROR out of memory writing get response"); */
/* 			return; */
/* 		} */
/* 		// add item to remembered list (i.e., we've taken ownership of them */
/* 		// through refcounting and later must release them once we've */
/* 		// written out the iov associated with them). */
/* 		item_update(it); */
/* 		*(c->ilist + i) = it; */
/* 		i++; */
/* 	} */
/*  */
/* 	// make sure it's a single get */
/* 	key_token++; */
/* 	if (key_token->length != 0 || key_token->value != NULL) { */
/* 		error_response(c, "SERVER_ERROR only support single `get`"); */
/* 		return; */
/* 	} */
/*  */
/* 	// update our rememberd reference set. */
/* 	c->icurr = c->ilist; */
/* 	c->ileft = i; */
/*  */
/* 	// setup RPC calls. */
/* 	for (i = 0; i < t->memcache_used; i++) { */
/* 		mc = t->memcache[i]; */
/* 		if (!conn_add_msghdr(mc) != 0) { */
/* 			error_response(mc, "SERVER_ERROR out of memory preparing response"); */
/* 			return; */
/* 		} */
/* 		memcache_get(mc, c, default_key); */
/* 	} */
/* 	conn_set_state(c, conn_rpc_wait); */
/* } */

// complete the response to a get request.
void finish_get_command(conn *c) {
	item *it;
	int i;

	// setup all items for writing out.
	for (i = 0; i < c->ileft; i++) {
		it = *(c->ilist + i);
		if (it) {
			// Construct the response. Each hit adds three elements to the
			// outgoing data list:
			//   "VALUE <key> <flags> <data_length>\r\n"
			//   "<data>\r\n"
			// The <data> element is stored on the connection item list, not on
			// the iov list.
			if (!conn_add_iov(c, "VALUE ", 6) ||
				 !conn_add_iov(c, ITEM_key(it), it->nkey) ||
				 !conn_add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes)) {
				item_remove(it);
				error_response(c, "SERVER_ERROR out of memory writing get response");
				return;
			}

			if (config.verbose > 1) {
				fprintf(stderr, ">%d sending key %s\n", c->sfd, ITEM_key(it));
			}
		} else {
			fprintf(stderr, "ERROR corrupted ilist!\n");
			exit(1);
		}
	}

	if (config.verbose > 1) {
		fprintf(stderr, ">%d END\n", c->sfd);
	}

	if (!conn_add_iov(c, "END\r\n", 5) != 0) {
		error_response(c, "SERVER_ERROR out of memory writing get response");
	} else {
		conn_set_state(c, conn_mwrite);
	}
}

// process a memcached get(s) command. (we don't support CAS).
void process_get_command(conn *c, token_t *tokens, size_t ntokens,
                                bool return_cas) {
	char *key;
	size_t nkey;
	int i = 0;
	item *it;
	token_t *key_token = &tokens[KEY_TOKEN];
	char *suffix;

	assert(c != NULL);

	if (config.alloc && c->mem_blob == NULL) {
		long size = config.alloc_mean + gsl_ran_gaussian(c->thread->r, config.alloc_stddev);
		size = size <= 0 ? 10 : size;
		if (config.verbose > 0) {
			fprintf(stderr, "allocated blob: %ld\n", size);
		}

		c->mem_blob = malloc(sizeof(char) * size);
		c->mem_free_delay = 0;

		if (config.rtt_delay) {
			double r = config.rtt_mean + gsl_ran_gaussian(c->thread->r, config.rtt_stddev);
			if (r >= config.rtt_cutoff) {
				int wait = r / 100;
				if (config.verbose > 0) {
					fprintf(stderr, "delay: %d\n", wait);
				}
				c->mem_free_delay = wait;
				conn_set_state(c, conn_mwrite);
			}
		}
	}

	// process the whole command line, (only part of it may be tokenized right now)
	do {
		// process all tokenized keys at this stage.
		while(key_token->length != 0) {
			key = key_token->value;
			nkey = key_token->length;

			if(nkey > KEY_MAX_LENGTH) {
				error_response(c, "CLIENT_ERROR bad command line format");
				return;
			}

			// lookup key-value.
			it = item_get(key, nkey);

			// hit.
			if (it) {
				if (i >= c->isize && !conn_expand_items(c)) {
					item_remove(it);
					break;
				}

				// Construct the response. Each hit adds three elements to the
				// outgoing data list:
				//   "VALUE <key> <flags> <data_length>\r\n"
				//   "<data>\r\n"
				// The <data> element is stored on the connection item list, not on
				// the iov list.
				if (!conn_add_iov(c, "VALUE ", 6) != 0 ||
				    !conn_add_iov(c, ITEM_key(it), it->nkey) != 0 ||
				    !conn_add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0) {
					item_remove(it);
					break;
				}

				if (config.verbose > 1) {
					fprintf(stderr, ">%d sending key %s\n", c->sfd, key);
				}

				// add item to remembered list (i.e., we've taken ownership of them
				// through refcounting and later must release them once we've
				// written out the iov associated with them).
				item_update(it);
				*(c->ilist + i) = it;
				i++;
			}

			key_token++;
		}

		/*
		 * If the command string hasn't been fully processed, get the next set
		 * of tokens.
		 */
		if(key_token->value != NULL) {
			ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS);
			key_token = tokens;
		}

	} while(key_token->value != NULL);

	c->icurr = c->ilist;
	c->ileft = i;

	if (config.verbose > 1) {
		fprintf(stderr, ">%d END\n", c->sfd);
	}

	// If the loop was terminated because of out-of-memory, it is not reliable
	// to add END\r\n to the buffer, because it might not end in \r\n. So we
	// send SERVER_ERROR instead.
	if (key_token->value != NULL || !conn_add_iov(c, "END\r\n", 5) != 0) {
		error_response(c, "SERVER_ERROR out of memory writing get response");
	} else {
		conn_set_state(c, conn_mwrite);
	}
}

// process a memcached set command.
void process_update_command(conn *c, token_t *tokens,
                            const size_t ntokens,
                            int comm, bool handle_cas) {
	int vlen;
	assert(c != NULL);

	if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH ||
	    !safe_strtol(tokens[4].value, (int32_t *)&vlen)) {
		error_response(c, "CLIENT_ERROR bad command line format");
		return;
	}

	if (vlen < 0) {
		error_response(c, "CLIENT_ERROR bad command line format");
		return;
	}

	// setup value to be read
	c->sbytes = vlen + 2; // for \r\n consumption.
	conn_set_state(c, conn_read_value);
}

// process a memcached stat command.
void process_stat_command(conn *c, token_t *tokens, const size_t ntokens) {
	mutex_lock(&c->stats->lock);

	// just for debugging right now
	fprintf(stderr, "STAT client_id %d\n", c->stats->client_id);
	fprintf(stderr, "STAT total_connections %d\n", c->stats->total_connections);
	fprintf(stderr, "STAT live_connections %d\n", c->stats->live_connections);
	fprintf(stderr, "STAT requests %d\n", c->stats->requests);

	mutex_unlock(&c->stats->lock);
	conn_set_state(c, conn_new_cmd);
}


#ifndef LIC_OPCODES
#define LIC_OPCODES

#include <gsl/gsl>

namespace lic
{

constexpr uint8_t MethodHeaderFormatMask = 0x3;

enum class HeaderFormat : uint8_t
{
    Tiny = 0x2,
    Fat = 0x3
};

enum class Opcode : uint8_t
{
    Nop = 0x00,
    Break = 0x01,
    Ldarg_0 = 0x02,
    Ldarg_1 = 0x03,
    Ldarg_2 = 0x04,
    Ldarg_3 = 0x05,
    Ldloc_0 = 0x06,
    Ldloc_1 = 0x07,
    Ldloc_2 = 0x08,
    Ldloc_3 = 0x09,
    Stloc_0 = 0x0a,
    Stloc_1 = 0x0b,
    Stloc_2 = 0x0c,
    Stloc_3 = 0x0d,
    Ldarg_S = 0x0e,
    Ldarga_S = 0x0f,
    Starg_S = 0x10,
    Ldloc_S = 0x11,
    Ldloca_S = 0x12,
    Stloc_S = 0x13,
    Ldnull = 0x14,
    Ldc_I4_M1 = 0x15,
    Ldc_I4_0 = 0x16,
    Ldc_I4_1 = 0x17,
    Ldc_I4_2 = 0x18,
    Ldc_I4_3 = 0x19,
    Ldc_I4_4 = 0x1a,
    Ldc_I4_5 = 0x1b,
    Ldc_I4_6 = 0x1c,
    Ldc_I4_7 = 0x1d,
    Ldc_I4_8 = 0x1e,
    Ldc_I4_S = 0x1f,
    Ldc_I4 = 0x20,
    Ldc_I8 = 0x21,
    Ldc_R4 = 0x22,
    Ldc_R8 = 0x23,
    Dup = 0x25,
    Pop = 0x26,
    Jmp = 0x27,
    Call = 0x28,
    Calli = 0x29,
    Ret = 0x2a,
    Br_S = 0x2b,
    Brfalse_S = 0x2c,
    Brtrue_S = 0x2d,
    Beq_S = 0x2e,
    Bge_S = 0x2f,
    Bgt_S = 0x30,
    Ble_S = 0x31,
    Blt_S = 0x32,
    Bne_Un_S = 0x33,
    Bge_Un_S = 0x34,
    Bgt_Un_S = 0x35,
    Ble_Un_S = 0x36,
    Blt_Un_S = 0x37,
    Br = 0x38,
    Brfalse = 0x39,
    Brtrue = 0x3a,
    Beq = 0x3b,
    Bge = 0x3c,
    Bgt = 0x3d,
    Ble = 0x3e,
    Blt = 0x3f,
    Bne_Un = 0x40,
    Bge_Un = 0x41,
    Bgt_Un = 0x42,
    Ble_Un = 0x43,
    Blt_Un = 0x44,
    Switch = 0x45,
    Ldind_I1 = 0x46,
    Ldind_U1 = 0x47,
    Ldind_I2 = 0x48,
    Ldind_U2 = 0x49,
    Ldind_I4 = 0x4a,
    Ldind_U4 = 0x4b,
    Ldind_I8 = 0x4c,
    Ldind_I = 0x4d,
    Ldind_R4 = 0x4e,
    Ldind_R8 = 0x4f,
    Ldind_Ref = 0x50,
    Stind_Ref = 0x51,
    Stind_I1 = 0x52,
    Stind_I2 = 0x53,
    Stind_I4 = 0x54,
    Stind_I8 = 0x55,
    Stind_R4 = 0x56,
    Stind_R8 = 0x57,
    Add = 0x58,
    Sub = 0x59,
    Mul = 0x5a,
    Div = 0x5b,
    Div_Un = 0x5c,
    Rem = 0x5d,
    Rem_Un = 0x5e,
    And = 0x5f,
    Or = 0x60,
    Xor = 0x61,
    Shl = 0x62,
    Shr = 0x63,
    Shr_Un = 0x64,
    Neg = 0x65,
    Not = 0x66,
    Conv_I1 = 0x67,
    Conv_I2 = 0x68,
    Conv_I4 = 0x69,
    Conv_I8 = 0x6a,
    Conv_R4 = 0x6b,
    Conv_R8 = 0x6c,
    Conv_U4 = 0x6d,
    Conv_U8 = 0x6e,
    Callvirt = 0x6f,
    Cpobj = 0x70,
    Ldobj = 0x71,
    Ldstr = 0x72,
    Newobj = 0x73,
    Castclass = 0x74,
    Isinst = 0x75,
    Conv_R_Un = 0x76,
    Unbox = 0x79,
    Throw = 0x7a,
    Ldfld = 0x7b,
    Ldflda = 0x7c,
    Stfld = 0x7d,
    Ldsfld = 0x7e,
    Ldsflda = 0x7f,
    Stsfld = 0x80,
    Stobj = 0x81,
    Conv_Ovf_I1_Un = 0x82,
    Conv_Ovf_I2_Un = 0x83,
    Conv_Ovf_I4_Un = 0x84,
    Conv_Ovf_I8_Un = 0x85,
    Conv_Ovf_U1_Un = 0x86,
    Conv_Ovf_U2_Un = 0x87,
    Conv_Ovf_U4_Un = 0x88,
    Conv_Ovf_U8_Un = 0x89,
    Conv_Ovf_I_Un = 0x8a,
    Conv_Ovf_U_Un = 0x8b,
    Box = 0x8c,
    Newarr = 0x8d,
    Ldlen = 0x8e,
    Ldelema = 0x8f,
    Ldelem_I1 = 0x90,
    Ldelem_U1 = 0x91,
    Ldelem_I2 = 0x92,
    Ldelem_U2 = 0x93,
    Ldelem_I4 = 0x94,
    Ldelem_U4 = 0x95,
    Ldelem_I8 = 0x96,
    Ldelem_I = 0x97,
    Ldelem_R4 = 0x98,
    Ldelem_R8 = 0x99,
    Ldelem_Ref = 0x9a,
    Stelem_I = 0x9b,
    Stelem_I1 = 0x9c,
    Stelem_I2 = 0x9d,
    Stelem_I4 = 0x9e,
    Stelem_I8 = 0x9f,
    Stelem_R4 = 0xa0,
    Stelem_R8 = 0xa1,
    Stelem_Ref = 0xa2,
    Conv_Ovf_I1 = 0xb3,
    Conv_Ovf_U1 = 0xb4,
    Conv_Ovf_I2 = 0xb5,
    Conv_Ovf_U2 = 0xb6,
    Conv_Ovf_I4 = 0xb7,
    Conv_Ovf_U4 = 0xb8,
    Conv_Ovf_I8 = 0xb9,
    Conv_Ovf_U8 = 0xba,
    Refanyval = 0xc2,
    Ckfinite = 0xc3,
    Mkrefany = 0xc6,
    Ldtoken = 0xd0,
    Conv_U2 = 0xd1,
    Conv_U1 = 0xd2,
    Conv_I = 0xd3,
    Conv_Ovf_I = 0xd4,
    Conv_Ovf_U = 0xd5,
    Add_Ovf = 0xd6,
    Add_Ovf_Un = 0xd7,
    Mul_Ovf = 0xd8,
    Mul_Ovf_Un = 0xd9,
    Sub_Ovf = 0xda,
    Sub_Ovf_Un = 0xdb,
    Endfinally = 0xdc,
    Leave = 0xdd,
    Leave_S = 0xde,
    Stind_I = 0xdf,
    Conv_U = 0xe0,

    LongOpcodePrefix = 0xfe
};

enum class LongOpcodeSuffix: uint8_t
{
    Arglist = 0x00,
    Ceq = 0x01,
    Cgt = 0x02,
    Cgt_Un = 0x03,
    Clt = 0x04,
    Clt_Un = 0x05,
    Ldftn = 0x06,
    Ldvirtftn = 0x07,
    Ldarg = 0x09,
    Ldarga = 0x0a,
    Starg = 0x0b,
    Ldloc = 0x0c,
    Ldloca = 0x0d,
    Stloc = 0x0e,
    Localloc = 0x0f,
    Endfilter = 0x11,
    Unaligned_ = 0x12,
    Volatile_ = 0x13,
    Tail_ = 0x14,
    Initobj = 0x15,
    Cpblk = 0x17,
    Initblk = 0x18,
    Rethrow = 0x1a,
    Sizeof = 0x1c,
    Refanytype = 0x1d,

    InvalidOpcode = 0xff
};

const char* OpcodeName(Opcode first, LongOpcodeSuffix second = LongOpcodeSuffix::InvalidOpcode);

}

#endif // !LIC_OPCODES


/*	$Id$ */
/*
 * Copyright (c) 2014, 2015 Kristaps Dzonsons <kristaps@kcons.eu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <cairo.h>
#include <gtk/gtk.h>
#include <gsl/gsl_multifit.h>
#include <kplot.h>

#include "extern.h"

void
draw(GtkWidget *w, cairo_t *cr, struct curwin *cur)
{
	double		 x, y;

	cur->redraw = 0;

	/* White-out the view. */
	x = gtk_widget_get_allocated_width(w);
	y = gtk_widget_get_allocated_height(w);
	cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
	cairo_rectangle(cr, 0.0, 0.0, x, y);
	cairo_fill(cr);

	/* Draw our plot. */
	kplot_draw(cur->views[cur->view], x, y, cr);
}


#pragma once
#include "halley/plugin/iasset_importer.h"
#include <gsl/gsl>

namespace Halley
{
	class AudioImporter : public IAssetImporter
	{
	public:
		ImportAssetType getType() const override { return ImportAssetType::Audio; }

		void import(const ImportingAsset& asset, IAssetCollector& collector) override;

	private:
		Bytes encodeVorbis(int channels, int sampleRate, gsl::span<const std::vector<float>> src);
		static std::vector<float> resampleChannel(int from, int to, gsl::span<const float> src);
	};
}


#pragma once
#include <gsl/gsl>
#include <typeindex>
#include "halley/utils/utils.h"
#include "halley/data_structures/maybe.h"
#include "halley/file/byte_serializer.h"

namespace Halley
{
	class IMessageStream;
	class MessageQueue;
	class MessageQueueUDP;
	class MessageQueueTCP;

	class NetworkMessage
	{
		friend class MessageQueue;
		friend class MessageQueueUDP;
		friend class MessageQueueTCP;

	public:
		virtual ~NetworkMessage() = default;

		size_t getSerializedSize() const
		{
			if (!serialized) {
				serialized = Serializer::toBytes(*this);
			}
			return serialized.get().size();
		}

		void serializeTo(gsl::span<gsl::byte> dst) const
		{
			if (!serialized) {
				serialized = Serializer::toBytes(*this);
			}
			memcpy(dst.data(), serialized.get().data(), serialized.get().size());
		}

		virtual void serialize(Serializer& s) const = 0;

	private:
		unsigned short seq = 0;
		char channel = -1;

		mutable Maybe<Bytes> serialized;
	};

	class NetworkMessageFactoryBase
	{
	public:
		virtual ~NetworkMessageFactoryBase() {}

		virtual std::unique_ptr<NetworkMessage> create(gsl::span<const gsl::byte> src) const = 0;
		virtual std::type_index getTypeIndex() const = 0;
	};

	template <typename T>
	class NetworkMessageFactory : public NetworkMessageFactoryBase
	{
	public:
		std::unique_ptr<NetworkMessage> create(gsl::span<const gsl::byte> src) const override
		{
			return std::make_unique<T>(src);
		}

		std::type_index getTypeIndex() const override
		{
			return std::type_index(typeid(T));
		}
	};
}


#pragma once

#include <renderer/Shading.h>
#include <renderer/VertexSpecification.h>
#include <gsl/span>

#include <cassert>

namespace ad {
namespace graphics {

struct GenericDrawer
{
    VertexArrayObject mVertexArray;
    Program mProgram;

    std::size_t mVertexCount{0};
    std::size_t mInstanceCount{1};

    template <class T_vertex>
    VertexBufferObject addVertexBuffer(
        const std::initializer_list<AttributeDescription> & aAttributes,
        gsl::span<T_vertex> aVertices)
    {
        if (mVertexCount)
        {
            assert(static_cast<std::size_t>(aVertices.size()) == mVertexCount);
        }
        else
        {
            mVertexCount = aVertices.size();
        }

        return loadVertexBuffer(mVertexArray,
                                aAttributes,
                                sizeof(T_vertex),
                                sizeof(T_vertex)*aVertices.size(),
                                aVertices.data());
    }

    void render() const
    {
        glBindVertexArray(mVertexArray);
        glUseProgram(mProgram);

        glDrawArraysInstanced(GL_TRIANGLE_STRIP,
                              0,
                              mVertexCount,
                              mInstanceCount);
    }
};

} // namespace graphics
} // namespace ad


/**
 * This file is part of the "libterminal" project
 *   Copyright (c) 2019-2020 Christian Parpart <christian@parpart.family>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the 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.
 */
#pragma once

#include <terminal/GraphicsAttributes.h>
#include <terminal/primitives.h>

#include <crispy/Comparison.h>
#include <crispy/assert.h>

#include <gsl/span>
#include <gsl/span_ext>

#include <iterator>
#include <sstream>
#include <string>
#include <vector>

namespace terminal
{

enum class LineFlags : uint8_t
{
    None = 0x0000,
    Wrappable = 0x0001,
    Wrapped = 0x0002,
    Marked = 0x0004,
    // TODO: DoubleWidth  = 0x0010,
    // TODO: DoubleHeight = 0x0020,
};

template <typename, bool>
struct OptionalProperty;
template <typename T>
struct OptionalProperty<T, false>
{
};
template <typename T>
struct OptionalProperty<T, true>
{
    T value;
};

struct SimpleLineBuffer
{
    GraphicsAttributes attributes;
    std::string text;  // TODO: Try std::string_view later to avoid scattered copies.
    ColumnCount width; // page display width
};

template <typename Cell>
using InflatedLineBuffer = std::vector<Cell>;

template <typename Cell>
InflatedLineBuffer<Cell> inflate(SimpleLineBuffer const& input);

template <typename Cell>
using LineStorage = std::variant<SimpleLineBuffer, InflatedLineBuffer<Cell>>;

/**
 * Line<Cell> API.
 *
 * TODO: Use custom allocator for ensuring cache locality of Cells to sibling lines.
 * TODO: Make the line optimization work.
 */
template <typename Cell, bool Optimize = false>
class Line
{
  public:
    Line() = default;
    Line(Line const&) = default;
    Line(Line&&) noexcept = default;
    Line& operator=(Line const&) = default;
    Line& operator=(Line&&) noexcept = default;

    using InflatedBuffer = InflatedLineBuffer<Cell>;
    using Storage = LineStorage<Cell>;
    using value_type = Cell;
    using iterator = typename InflatedBuffer::iterator;
    using reverse_iterator = typename InflatedBuffer::reverse_iterator;
    using const_iterator = typename InflatedBuffer::const_iterator;

    Line(ColumnCount _width, LineFlags _flags, Cell _template = {}):
        buffer_(_width.as<size_t>(), _template /*, _allocator*/), flags_ { static_cast<unsigned>(_flags) }
    {
    }

    Line(ColumnCount _width, InflatedBuffer _buffer, LineFlags _flags):
        buffer_ { std::move(_buffer) }, flags_ { static_cast<unsigned>(_flags) }
    {
        buffer_.resize(unbox<size_t>(_width));
    }

    Line(InflatedBuffer _buffer, LineFlags _flags):
        buffer_ { std::move(_buffer) }, flags_ { static_cast<unsigned>(_flags) }
    {
    }

    constexpr static inline bool ColumnOptimized = Optimize;

    // This is experimental (aka. buggy) and going to be replaced with another optimization idea soon.
    //#define LINE_AVOID_CELL_RESET 1

    void reset(LineFlags _flags,
               GraphicsAttributes _attributes) noexcept // TODO: optimize by having no need to O(n) iterate
                                                        // through all buffer cells.
    {
        flags_ = static_cast<unsigned>(_flags);

        // if constexpr (ColumnOptimized)
        // {
        //     #if !defined(LINE_AVOID_CELL_RESET)
        //     if (buffer_.back().backgroundColor() != _attributes.backgroundColor)
        //         // TODO: also styles and UL color
        //         markUsedFirst(ColumnCount::cast_from(buffer_.size()));
        //
        //     for (auto i = 0; i < *columnsUsed(); ++i)
        //         buffer_[i].reset(_attributes);
        //     #endif
        //     markUsedFirst(ColumnCount(0));
        // }
        // else
        {
            for (Cell& cell: buffer_)
                cell.reset(_attributes);
        }
    }

    void markUsedFirst(ColumnCount /*_n*/) noexcept
    {
        // if constexpr (ColumnOptimized)
        //     usedColumns_.value = _n;
    }

    void reset(LineFlags _flags,
               GraphicsAttributes const& _attributes,
               char32_t _codepoint,
               uint8_t _width) noexcept
    {
        flags_ = static_cast<unsigned>(_flags);
        markUsedFirst(size());
        for (Cell& cell: buffer_)
        {
            cell.reset();
            cell.write(_attributes, _codepoint, _width);
        }
    }

    /**
     * Fills this line with the given content.
     *
     * @p _start offset into this line of the first charater
     * @p _sgr graphics rendition for the line starting at @c _start until the end
     * @p _ascii the US-ASCII characters to fill with
     */
    void fill(ColumnOffset _start, GraphicsAttributes const& _sgr, std::string_view _ascii)
    {
        auto& buffer = editable();

        assert(unbox<size_t>(_start) + _ascii.size() <= buffer.size());

        auto constexpr ASCII_Width = 1;
        auto const* s = _ascii.data();

        Cell* i = &buffer_[unbox<size_t>(_start)];
        Cell* e = i + _ascii.size();
        while (i != e)
            (i++)->write(_sgr, static_cast<char32_t>(*s++), ASCII_Width);

        // if constexpr (ColumnOptimized)
        // {
        //     #if !defined(LINE_AVOID_CELL_RESET)
        //     auto const e2 = buffer.data() + unbox<long>(columnsUsed());
        //     while (i != e2)
        //         (i++)->reset();
        //     #endif
        //     usedColumns_.value = boxed_cast<ColumnCount>(_start) + ColumnCount::cast_from(_ascii.size());
        // }
        // else
        {
            auto const e2 = buffer.data() + buffer.size();
            while (i != e2)
                (i++)->reset();
        }
    }

    ColumnCount size() const noexcept { return ColumnCount::cast_from(buffer_.size()); }

    ColumnCount columnsUsed() const noexcept
    {
        // if constexpr (ColumnOptimized)
        //     return usedColumns_.value;
        // else
        return size();
    }

    void resize(ColumnCount _count);

    gsl::span<Cell const> trim_blank_right() const noexcept;

    gsl::span<Cell const> cells() const noexcept { return buffer_; }

    gsl::span<Cell> useRange(ColumnOffset _start, ColumnCount _count) noexcept
    {
        markUsedFirst(std::max(columnsUsed(), boxed_cast<ColumnCount>(_start) + _count));
#if defined(__FreeBSD__)
        auto const bufferSpan = gsl::span(buffer_);
        return bufferSpan.subspan(unbox<size_t>(_start), unbox<size_t>(_count));
#else
        // NOTE: On FreeBSD the line below does not compile.
        return gsl::span(buffer_).subspan(unbox<size_t>(_start), unbox<size_t>(_count));
#endif
    }

    iterator begin() noexcept { return buffer_.begin(); }
    iterator end() noexcept { return std::next(std::begin(buffer_), unbox<int>(columnsUsed())); }

    const_iterator begin() const noexcept { return buffer_.begin(); }
    const_iterator end() const noexcept { return std::next(buffer_.begin(), unbox<int>(columnsUsed())); }

    reverse_iterator rbegin() noexcept { return buffer_.rbegin(); }
    reverse_iterator rend() noexcept { return buffer_.rend(); }

    Cell& front() noexcept { return buffer_.front(); }
    Cell const& front() const noexcept { return buffer_.front(); }

    Cell& back() noexcept { return *std::next(buffer_.begin(), unbox<int>(columnsUsed() - 1)); }
    Cell const& back() const noexcept { return *std::next(buffer_.begin(), unbox<int>(columnsUsed() - 1)); }

    Cell& useCellAt(ColumnOffset _column) noexcept
    {
        Require(ColumnOffset(0) <= _column);
        Require(_column <= ColumnOffset::cast_from(buffer_.size())); // Allow off-by-one for sentinel.
        return editable()[unbox<size_t>(_column)];
    }

    Cell const& at(ColumnOffset _column) const noexcept
    {
        Require(ColumnOffset(0) <= _column);
        Require(_column <= ColumnOffset::cast_from(buffer_.size())); // Allow off-by-one for sentinel.
        return buffer_[unbox<size_t>(_column)];
    }

    LineFlags flags() const noexcept { return static_cast<LineFlags>(flags_); }

    bool marked() const noexcept { return isFlagEnabled(LineFlags::Marked); }
    void setMarked(bool _enable) { setFlag(LineFlags::Marked, _enable); }

    bool wrapped() const noexcept { return isFlagEnabled(LineFlags::Wrapped); }
    void setWrapped(bool _enable) { setFlag(LineFlags::Wrapped, _enable); }

    bool wrappable() const noexcept { return isFlagEnabled(LineFlags::Wrappable); }
    void setWrappable(bool _enable) { setFlag(LineFlags::Wrappable, _enable); }

    LineFlags wrappableFlag() const noexcept { return wrappable() ? LineFlags::Wrappable : LineFlags::None; }
    LineFlags wrappedFlag() const noexcept { return marked() ? LineFlags::Wrapped : LineFlags::None; }
    LineFlags markedFlag() const noexcept { return marked() ? LineFlags::Marked : LineFlags::None; }

    LineFlags inheritableFlags() const noexcept
    {
        auto constexpr Inheritables = unsigned(LineFlags::Wrappable) | unsigned(LineFlags::Marked);
        return static_cast<LineFlags>(flags_ & Inheritables);
    }

    void setFlag(LineFlags _flag, bool _enable) noexcept
    {
        if (_enable)
            flags_ |= static_cast<unsigned>(_flag);
        else
            flags_ &= ~static_cast<unsigned>(_flag);
    }

    bool isFlagEnabled(LineFlags _flag) const noexcept
    {
        return (flags_ & static_cast<unsigned>(_flag)) != 0;
    }

    InflatedBuffer reflow(ColumnCount _newColumnCount);
    std::string toUtf8() const;
    std::string toUtf8Trimmed() const;

    // Returns a reference to this mutable grid-line buffer.
    //
    // If this line has been stored in an optimized state, then
    // the line will be first unpacked into a vector of grid cells.
    InflatedBuffer& editable();

  private:
    InflatedBuffer buffer_;
    Storage storage_;
    unsigned flags_ = 0;
    // OptionalProperty<ColumnCount, ColumnOptimized> usedColumns_;
};

constexpr LineFlags operator|(LineFlags a, LineFlags b) noexcept
{
    return LineFlags(unsigned(a) | unsigned(b));
}

constexpr LineFlags operator~(LineFlags a) noexcept
{
    return LineFlags(~unsigned(a));
}

constexpr LineFlags operator&(LineFlags a, LineFlags b) noexcept
{
    return LineFlags(unsigned(a) & unsigned(b));
}

template <typename Cell, bool Optimize>
inline typename Line<Cell, Optimize>::InflatedBuffer& Line<Cell, Optimize>::editable()
{
    // TODO: when we impement the line text buffer optimization,
    // then this is the place where we want to *promote* a possibly
    // optimized text buffer to a full grid line buffer.
#if 0
    if (std::holds_alternative<SimpleLineBuffer>(storage_))
        storage_ = inflate<Cell>(std::get<SimpleLineBuffer>(storage_));

    return std::get<InflatedBuffer>(storage_);
#else
    return buffer_;
#endif
}

} // namespace terminal

namespace fmt // {{{
{
template <>
struct formatter<terminal::LineFlags>
{
    template <typename ParseContext>
    auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template <typename FormatContext>
    auto format(const terminal::LineFlags _flags, FormatContext& ctx) const
    {
        static const std::array<std::pair<terminal::LineFlags, std::string_view>, 3> nameMap = {
            std::pair { terminal::LineFlags::Wrappable, std::string_view("Wrappable") },
            std::pair { terminal::LineFlags::Wrapped, std::string_view("Wrapped") },
            std::pair { terminal::LineFlags::Marked, std::string_view("Marked") },
        };
        std::string s;
        for (auto const& mapping: nameMap)
        {
            if ((mapping.first & _flags) != terminal::LineFlags::None)
            {
                if (!s.empty())
                    s += ",";
                s += mapping.second;
            }
        }
        return format_to(ctx.out(), s);
    }
};
} // namespace fmt


#pragma once

#include <nextalign/nextalign.h>

#include <gsl/string_span>
#include <string>
#include <vector>

template<typename Letter>
using SequenceSpan = gsl::basic_string_span<Letter, gsl::dynamic_extent>;

using NucleotideSequenceSpan = SequenceSpan<Nucleotide>;

using AminoacidSequenceSpan = SequenceSpan<Aminoacid>;

Nucleotide toNucleotide(char nuc);

Nucleotide stringToNuc(const std::string& nuc);

char nucToChar(Nucleotide nuc);

std::string nucToString(Nucleotide nuc);

Aminoacid charToAa(char aa);

Aminoacid stringToAa(const std::string& aa);

char aaToChar(Aminoacid aa);

std::string aaToString(Aminoacid aa);

template<typename Letter>
struct LetterTag {};

template<typename Letter>
inline Letter stringToLetter(const std::string& str, LetterTag<Letter>);

template<>
inline Nucleotide stringToLetter<Nucleotide>(const std::string& str, LetterTag<Nucleotide>) {
  return stringToNuc(str);
}

template<>
inline Aminoacid stringToLetter<Aminoacid>(const std::string& str, LetterTag<Aminoacid>) {
  return stringToAa(str);
}

template<typename Letter>
inline std::string letterToString(Letter letter);

template<>
inline std::string letterToString(Nucleotide letter) {
  return nucToString(letter);
}

template<>
inline std::string letterToString(Aminoacid letter) {
  return aaToString(letter);
}

std::vector<Insertion> toInsertionsExternal(const std::vector<InsertionInternal<Nucleotide>>& insertions);

std::vector<Peptide> toPeptidesExternal(const std::vector<PeptideInternal>& peptides);

std::vector<RefPeptide> toRefPeptidesExternal(const std::vector<RefPeptideInternal>& peptides);

inline std::ostream& operator<<(std::ostream& os, const Nucleotide& nuc) {
  os << "'" << nucToString(nuc) << "'";
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const NucleotideSequence& seq) {
  os << "\"";
  for (const auto& nuc : seq) {
    os << nucToString(nuc);
  }
  os << "\"";
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const Aminoacid& aa) {
  os << "'" << aaToString(aa) << "'";
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const AminoacidSequence& seq) {
  os << "\"";
  for (const auto& aa : seq) {
    os << aaToString(aa);
  }
  os << "\"";
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const Range& f) {
  os << "{ " << f.begin << ", " << f.end << " }";
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const FrameShiftContext& f) {
  os << "{ "                        //
     << "codon: " << f.codon << ", "//
     << "}"                         //
    ;
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const FrameShiftResult& f) {
  os << "{ "                                      //
     << "geneName: \"" << f.geneName << "\", "    //
     << "nucRel: " << f.nucRel << ", "            //
     << "nucAbs: " << f.nucAbs << ", "            //
     << "codon: " << f.codon << ", "              //
     << "gapsLeading: " << f.gapsLeading << ", "  //
     << "gapsTrailing: " << f.gapsTrailing << ", "//
     << "}"                                       //
    ;
  return os;
}


#pragma once

#include "NativeDataStream.h"
#include "PerFrameValue.h"
#include "ShaderCompiler.h"
#include "VertexArray.h"

#include <Babylon/JsRuntime.h>
#include <Babylon/JsRuntimeScheduler.h>

#include <GraphicsImpl.h>
#include <BgfxCallback.h>
#include <FrameBuffer.h>

#include <napi/napi.h>

#include <bgfx/bgfx.h>
#include <bgfx/platform.h>
#include <bimg/bimg.h>
#include <bx/allocator.h>

#include <gsl/gsl>

#include <arcana/threading/cancellation.h>
#include <unordered_map>

namespace Babylon
{
    struct UniformInfo final
    {
        UniformInfo(uint8_t stage, bgfx::UniformHandle handle) :
            Stage{stage},
            Handle{handle}
        {
        }

        uint8_t Stage{};
        bgfx::UniformHandle Handle{bgfx::kInvalidHandle};
    };

    struct ProgramData final
    {
        ProgramData() = default;
        ProgramData(ProgramData&& other) = delete;
        ProgramData(const ProgramData&) = delete;
        ProgramData& operator=(ProgramData&& other) = delete;
        ProgramData& operator=(const ProgramData& other) = delete;

        ~ProgramData()
        {
            Dispose();
        }

        void Dispose()
        {
            if (!Disposed && bgfx::isValid(Handle))
            {
                bgfx::destroy(Handle);
            }
            Disposed = true;
        }

        std::unordered_map<std::string, uint32_t> VertexAttributeLocations{};
        std::unordered_map<std::string, UniformInfo> UniformInfos{};

        bgfx::ProgramHandle Handle{bgfx::kInvalidHandle};
        bool Disposed{false};

        struct UniformValue
        {
            std::vector<float> Data{};
            uint16_t ElementLength{};
        };

        std::unordered_map<uint16_t, UniformValue> Uniforms{};

        void SetUniform(bgfx::UniformHandle handle, gsl::span<const float> data, size_t elementLength = 1)
        {
            UniformValue& value = Uniforms[handle.idx];
            value.Data.assign(data.begin(), data.end());
            value.ElementLength = static_cast<uint16_t>(elementLength);
        }
    };

    class NativeEngine final : public Napi::ObjectWrap<NativeEngine>
    {
        static constexpr auto JS_CLASS_NAME = "_NativeEngine";
        static constexpr auto JS_CONSTRUCTOR_NAME = "Engine";

    public:
        NativeEngine(const Napi::CallbackInfo& info);
        NativeEngine(const Napi::CallbackInfo& info, JsRuntime& runtime);
        ~NativeEngine();

        static void Initialize(Napi::Env env);

    private:
        void Dispose();

        void Dispose(const Napi::CallbackInfo& info);
        void RequestAnimationFrame(const Napi::CallbackInfo& info);
        Napi::Value CreateVertexArray(const Napi::CallbackInfo& info);
        void DeleteVertexArray(NativeDataStream::Reader& data);
        void BindVertexArray(NativeDataStream::Reader& data);
        Napi::Value CreateIndexBuffer(const Napi::CallbackInfo& info);
        void DeleteIndexBuffer(NativeDataStream::Reader& data);
        void RecordIndexBuffer(const Napi::CallbackInfo& info);
        void UpdateDynamicIndexBuffer(const Napi::CallbackInfo& info);
        Napi::Value CreateVertexBuffer(const Napi::CallbackInfo& info);
        void DeleteVertexBuffer(NativeDataStream::Reader& data);
        void RecordVertexBuffer(const Napi::CallbackInfo& info);
        void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info);
        Napi::Value CreateProgram(const Napi::CallbackInfo& info);
        Napi::Value GetUniforms(const Napi::CallbackInfo& info);
        Napi::Value GetAttributes(const Napi::CallbackInfo& info);
        void SetProgram(NativeDataStream::Reader& data);
        void DeleteProgram(NativeDataStream::Reader& data);
        void SetState(NativeDataStream::Reader& data);
        void SetZOffset(NativeDataStream::Reader& data);
        void SetZOffsetUnits(NativeDataStream::Reader& data);
        void SetDepthTest(NativeDataStream::Reader& data);
        void SetDepthWrite(NativeDataStream::Reader& data);
        void SetColorWrite(NativeDataStream::Reader& data);
        void SetBlendMode(NativeDataStream::Reader& data);
        void SetMatrix(NativeDataStream::Reader& data);
        void SetInt(NativeDataStream::Reader& data);
        void SetIntArray(NativeDataStream::Reader& data);
        void SetIntArray2(NativeDataStream::Reader& data);
        void SetIntArray3(NativeDataStream::Reader& data);
        void SetIntArray4(NativeDataStream::Reader& data);
        void SetFloatArray(NativeDataStream::Reader& data);
        void SetFloatArray2(NativeDataStream::Reader& data);
        void SetFloatArray3(NativeDataStream::Reader& data);
        void SetFloatArray4(NativeDataStream::Reader& data);
        void SetMatrices(NativeDataStream::Reader& data);
        void SetMatrix3x3(NativeDataStream::Reader& data);
        void SetMatrix2x2(NativeDataStream::Reader& data);
        void SetFloat(NativeDataStream::Reader& data);
        void SetFloat2(NativeDataStream::Reader& data);
        void SetFloat3(NativeDataStream::Reader& data);
        void SetFloat4(NativeDataStream::Reader& data);
        Napi::Value CreateTexture(const Napi::CallbackInfo& info);
        void LoadTexture(const Napi::CallbackInfo& info);
        void CopyTexture(const Napi::CallbackInfo& info);
        void LoadRawTexture(const Napi::CallbackInfo& info);
        void LoadCubeTexture(const Napi::CallbackInfo& info);
        void LoadCubeTextureWithMips(const Napi::CallbackInfo& info);
        Napi::Value GetTextureWidth(const Napi::CallbackInfo& info);
        Napi::Value GetTextureHeight(const Napi::CallbackInfo& info);
        void SetTextureSampling(NativeDataStream::Reader& data);
        void SetTextureWrapMode(NativeDataStream::Reader& data);
        void SetTextureAnisotropicLevel(NativeDataStream::Reader& data);
        void SetTexture(NativeDataStream::Reader& data);
        void DeleteTexture(const Napi::CallbackInfo& info);
        Napi::Value CreateFrameBuffer(const Napi::CallbackInfo& info);
        void DeleteFrameBuffer(NativeDataStream::Reader& data);
        void BindFrameBuffer(NativeDataStream::Reader& data);
        void UnbindFrameBuffer(NativeDataStream::Reader& data);
        void DrawIndexed(NativeDataStream::Reader& data);
        void Draw(NativeDataStream::Reader& data);
        void Clear(NativeDataStream::Reader& data);
        Napi::Value GetRenderWidth(const Napi::CallbackInfo& info);
        Napi::Value GetRenderHeight(const Napi::CallbackInfo& info);
        void SetViewPort(const Napi::CallbackInfo& info);
        Napi::Value GetHardwareScalingLevel(const Napi::CallbackInfo& info);
        void SetHardwareScalingLevel(const Napi::CallbackInfo& info);
        Napi::Value CreateImageBitmap(const Napi::CallbackInfo& info);
        Napi::Value ResizeImageBitmap(const Napi::CallbackInfo& info);
        void GetFrameBufferData(const Napi::CallbackInfo& info);
        void SetStencil(NativeDataStream::Reader& data);
        void SetCommandDataStream(const Napi::CallbackInfo& info);
        void SubmitCommands(const Napi::CallbackInfo& info);
        void DrawInternal(bgfx::Encoder* encoder, uint32_t fillMode);

        std::string ProcessShaderCoordinates(const std::string& vertexSource);

        GraphicsImpl::UpdateToken& GetUpdateToken();
        FrameBuffer& GetBoundFrameBuffer(bgfx::Encoder& encoder);

        std::shared_ptr<arcana::cancellation_source> m_cancellationSource{};

        ShaderCompiler m_shaderCompiler{};

        ProgramData* m_currentProgram{nullptr};

        JsRuntime& m_runtime;
        GraphicsImpl& m_graphicsImpl;

        JsRuntimeScheduler m_runtimeScheduler;

        std::optional<GraphicsImpl::UpdateToken> m_updateToken{};

        void ScheduleRequestAnimationFrameCallbacks();
        bool m_requestAnimationFrameCallbacksScheduled{};

        bx::DefaultAllocator m_allocator{};
        uint64_t m_engineState{BGFX_STATE_DEFAULT};
        uint32_t m_stencilState{BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP | BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_REPLACE};

        template<int size, typename arrayType>
        void SetTypeArrayN(const UniformInfo& uniformInfo, const uint32_t elementLength, const arrayType& array);

        template<int size>
        void SetIntArrayN(NativeDataStream::Reader& data);

        template<int size>
        void SetFloatArrayN(NativeDataStream::Reader& data);

        template<int size>
        void SetFloatN(NativeDataStream::Reader& data);

        template<int size>
        void SetMatrixN(NativeDataStream::Reader& data);

        // Scratch vector used for data alignment.
        std::vector<float> m_scratch{};

        std::vector<Napi::FunctionReference> m_requestAnimationFrameCallbacks{};

        VertexArray* m_boundVertexArray{};
        FrameBuffer m_defaultFrameBuffer;
        FrameBuffer* m_boundFrameBuffer{};
        PerFrameValue<bool> m_boundFrameBufferNeedsRebinding;

        // TODO: This should be changed to a non-owning ref once multi-update is available.
        NativeDataStream* m_commandStream{};
    };
}


#pragma once

#include <algorithm>
#include <gsl/gsl_assert>
#include "family_type.h"
#include "family_mask.h"
#include "entity_id.h"
#include "halley/data_structures/nullable_reference.h"
#include "halley/support/exception.h"
#include "halley/support/debug.h"
#include "halley/utils/utils.h"

namespace Halley {
	class Entity;
	class FamilyBindingBase;

	class Family {
		friend class World;

	public:
		explicit Family(FamilyMaskType inclusionMask, FamilyMaskType optionalMask);
		virtual ~Family() {}

		size_t count() const
		{
			return elemCount;
		}

		void* getElement(size_t n) const
		{
			return static_cast<char*>(elems) + (n * elemSize);
		}

		void addOnEntitiesAdded(FamilyBindingBase* bind);
		void removeOnEntityAdded(FamilyBindingBase* bind);
		void addOnEntitiesRemoved(FamilyBindingBase* bind);
		void removeOnEntityRemoved(FamilyBindingBase* bind);
		void addOnEntitiesReloaded(FamilyBindingBase* bind);
		void removeOnEntitiesReloaded(FamilyBindingBase* bind);

		void notifyAdd(void* entities, size_t count);
		void notifyRemove(void* entities, size_t count);
		void notifyReload(void* entities, size_t count);

	protected:
		virtual void addEntity(Entity& entity) = 0;
		virtual void refreshEntity(Entity& entity) = 0;
		void removeEntity(Entity& entity);
		void reloadEntity(Entity& entity);
		virtual void updateEntities() = 0;
		virtual void clearEntities() = 0;

		void* elems = nullptr;
		size_t elemCount = 0;
		size_t elemSize = 0;
		Vector<EntityId> toRemove;
		Vector<EntityId> toReload;

		Vector<FamilyBindingBase*> addEntityCallbacks;
		Vector<FamilyBindingBase*> removeEntityCallbacks;
		Vector<FamilyBindingBase*> modifiedEntityCallbacks;

	private:
		FamilyMaskType inclusionMask;
		FamilyMaskType optionalMask;
	};

	class FamilyBase {
	protected:
		NullableReferenceAnchor anchor;

	public:
		EntityId entityId;
	};

	template <typename T>
	class FamilyBaseOf : public FamilyBase {
	public:
		NullableReferenceOf<T> getReference()
		{
			return anchor.getReferenceOf<T>();
		}

		NullableReferenceOf<const T> getReference() const
		{
			return anchor.getReferenceOf<const T>();
		}
	};

	// Apple's Clang 3.5 does not seem to have constexpr std::max...
	constexpr size_t maxSize(size_t a, size_t b)
	{
		return a > b ? a : b;
	}

	template <typename T>
	class FamilyImpl : public Family
	{
		constexpr static size_t storageSize = sizeof(T) - alignUp(sizeof(FamilyBase), alignof(void*));
		static_assert(std::is_base_of<FamilyBase, T>::value, "Family type does not derive from FamilyBase");

		// I don't know why this needs to be aligned up to 8 on Win32. :|
		static_assert(alignUp(T::Type::getNumComponents() * sizeof(void*), size_t(8)) == storageSize, "Family type has unexpected storage size");

		struct StorageType : public FamilyBase
		{
			alignas(alignof(void*)) std::array<char, storageSize> data;
		};

	public:
		explicit FamilyImpl(MaskStorage& storage)
			: Family(T::Type::inclusionMask(storage), T::Type::optionalMask(storage))
		{
		}

	protected:
		void addEntity(Entity& entity) override
		{
			auto& e = entities.emplace_back();
			e.entityId = entity.getEntityId();
			T::Type::loadComponents(entity, &e.data[0]);

			dirty = true;
		}

		void refreshEntity(Entity& entity) override
		{
			for (auto& e: entities) {
				if (e.entityId == entity.getEntityId()) {
					T::Type::loadComponents(entity, &e.data[0]);
					break;
				}
			}
		}

		void updateEntities() override
		{
			if (dirty) {
				// Notify additions
				HALLEY_DEBUG_TRACE();
				size_t prevSize = elemCount;
				size_t curSize = entities.size();
				updateElems();
				Expects(curSize >= prevSize);
				if (curSize > prevSize) {
					notifyAdd(entities.data() + prevSize, curSize - prevSize);
				}

				dirty = false;
			}

			if (!toReload.empty()) {
				// Notify reloads
				HALLEY_DEBUG_TRACE();
				std::vector<StorageType*> reloadedEntities;
				for (auto& entity : entities) {
					if (std::find(toReload.begin(), toReload.end(), entity.entityId) != toReload.end()) {
						reloadedEntities.push_back(&entity);
					}
				}
				notifyReload(reloadedEntities.data(), reloadedEntities.size());
				toReload.clear();
			}

			// Remove
			removeDeadEntities();
		}

		void clearEntities() override
		{
			notifyRemove(entities.data(), entities.size());
			entities.clear();
			updateElems();
		}

	private:
		Vector<StorageType> entities;
		bool dirty = false;

		void updateElems()
		{
			elems = entities.empty() ? nullptr : entities.data();
			elemCount = entities.size();
			elemSize = sizeof(StorageType);
		}

		void removeDeadEntities()
		{
			// Performance-critical code
			// Benchmarks suggest that using a Vector is faster than std::set and std::unordered_set
			if (!toRemove.empty()) {
				HALLEY_DEBUG_TRACE();
				size_t removeCount = toRemove.size();
				Expects(removeCount > 0);
				Expects(removeCount <= entities.size());
				std::sort(toRemove.begin(), toRemove.end());

				for (size_t i = 1; i < toRemove.size(); ++i) {
					Expects(toRemove[i - 1] != toRemove[i]);
				}

				// Move all entities to be removed to the back of the vector
				{
					int n = int(entities.size());
					// Note: it's important to scan it forward. Scanning backwards would improve performance for short-lived entities,
					// but it causes an issue where an entity is removed and added to the same family in one frame.
					for (int i = 0; i < n; i++) {
						EntityId id = entities[i].entityId;
						auto iter = std::lower_bound(toRemove.begin(), toRemove.end(), id);
						if (iter != toRemove.end() && id == *iter) {
							toRemove.erase(iter);
							if (i != n - 1) {
								std::swap(entities[i], entities[n - 1]);
								i--;
							}
							n--;
							if (toRemove.empty()) {
								break;
							}
						}
					}
					Ensures(size_t(n) + removeCount == entities.size());
				}

				Expects(toRemove.empty());

				// Notify removal
				size_t newSize = entities.size() - removeCount;
				Ensures(newSize < entities.size());
				notifyRemove(entities.data() + newSize, removeCount);

				// Remove them
				entities.resize(newSize);
				updateElems();
			}
			Ensures(toRemove.empty());
		}
	};
}


#pragma once

#include "halley/net/connection/iconnection.h"
#include "halley/net/connection/network_packet.h"

#ifdef _MSC_VER
#pragma warning(disable: 4834)
#endif
#define BOOST_SYSTEM_NO_DEPRECATED
#define BOOST_ERROR_CODE_HEADER_ONLY
#include <boost/asio.hpp>

#include <deque>
#include <array>
#include <string>
#include <gsl/gsl>

namespace Halley
{
	class NetworkService;
	using UDPEndpoint = boost::asio::ip::udp::endpoint;
	using UDPSocket = boost::asio::ip::udp::socket;

	class AsioUDPConnection : public IConnection
	{
	public:
		AsioUDPConnection(UDPSocket& socket, UDPEndpoint remote);

		void close() override;
		ConnectionStatus getStatus() const override { return status; }
		void send(OutboundNetworkPacket&& packet) override;
		bool receive(InboundNetworkPacket& packet) override;

		bool matchesEndpoint(const UDPEndpoint& remoteEndpoint) const;
		void onReceive(gsl::span<const gsl::byte> data);
		void setError(const std::string& cs);

		void open(short connectionId);
		void onOpen(short connectionId);
		void terminateConnection();
		short getConnectionId() const { return connectionId; }

	private:
		UDPSocket& socket;
		UDPEndpoint remote;
		ConnectionStatus status;
		short connectionId;

		std::deque<OutboundNetworkPacket> pendingSend;
		std::deque<InboundNetworkPacket> pendingReceive;
		std::array<gsl::byte, 2048> sendBuffer;
		std::string error;

		void sendNext();
	};
}


#pragma once

#include <vector>

#include <gsl/gsl>
#include <atlcomcli.h>

using gsl::span;

namespace gfx {

	/*
		Defines the file formats supported by the imaging functions.
	*/
	enum class ImageFileFormat {
		BMP,
		JPEG,
		TGA,
		FNTART,
		IMG,
		Unknown
	};

	struct ImageFileInfo {
		ImageFileFormat format = ImageFileFormat::Unknown;
		int width = 0;
		int height = 0;
		bool hasAlpha = false;
	};

	/*
		Tries to detect the image format of the given data by
		inspecting the header only.
	*/
	ImageFileInfo DetectImageFormat(span<uint8_t> data);

	bool DetectTga(span<uint8_t> data, ImageFileInfo &info);

	std::unique_ptr<uint8_t[]> DecodeTga(span<uint8_t> data);

	/*
		Specifies the pixel format of the uncompressed data
		when encoding or decoding a JPEG image.
	*/
	enum class JpegPixelFormat {
		RGB,
		BGR,
		RGBX,
		BGRX,
		XBGR,
		XRGB
	};

	/*
		Encodes a JPEG image in memory and returns the compressed data.
		pitch is the size in pixels of a line of the uncompressed data.
		quality is an integer between 1 and 100.
	*/
	std::vector<uint8_t> EncodeJpeg(const uint8_t* imageData,
	                                JpegPixelFormat imageDataFormat,
	                                int width,
	                                int height,
	                                int quality,
	                                int pitch);

	struct DecodedImage {
		std::unique_ptr<uint8_t[]> data;
		ImageFileInfo info;
	};

	DecodedImage DecodeFontArt(const span<uint8_t> data);

	DecodedImage DecodeImage(const span<uint8_t> data);

	DecodedImage DecodeCombinedImage(const std::string &filename, span<uint8_t> data);

	HCURSOR LoadImageToCursor(const span<uint8_t> data, uint32_t hotspotX, uint32_t hotspotY);

}


// ---------------------------------------------------------------------------
//
//  Author  : github.com/luncliff (luncliff@gmail.com)
//  License : CC BY 4.0
//
//  Note
//      Async I/O operation support for socket
//
// ---------------------------------------------------------------------------
#pragma once
// clang-format off
#ifdef USE_STATIC_LINK_MACRO // ignore macro declaration in static build
#   define _INTERFACE_
#   define _HIDDEN_
#else
#   if defined(_MSC_VER) // MSVC
#       define _HIDDEN_
#       ifdef _WINDLL
#           define _INTERFACE_ __declspec(dllexport)
#       else
#           define _INTERFACE_ __declspec(dllimport)
#       endif
#   elif defined(__GNUC__) || defined(__clang__)
#       define _INTERFACE_ __attribute__((visibility("default")))
#       define _HIDDEN_ __attribute__((visibility("hidden")))
#   else
#       error "unexpected compiler"
#   endif // compiler check
#endif
// clang-format on

#ifndef COROUTINE_NET_IO_H
#define COROUTINE_NET_IO_H
#include <coroutine/yield.hpp>

#include <chrono>
#include <gsl/gsl>

#if defined(_MSC_VER)
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <ws2def.h>
#pragma comment(lib, "Ws2_32.lib")

using io_control_block = OVERLAPPED;

static constexpr bool is_winsock = true;
static constexpr bool is_netinet = false;
#else
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

// - Note
// follow the definition of Windows `OVERLAPPED`
struct io_control_block
{
    uint64_t internal; // uint32_t errc; int32_t flag;
    uint64_t internal_high;
    union {
        struct
        {
            int32_t offset; // socklen_t addrlen;
            int32_t offset_high;
        };
        void* ptr;
    };
    int64_t handle; // int64_t sd;
};

static constexpr bool is_winsock = false;
static constexpr bool is_netinet = true;
#endif

// - Note
//      Even though this class violates C++ Core Guidelines,
//      it will be used for simplicity
union endpoint_t final {
    sockaddr_storage storage{};
    sockaddr addr;
    sockaddr_in in4;
    sockaddr_in6 in6;
};

using io_task_t = std::experimental::coroutine_handle<void>;
using buffer_view_t = gsl::span<gsl::byte>;

struct io_work_t : public io_control_block
{
    io_task_t task{};
    buffer_view_t buffer{};
    endpoint_t* ep{};

  public:
    _INTERFACE_ bool ready() const noexcept;
    _INTERFACE_ uint32_t error() const noexcept;
};
static_assert(sizeof(buffer_view_t) <= sizeof(void*) * 2);
static_assert(sizeof(io_work_t) <= 64);

class io_send_to final : public io_work_t
{
  public:
    _INTERFACE_ void suspend(io_task_t rh) noexcept(false);
    _INTERFACE_ int64_t resume() noexcept;

  public:
    auto await_ready() const noexcept
    {
        return this->ready();
    }
    void await_suspend(io_task_t rh) noexcept(false)
    {
        return this->suspend(rh);
    }
    auto await_resume() noexcept
    {
        return this->resume();
    }
};
static_assert(sizeof(io_send_to) == sizeof(io_work_t));

[[nodiscard]] _INTERFACE_ auto
    send_to(uint64_t sd, const sockaddr_in& remote, //
            buffer_view_t buffer, io_work_t& work) noexcept(false)
        -> io_send_to&;

[[nodiscard]] _INTERFACE_ //
    auto
    send_to(uint64_t sd, const sockaddr_in6& remote, //
            buffer_view_t buffer, io_work_t& work) noexcept(false)
        -> io_send_to&;

class io_recv_from final : public io_work_t
{
  public:
    _INTERFACE_ void suspend(io_task_t rh) noexcept(false);
    _INTERFACE_ int64_t resume() noexcept;

  public:
    auto await_ready() const noexcept
    {
        return this->ready();
    }
    void await_suspend(io_task_t rh) noexcept(false)
    {
        return this->suspend(rh);
    }
    auto await_resume() noexcept
    {
        return this->resume();
    }
};
static_assert(sizeof(io_recv_from) == sizeof(io_work_t));

[[nodiscard]] _INTERFACE_ //
    auto
    recv_from(uint64_t sd, sockaddr_in6& remote, //
              buffer_view_t buffer, io_work_t& work) noexcept(false)
        -> io_recv_from&;

[[nodiscard]] _INTERFACE_ //
    auto
    recv_from(uint64_t sd, sockaddr_in& remote, //
              buffer_view_t buffer, io_work_t& work) noexcept(false)
        -> io_recv_from&;

class io_send final : public io_work_t
{
  public:
    _INTERFACE_ void suspend(io_task_t rh) noexcept(false);
    _INTERFACE_ int64_t resume() noexcept;

  public:
    auto await_ready() const noexcept
    {
        return this->ready();
    }
    void await_suspend(io_task_t rh) noexcept(false)
    {
        return this->suspend(rh);
    }
    auto await_resume() noexcept
    {
        return this->resume();
    }
};
static_assert(sizeof(io_send) == sizeof(io_work_t));

[[nodiscard]] _INTERFACE_ //
    auto
    send_stream(uint64_t sd, buffer_view_t buffer, uint32_t flag,
                io_work_t& work) noexcept(false) -> io_send&;

class io_recv final : public io_work_t
{
  public:
    _INTERFACE_ void suspend(io_task_t rh) noexcept(false);
    _INTERFACE_ int64_t resume() noexcept;

  public:
    auto await_ready() const noexcept
    {
        return this->ready();
    }
    void await_suspend(io_task_t rh) noexcept(false)
    {
        return this->suspend(rh);
    }
    auto await_resume() noexcept
    {
        return this->resume();
    }
};
static_assert(sizeof(io_recv) == sizeof(io_work_t));

[[nodiscard]] _INTERFACE_ //
    auto
    recv_stream(uint64_t sd, buffer_view_t buffer, uint32_t flag,
                io_work_t& work) noexcept(false) -> io_recv&;

// - Note
//      This function is only non-windows platform.
//      Over windows api, it always yields nothing.
//
//      User must continue looping without break so that there is no leak of
//      event. Also, the library doesn't guarantee all coroutines(i/o tasks)
//      will be fetched at once. Therefore it is strongly recommended for user
//      to have a method to detect that coroutines are returned.
_INTERFACE_
auto wait_io_tasks(std::chrono::nanoseconds timeout) noexcept(false)
    -> coro::enumerable<io_task_t>;

_INTERFACE_
auto host_name() noexcept -> gsl::czstring<NI_MAXHOST>;

_INTERFACE_
std::errc peer_name(uint64_t sd, sockaddr_in6& ep) noexcept;

_INTERFACE_
std::errc sock_name(uint64_t sd, sockaddr_in6& ep) noexcept;

_INTERFACE_
auto resolve(const addrinfo& hint, //
             gsl::czstring<NI_MAXHOST> name,
             gsl::czstring<NI_MAXSERV> serv) noexcept
    -> coro::enumerable<sockaddr_in6>;

_INTERFACE_
std::errc nameof(const sockaddr_in& ep, //
                 gsl::zstring<NI_MAXHOST> name) noexcept;

_INTERFACE_
std::errc nameof(const sockaddr_in6& ep, //
                 gsl::zstring<NI_MAXHOST> name) noexcept;

_INTERFACE_
std::errc nameof(const sockaddr_in6& ep, //
                 gsl::zstring<NI_MAXHOST> name,
                 gsl::zstring<NI_MAXSERV> serv) noexcept;

#endif // COROUTINE_NET_IO_H