The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "uri.h"
#include "ccv.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

static void uri_dpm_on_model_string(void* context, char* string);
static void uri_dpm_on_source_blob(void* context, ebb_buf data);

static const param_dispatch_t param_map[] = {
	{
		.property = "interval",
		.type = PARAM_TYPE_INT,
		.offset = offsetof(ccv_dpm_param_t, interval),
	},
	{
		.property = "min_neighbors",
		.type = PARAM_TYPE_INT,
		.offset = offsetof(ccv_dpm_param_t, min_neighbors),
	},
	{
		.property = "model",
		.type = PARAM_TYPE_STRING,
		.on_string = uri_dpm_on_model_string,
		.offset = 0,
	},
	{
		.property = "source",
		.type = PARAM_TYPE_BODY,
		.on_blob = uri_dpm_on_source_blob,
		.offset = 0,
	},
	{
		.property = "threshold",
		.type = PARAM_TYPE_FLOAT,
		.offset = offsetof(ccv_dpm_param_t, threshold),
	},
};

typedef struct {
	ebb_buf desc;
	ccv_dpm_mixture_model_t* pedestrian;
	ccv_dpm_mixture_model_t* car;
} dpm_context_t;

typedef struct {
	param_parser_t param_parser;
	dpm_context_t* context;
	ccv_dpm_param_t params;
	ccv_dpm_mixture_model_t* mixture_model;
	ebb_buf source;
} dpm_param_parser_t;

static void uri_dpm_param_parser_init(dpm_param_parser_t* parser)
{
	param_parser_init(&parser->param_parser, param_map, sizeof(param_map) / sizeof(param_dispatch_t), &parser->params, parser);
	parser->params = ccv_dpm_default_params;
	parser->mixture_model = 0;
	parser->source.data = 0;
}

static void uri_dpm_on_model_string(void* context, char* string)
{
	dpm_param_parser_t* parser = (dpm_param_parser_t*)context;
	if (strcmp(string, "pedestrian") == 0)
		parser->mixture_model = parser->context->pedestrian;
	else if (strcmp(string, "car") == 0)
		parser->mixture_model = parser->context->car;
}

static void uri_dpm_on_source_blob(void* context, ebb_buf data)
{
	dpm_param_parser_t* parser = (dpm_param_parser_t*)context;
	parser->source = data;
}

void* uri_dpm_detect_objects_parse(const void* context, void* parsed, int resource_id, const char* buf, size_t len, uri_parse_state_t state, int header_index)
{
	dpm_param_parser_t* parser;
	if (parsed)
		parser = (dpm_param_parser_t*)parsed;
	else {
		parser = (dpm_param_parser_t*)malloc(sizeof(dpm_param_parser_t));
		uri_dpm_param_parser_init(parser);
		parser->context = (dpm_context_t*)context;
	}
	switch (state)
	{
		case URI_QUERY_STRING:
		case URI_CONTENT_BODY:
		case URI_PARSE_TERMINATE:
		case URI_MULTIPART_HEADER_FIELD:
		case URI_MULTIPART_HEADER_VALUE:
		case URI_MULTIPART_DATA:
			param_parser_execute(&parser->param_parser, resource_id, buf, len, state, header_index);
			break;
	}
	return parser;
}

void* uri_dpm_detect_objects_init(void)
{
	dpm_context_t* context = (dpm_context_t*)malloc(sizeof(dpm_context_t));
	context->pedestrian = ccv_load_dpm_mixture_model("../samples/pedestrian.m");
	context->car = ccv_load_dpm_mixture_model("../samples/car.m");
	assert(context->pedestrian && context->car);
	assert(param_parser_map_alphabet(param_map, sizeof(param_map) / sizeof(param_dispatch_t)) == 0);
	context->desc = param_parser_map_http_body(param_map, sizeof(param_map) / sizeof(param_dispatch_t),
		"[{"
			"\"x\":\"integer\","
			"\"y\":\"integer\","
			"\"width\":\"integer\","
			"\"height\":\"integer\","
			"\"confidence\":\"number\","
			"\"parts\":[{"
				"\"x\":\"integer\","
				"\"y\":\"integer\","
				"\"width\":\"integer\","
				"\"height\":\"integer\","
				"\"confidence\":\"number\""
			"}]"
		"}]");
	return context;
}

void uri_dpm_detect_objects_destroy(void* context)
{
	dpm_context_t* dpm_context = (dpm_context_t*)context;
	ccv_dpm_mixture_model_free(dpm_context->pedestrian);
	ccv_dpm_mixture_model_free(dpm_context->car);
	free(dpm_context->desc.data);
	free(dpm_context);
}

int uri_dpm_detect_objects_intro(const void* context, const void* parsed, ebb_buf* buf)
{
	dpm_context_t* dpm_context = (dpm_context_t*)context;
	buf->data = dpm_context->desc.data;
	buf->len = dpm_context->desc.len;
	return 0;
}

int uri_dpm_detect_objects(const void* context, const void* parsed, ebb_buf* buf)
{
	if (!parsed)
		return -1;
	dpm_param_parser_t* parser = (dpm_param_parser_t*)parsed;
	param_parser_terminate(&parser->param_parser);
	if (parser->source.data == 0)
	{
		free(parser);
		return -1;
	}
	if (parser->mixture_model == 0)
	{
		free(parser->source.data);
		free(parser);
		return -1;
	}
	ccv_dense_matrix_t* image = 0;
	ccv_read(parser->source.data, &image, CCV_IO_ANY_STREAM | CCV_IO_GRAY, parser->source.written);
	free(parser->source.data);
	if (image == 0)
	{
		free(parser);
		return -1;
	}
	ccv_array_t* seq = ccv_dpm_detect_objects(image, &parser->mixture_model, 1, parser->params);
	ccv_matrix_free(image);
	if (seq  == 0)
	{
		free(parser);
		return -1;
	}
	if (seq->rnum > 0)
	{
		int i, j;
		buf->len = 192 + seq->rnum * 131 + 2;
		char* data = (char*)malloc(buf->len);
		data[0] = '[';
		buf->written = 1;
		for (i = 0; i < seq->rnum; i++)
		{
			char cell[128];
			ccv_root_comp_t* comp = (ccv_root_comp_t*)ccv_array_get(seq, i);
			snprintf(cell, 128, "{\"x\":%d,\"y\":%d,\"width\":%d,\"height\":%d,\"confidence\":%f,\"parts\":[", comp->rect.x, comp->rect.y, comp->rect.width, comp->rect.height, comp->confidence);
			size_t len = strnlen(cell, 128);
			while (buf->written + len >= buf->len)
			{
				buf->len = (buf->len * 3 + 1) / 2;
				data = (char*)realloc(data, buf->len);
			}
			memcpy(data + buf->written, cell, len);
			buf->written += len;
			for (j = 0; j < comp->pnum; j++)
			{
				snprintf(cell, 128, "{\"x\":%d,\"y\":%d,\"width\":%d,\"height\":%d,\"confidence\":%f}", comp->part[j].rect.x, comp->part[j].rect.y, comp->part[j].rect.width, comp->part[j].rect.height, comp->part[j].confidence);
				len = strnlen(cell, 128);
				while (buf->written + len + 3 >= buf->len)
				{
					buf->len = (buf->len * 3 + 1) / 2;
					data = (char*)realloc(data, buf->len);
				}
				memcpy(data + buf->written, cell, len);
				buf->written += len + 1;
				data[buf->written - 1] = (j == comp->pnum - 1) ? ']' : ',';
			}
			buf->written += 2;
			data[buf->written - 2] = '}';
			data[buf->written - 1] = (i == seq->rnum - 1) ? ']' : ',';
		}
		char http_header[192];
		snprintf(http_header, 192, ebb_http_header, buf->written);
		size_t len = strnlen(http_header, 192);
		if (buf->written + len + 1 >= buf->len)
		{
			buf->len = buf->written + len + 1;
			data = (char*)realloc(data, buf->len);
		}
		memmove(data + len, data, buf->written);
		memcpy(data, http_header, len);
		buf->written += len + 1;
		data[buf->written - 1] = '\n';
		buf->data = data;
		buf->len = buf->written;
		buf->on_release = uri_ebb_buf_free;
	} else {
		buf->data = (void*)ebb_http_empty_array;
		buf->len = sizeof(ebb_http_empty_array);
		buf->on_release = 0;
	}
	ccv_array_free(seq);
	free(parser);
	return 0;
}