
// Hapy library example

// Assembler for the RenderMan's RiBlobby primitive
// Based on Edmonds syntax (http://www.dctsystems.co.uk/RenderMan/edmonds.html)
// except that space is used instead of comma as separator
// run as : blobby_asm < file.bsm

/*

   s1 = ellipsoid 1 0 0 0
   s2 = ellipsoid 1 1 0 0
   s3 = ellipsoid 1 2 0 0
   pin = ellipsoid 3 0.3 0.3   1 0 0

   beads = union s1 s2 s3
   result = sub beads pin

*/

#include <Hapy/Parser.h>
#include <Hapy/Rules.h>
#include <Hapy/PreeFarm.h>
#include <Hapy/Assert.h>
#include <Hapy/IoStream.h>

#include <iostream>
#include <map>
#include <sstream>


using namespace Hapy;

Rule integer;
Rule value; // float
Rule name;
Rule variable;

Rule ellipsoid4;
Rule ellipsoid6;
Rule ellipsoid16;
Rule segment;
Rule plane;

Rule sub_operation;
Rule div_operation;
Rule sum_operation;
Rule mul_operation;
Rule max_operation;
Rule min_operation;

Rule r_value;
Rule definition;

Rule file;

void init_parser()
{
	integer = !char_r('-') >> +digit_r;
	value = integer >> !('.' >> *digit_r >> !('e' >> integer));
	value.leaf(true);
	value.verbatim(true);

	name = quoted_r(anychar_r, '"');

	variable = alpha_r >> *alnum_r;
	variable.verbatim(true);

	ellipsoid4 = "ellipsoid" >> value >> value >> value >> value;
	ellipsoid6 = "ellipsoid" >> value >> value >> value >> value >> value >> value;
	ellipsoid16 = "ellipsoid" >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value;
	segment = "segment" >> (value >> value >> value >> value >> value >> value | value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value >> value);
	plane = name >> value >> value >> value >> value;

	sub_operation = "sub" >> variable >> variable;
	div_operation = "div" >> variable >> variable;
	sum_operation = "sum" >> +variable | "add" >> +variable;
	mul_operation = "mul" >> +variable | "product" >> +variable;
	max_operation = "max" >> +variable | "union" >> +variable;
	min_operation = "min" >> +variable | "intersection" >> +variable;

	r_value = value | ellipsoid4 | ellipsoid6 | ellipsoid16 | segment | plane | sub_operation | div_operation | sum_operation | mul_operation | max_operation | min_operation;
	definition = variable >> '=' >> r_value;

	file = *eol_r >> +(definition >> +eol_r);
}

std::string blobby_definition = "";
std::string blobby_data = "";
unsigned long blobby_data_index = 0;

unsigned long leaves = 0;

std::map<std::string, unsigned long> variables;
unsigned long variable_index = 0;


const std::string to_string(const unsigned long Value)
{
	std::ostringstream stream;
	stream << Value;

	return std::string(stream.str());
}

const std::string get_variable_id(const std::string Value)
{
	std::map<std::string, unsigned long>::iterator v = variables.find(Value);
	if(v == variables.end())
	{
		std::cerr << "Undefined variable " << Value << std::endl;
		return to_string(0);
	}

	return to_string(v->second);
}

const std::string get_n_parameters(Hapy::Pree::const_iterator Pree)
{
	Hapy::Pree::const_iterator val = Pree->begin();
	Hapy::Pree::const_iterator end = Pree->end();

	unsigned long n = 0;
	std::string vals = "";
	while(val != end)
	{
		vals += ' ' + get_variable_id(val->image());
		val++;
		n++;
	}

	return to_string(n) + vals;
}


void parse_subpree(const Hapy::Pree& Node)
{
	if(Node.begin() == Node.end())
		return;

	if(Node.rid() == definition.id())
	{
		Hapy::Pree::const_iterator def_node = Node.begin();

		std::string variable = def_node->image();
		def_node++; // =
		def_node++; // rvalue

		// Save variable index
		if(variables.find(variable) == variables.end())
			variables[variable] = variable_index++;

		def_node = def_node->begin();
		if(def_node[0].rid() == value.id())
		{
			blobby_definition += "1000 " + def_node[0].image() + "\n";

			std::cerr << "constant is unimplemented" << std::endl;
		}
		else if(def_node[0].rid() == ellipsoid4.id())
		{
			leaves++;
			blobby_definition += "1001 " + to_string(blobby_data_index) + "\n";

			// ellipsoid
			def_node = def_node->begin();

			// parameters
			def_node++;
			std::string radius = def_node->image();
			def_node++;
			std::string px = def_node->image();
			def_node++;
			std::string py = def_node->image();
			def_node++;
			std::string pz = def_node->image();

			blobby_data += ' ' + radius + " 0 0 0\n";
			blobby_data += " 0 " + radius + " 0 0\n";
			blobby_data += " 0 0 " + radius + " 0\n";
			blobby_data += ' ' + px + ' ' + py + ' ' + pz + " 1\n";
			blobby_data_index += 16;
		}
		else if(def_node[0].rid() == ellipsoid6.id())
		{
			leaves++;
			blobby_definition += "1001 " + to_string(blobby_data_index) + "\n";

			// ellipsoid
			def_node = def_node->begin();

			// parameters
			def_node++;
			std::string rx = def_node->image();
			def_node++;
			std::string ry = def_node->image();
			def_node++;
			std::string rz = def_node->image();
			def_node++;
			std::string px = def_node->image();
			def_node++;
			std::string py = def_node->image();
			def_node++;
			std::string pz = def_node->image();

			blobby_data += ' ' + rx + " 0 0 0\n";
			blobby_data += " 0 " + ry + " 0 0\n";
			blobby_data += " 0 0 " + rz + " 0\n";
			blobby_data += ' ' + px + ' ' + py + ' ' + pz + " 1\n";
			blobby_data_index += 16;
		}
		else if(def_node[0].rid() == segment.id())
		{
			leaves++;
			blobby_definition += "1002 ";

			std::cerr << "segment is unimplemented" << std::endl;
		}
		else if(def_node[0].rid() == plane.id())
		{
			leaves++;
			blobby_definition += "1003 ";

			std::cerr << "plane is unimplemented" << std::endl;
		}

		else

		if(def_node[0].rid() == sub_operation.id())
		{
			blobby_definition += "4 ";

			// operation
			def_node = def_node->begin();

			// parameters
			def_node++;
			std::string subtrahend = def_node->image();
			def_node++;
			std::string minuend = def_node->image();

			blobby_definition += get_variable_id(subtrahend);
			blobby_definition += ' ';
			blobby_definition += get_variable_id(minuend);
			blobby_definition += "\n";
		}
		else if(def_node[0].rid() == div_operation.id())
		{
			blobby_definition += "5 ";

			// operation
			def_node = def_node->begin();

			// parameters
			def_node++;
			std::string subtrahend = def_node->image();
			def_node++;
			std::string minuend = def_node->image();

			blobby_definition += get_variable_id(subtrahend);
			blobby_definition += ' ';
			blobby_definition += get_variable_id(minuend);
			blobby_definition += "\n";
		}
		else if(def_node[0].rid() == sum_operation.id())
		{
			blobby_definition += "0 ";

			// alternative
			def_node = def_node->begin();

			// operation
			def_node = def_node->begin();

			// parameters
			def_node++;
			blobby_definition += get_n_parameters(def_node) + "\n";
		}
		else if(def_node[0].rid() == mul_operation.id())
		{
			blobby_definition += "1 ";

			// alternative
			def_node = def_node->begin();

			// operation
			def_node = def_node->begin();

			// parameters
			def_node++;
			blobby_definition += get_n_parameters(def_node) + "\n";
		}
		else if(def_node[0].rid() == max_operation.id())
		{
			blobby_definition += "2 ";

			// alternative
			def_node = def_node->begin();

			// operation
			def_node = def_node->begin();

			// parameters
			def_node++;
			blobby_definition += get_n_parameters(def_node) + "\n";
		}
		else if(def_node[0].rid() == min_operation.id())
		{
			blobby_definition += "3 ";

			// alternative
			def_node = def_node->begin();

			// operation
			def_node = def_node->begin();

			// parameters
			def_node++;
			blobby_definition += get_n_parameters(def_node) + "\n";
		}
	}
	else
	{
		Hapy::Pree::const_iterator pi = Node.begin();
		for(; pi != Node.end(); pi++)
			parse_subpree(*pi);
	}
}


int main()
{
	init_parser();

	std::string buffer;
	std::copy(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>(), std::back_inserter(buffer));

	file.trim(*(space_r - eol_r));

	Hapy::Parser parser;
	parser.grammar(file);

	if(!parser.parse(buffer))
	{
		std::cerr << parser.result().location() << " -> parsing failed" << std::endl;
		exit(2);
	}

	Hapy::Debug(true);

	// Process parse tree ...
	const Hapy::Pree& main_node = parser.result().pree;
	parse_subpree(main_node);

	// Output ...

	std::cout << "Blobby " << leaves << std::endl;
	std::cout << "[" << std::endl;
	std::cout << blobby_definition << std::endl;
	std::cout << "]" << std::endl;
	std::cout << "[" << std::endl;
	std::cout << blobby_data << std::endl;
	std::cout << "]" << std::endl;
	std::cout << "[\"\"]" << std::endl;

	return 0;
}


