/* SLV2
 * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
 *  
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#define _XOPEN_SOURCE 500
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <slv2/port.h>
#include <slv2/types.h>
#include <slv2/util.h>
#include <slv2/values.h>
#include "slv2_internal.h"


/* private */
SLV2Port
slv2_port_new(uint32_t index, const char* symbol/*, const char* node_id*/)
{
	struct _SLV2Port* port = malloc(sizeof(struct _SLV2Port));
	port->index = index;
	port->symbol = strdup(symbol);
	port->classes = slv2_values_new();
	//port->node_id = strdup(node_id);
	return port;
}


/* private */
void
slv2_port_free(SLV2Port port)
{
	free(port->symbol);
	slv2_values_free(port->classes);
	free(port);
}


/* private */
SLV2Port
slv2_port_duplicate(SLV2Port port)
{
	SLV2Port result = malloc(sizeof(struct _SLV2Port));
	result->index = port->index;
	result->symbol = strdup(port->symbol);
	return result;
}


bool
slv2_port_is_a(SLV2Plugin plugin,
               SLV2Port   port,
               SLV2Value  port_class)
{
	for (unsigned i=0; i < slv2_values_size(port->classes); ++i)
		if (slv2_value_equals(slv2_values_get_at(port->classes, i), port_class))
			return true;

	return false;
}


bool
slv2_port_has_property(SLV2Plugin  p,
                       SLV2Port    port,
                       const char* property)
{
	assert(property);

	SLV2Values result = NULL;

	char* query = slv2_strjoin(
			"SELECT DISTINCT ?port WHERE {\n"
			"<", librdf_uri_as_string(p->plugin_uri), "> lv2:port ?port ."
			"?port lv2:symbol \"", port->symbol, "\";\n",
			"      lv2:portProperty <", property, "> .\n}", NULL);
			
	result = slv2_plugin_simple_query(p, query, 0);

	const bool ret = (slv2_values_size(result) > 0);

	free(query);
	free(result);
	
	return ret;
}


bool
slv2_port_supports_event(SLV2Plugin  p,
                         SLV2Port    port,
                         const char* event)
{
	assert(event);

	char* query = slv2_strjoin(
			"ASK WHERE {\n"
			"<", librdf_uri_as_string(p->plugin_uri), "> lv2:port ?port ."
			"?port lv2:symbol \"", port->symbol, "\";\n",
			"      lv2ev:supportsEvent <", event, "> .\n"
			"}", NULL);
			
	librdf_query_results* results = slv2_plugin_query(p, query);
	assert(librdf_query_results_is_boolean(results));

	const bool ret = librdf_query_results_get_boolean(results);

	free(query);
	librdf_free_query_results(results);
	
	return ret;
}


SLV2Values
slv2_port_get_value(SLV2Plugin  p,
                    SLV2Port    port,
                    const char* property)
{
	assert(property);

	SLV2Values result = NULL;

	char* query = slv2_strjoin(
			"SELECT DISTINCT ?value WHERE {\n"
			"<", librdf_uri_as_string(p->plugin_uri), "> lv2:port ?port ."
			"?port lv2:symbol \"", port->symbol, "\";\n\t",
			       property, " ?value .\n}", NULL);
			
	result = slv2_plugin_simple_query(p, query, 0);

	free(query);
	
	return result;
}


char*
slv2_port_get_symbol(SLV2Plugin p,
                     SLV2Port   port)
{
	char* symbol = NULL;
	
	SLV2Values result = slv2_port_get_value(p, port, "lv2:symbol");

	if (result && slv2_values_size(result) == 1)
		symbol = strdup(slv2_value_as_string(slv2_values_get_at(result, 0)));
	
	slv2_values_free(result);

	return symbol;
}

	
char*
slv2_port_get_name(SLV2Plugin p,
                   SLV2Port   port)
{
	char* name = NULL;
	
	SLV2Values result = slv2_port_get_value(p, port, "lv2:name");

	if (result && slv2_values_size(result) == 1)
		name = strdup(slv2_value_as_string(slv2_values_get_at(result, 0)));
	
	slv2_values_free(result);

	return name;
}

	
SLV2Values
slv2_port_get_classes(SLV2Plugin p,
                      SLV2Port   port)
{
	return port->classes;
}


float
slv2_port_get_default_value(SLV2Plugin p, 
                            SLV2Port   port)
{
	float value = 0.0f;
	
	SLV2Values result = slv2_port_get_value(p, port, "lv2:default");

	if (result && slv2_values_size(result) == 1)
		value = slv2_value_as_float(slv2_values_get_at(result, 0));
	
	slv2_values_free(result);

	return value;
}


float
slv2_port_get_minimum_value(SLV2Plugin p, 
                            SLV2Port   port)
{
	float value = 0.0f;
	
	SLV2Values result = slv2_port_get_value(p, port, "lv2:minimum");

	if (result && slv2_values_size(result) == 1)
		value = slv2_value_as_float(slv2_values_get_at(result, 0));
	
	slv2_values_free(result);

	return value;
}


float
slv2_port_get_maximum_value(SLV2Plugin p, 
                            SLV2Port   port)
{
	float value = 0.0f;
	
	SLV2Values result = slv2_port_get_value(p, port, "lv2:maximum");

	if (result && slv2_values_size(result) == 1)
		value = slv2_value_as_float(slv2_values_get_at(result, 0));
	
	slv2_values_free(result);

	return value;
}


SLV2Values
slv2_port_get_properties(SLV2Plugin p,
                         SLV2Port   port)
{
	return slv2_port_get_value(p, port, "lv2:portProperty");
}