#include "psmod.h"
#include "icsg.h"

/*
 * Generic destructor for most of geomobjects.
 */
V_LOCAL void
generic_destroy(void_p self)
{
	memFree(self);
}

/*
 * Generic constructor of geomobject.
 */
#define	NEW_GEOMOBJ(suffix) \
	suffix = (csg_##suffix##_p)memAllocZ(sizeof(csg_##suffix##_t)); \
	suffix->base.check = ##suffix##_check; \
	suffix->base.destroy = generic_destroy; \
	suffix->base.dump = ##suffix##_dump;

/*
 * Planar constrain
 */
typedef struct csg_plane_s
{
	csg_geomobject_t base;
	vec3_t	normal; /* unit normal vector */
	vec_t	dist; /* normal dot r(x,y,z) = dist */
}	csg_plane_t, *csg_plane_p;

V_LOCAL void
print_quats(vhandle_t out,vec3_t normal)
{
	vec_t theta, phi;
	vec3Dir2Angles(normal,&theta,&phi);
	filePrintf(out,"rotation = (quat %g z_axis) * (quat %g y_axis)",
		phi * RAD_TO_ANGLE, theta * RAD_TO_ANGLE);
}

V_LOCAL void
plane_dump(void_p self,vhandle_t out,vboolean dsmax_format)
{
	csg_plane_p plane = (csg_plane_p)self;
	const vec_t plane_outsize = 100;
	vec3_t origin;
	if(dsmax_format)
	{
		filePrintf(out,
			"obj = plane backfacecull:false pos:[0,0,0] width:%g length:%g; obj.",
			plane_outsize, plane_outsize);
		print_quats(out,plane->normal);
		vec3Scale(origin,plane->normal,plane->dist);
		filePrintf(out,"; obj.pos = [%g,%g,%g]\n",origin[0],origin[1],origin[2]);
	}
	else
	{
		filePrintf(out,"plane [%g,%g,%g] %g\n",
			plane->normal[0],plane->normal[1],plane->normal[2],
			plane->dist);
	}
}

V_LOCAL vboolean
plane_check(void_p self,vec3_t pos,vec_t radius,vboolean inside)
{
	csg_plane_p plane = (csg_plane_p)self;
	vec_t delta = vec3Dot(pos,plane->normal) - plane->dist;
	if(inside) /* 'pos' should be in positive halfspace */
		return (delta > radius) ? true : false;
	return (delta < -radius) ? true : false;
}

V_LOCAL csg_geomobject_p
parse_plane(vhandle_t script)
{
	CALLER_NAME(parse_plane)
	csg_plane_p plane;
	vec3_t normal;
	vec_t nlen, dist;
	if(!icParseVec3Table(normal,sizeof(vec3_t),1,NULL,script))
	{
		SYN_EXPECTED(script,"const vec3 table[1] normal");
		return NULL;
	}
	nlen = vec3Abs(normal);
	if(IsZero(nlen))
	{
		GENERIC_PARSE_ERROR(script,"degenerate normal");
		return NULL;
	}
	vec3Scale(normal,normal,1 / nlen);
	if(!lexNextFloat(script,&dist))
	{
		SYN_EXPECTED(script,"float dist");
		return NULL;
	}
	NEW_GEOMOBJ(plane);
	vec3Copy(plane->normal,normal);
	plane->dist = dist;
	return &plane->base;
}

/*
 * Box constrain
 */
typedef struct csg_box_s
{
	csg_geomobject_t base;
	vec3_t	mins, maxs;
}	csg_box_t, *csg_box_p;

V_LOCAL void
box_dump(void_p self,vhandle_t out,vboolean dsmax_format)
{
	csg_box_p box = (csg_box_p)self;
	vec3_t size;
	vec2_t center;
	const vec_t base_segs = 4;
	int i, segs[3];
	vec_t seg_scale;
	if(!dsmax_format)
	{
		filePrintf(out,"box [%g,%g,%g;%g,%g,%g]\n",
			box->mins[0],box->mins[1],box->mins[2],
			box->maxs[0],box->maxs[1],box->maxs[2]);
		return;
	}
	vec3Sub(size,box->maxs,box->mins);
	vec2Add(center,box->maxs,box->mins);
	seg_scale = base_segs / size[vec3MinInd(size)];
	for(i = 0;i < 3;i++)
		segs[i] = (int)(size[i] * seg_scale);
	filePrintf(out,
		"box backfacecull:false pos:[%g,%g,%g] "
		"width:%g length:%g height:%g "
		" widthsegs:%d lengthsegs:%d heightsegs:%d\n",
		center[0], center[1], box->mins[2],
		size[0], size[1], size[2],
		segs[0], segs[1], segs[2]);
}

V_LOCAL vboolean
box_check(void_p self,vec3_t pos,vec_t radius,vboolean inside)
{
	int i;
	vec3_t delta, real_mins, real_maxs;
	csg_box_p box = (csg_box_p)self;
	vec3Set(delta,radius,radius,radius);
	if(inside)
	{
		/* Shrink the bounds */
		vec3Add(real_mins,box->mins,delta);
		vec3Sub(real_maxs,box->maxs,delta);
		for(i = 0;i < 3;i++)
			if(real_mins[i] >= pos[i] || real_maxs[i] <= pos[i])
				return false;
		return true;
	}
	/* Expand the bounds */
	vec3Sub(real_mins,box->mins,delta);
	vec3Add(real_maxs,box->maxs,delta);
	for(i = 0;i < 3;i++)
		if(real_mins[i] <= pos[i] && real_maxs[i] >= pos[i])
			return false;
	return false;
}

V_LOCAL csg_geomobject_p
parse_box(vhandle_t script)
{
	CALLER_NAME(parse_box)
	csg_box_p box;
	int i;
	vec_t minmax[6];
	if(!icParseVec3Table(minmax,sizeof(vec3_t),2,NULL,script))
	{
		SYN_EXPECTED(script,"const vec3 table[2] min;max");
		return NULL;
	}
	for(i = 0;i < 3;i++)
	{
		if(minmax[i] >= minmax[3+i])
		{
			GENERIC_PARSE_ERROR(script,"degenerate box");
			return NULL;
		}
	}
	NEW_GEOMOBJ(box);
	vec3Copy(box->mins,minmax);
	vec3Copy(box->maxs,&minmax[3]);
	return &box->base;
}

/*
 * Spherical constrain
 */
typedef struct csg_sphere_s
{
	csg_geomobject_t base;
	vec3_t	center;
	vec_t	radius;
}	csg_sphere_t, *csg_sphere_p;

V_LOCAL void
sphere_dump(void_p self,vhandle_t out,vboolean dsmax_format)
{
	csg_sphere_p sphere = (csg_sphere_p)self;
	if(dsmax_format)
	{
		filePrintf(out,
			"geosphere backfacecull:false segs:5 pos:[%g,%g,%g] radius:%g\n",
			sphere->center[0], sphere->center[1], sphere->center[2],
			sphere->radius);
	}
	else
	{
		filePrintf(out,"sphere [%g,%g,%g] %g\n",
			sphere->center[0],sphere->center[1],sphere->center[2],
			sphere->radius);
	}
}

V_LOCAL vboolean
sphere_check(void_p self,vec3_t pos,vec_t radius,vboolean inside)
{
	csg_sphere_p sphere = (csg_sphere_p)self;
	vec3_t delta;
	vec_t dist;
	vec3Sub(delta,sphere->center,pos);
	dist = vec3Abs(delta);
	if(inside)
		return ((dist + radius) < sphere->radius) ? true : false;
	return ((dist - radius) > sphere->radius) ? true : false;
}

V_LOCAL csg_geomobject_p
parse_sphere(vhandle_t script)
{
	CALLER_NAME(parse_sphere)
	csg_sphere_p sphere;
	vec3_t center;
	vec_t radius;
	if(!icParseVec3Table(center,sizeof(vec3_t),1,NULL,script))
	{
		SYN_EXPECTED(script,"const vec3 table[1] center");
		return NULL;
	}
	if(!lexNextFloat(script,&radius))
	{
		SYN_EXPECTED(script,"float radius");
		return NULL;
	}
	if(radius <= 0)
	{
		GENERIC_PARSE_ERROR(script,"degenerate radius");
		return NULL;
	}
	NEW_GEOMOBJ(sphere);
	vec3Copy(sphere->center,center);
	sphere->radius = radius;
	return &sphere->base;
}

/*
 * Cylindrical constrain
 */
typedef struct csg_cylinder_s
{
	csg_geomobject_t base;
	vec3_t	center; /* center of the lower base */
	vec3_t	dir; /* unit direction from center of lower base to upper one */
	vec_t	radius;
	vec_t	height;
}	csg_cylinder_t, *csg_cylinder_p;

V_LOCAL void
cylinder_dump(void_p self,vhandle_t out,vboolean dsmax_format)
{
	csg_cylinder_p cylinder = (csg_cylinder_p)self;
	if(dsmax_format)
	{
		filePrintf(out,
			"obj = cylinder backfacecull:false heightsegs:4 capsegs:4 "
			"pos:[0,0,0] radius:%g height:%g; obj.",
			cylinder->radius, cylinder->height);
		print_quats(out,cylinder->dir);
		filePrintf(out,"; obj.pos = [%g,%g,%g]\n",cylinder->center[0],
			cylinder->center[1],cylinder->center[2]);
	}
	else
	{
		filePrintf(out,"cylinder [%g,%g,%g;%g,%g,%g] [%g;%g]\n",
			cylinder->center[0],cylinder->center[1],cylinder->center[2],
			cylinder->dir[0],cylinder->dir[1],cylinder->dir[2],
			cylinder->radius,cylinder->height);
	}
}

V_LOCAL vboolean
cylinder_check(void_p self,vec3_t pos,vec_t radius,vboolean inside)
{
	csg_cylinder_p cylinder = (csg_cylinder_p)self;
	vec_t dist, axial_dist;
	vec3_t delta, axial;
	/* compute projection of (pos - cylinder->center) on cylinder->dir */
	vec3Sub(delta,pos,cylinder->center);
	dist = vec3Dot(cylinder->dir,delta);
	if(inside)
	{
		/* check if 'pos' is between lower and upper planes */
		if(dist <= radius || dist >= (cylinder->height - radius) )
			return false;
	}
	else
	{
		/* check if 'pos' is outside of lower and upper planes */
		if(dist < -radius || dist > (cylinder->height + radius))
			return true;
	}
	/* compute distance of 'pos' from axis */
	vec3Scale(axial,cylinder->dir,dist);
	vec3Sub(axial,delta,axial);
	axial_dist = vec3Abs(axial);
	if(inside)
		return (axial_dist < (cylinder->radius - radius)) ? true : false;
	return (axial_dist > (cylinder->radius - radius)) ? true : false;
}

V_LOCAL csg_geomobject_p
parse_cylinder(vhandle_t script)
{
	CALLER_NAME(parse_cylinder)
	csg_cylinder_p cylinder;
	vec_t	center_dir[6];
	vec_t	radius_height[2], dlen;
	if(!icParseVec3Table(center_dir,sizeof(vec3_t),2,NULL,script))
	{
		SYN_EXPECTED(script,"const vec3 table[2] lower_center;axis_dir");
		return NULL;
	}
	dlen = vec3Abs(&center_dir[3]);
	if(IsZero(dlen))
	{
		GENERIC_PARSE_ERROR(script,"degenerate direction vector");
		return NULL;
	}
	if(!icParseFloatTable(radius_height,sizeof(vec_t),2,NULL,script))
	{
		SYN_EXPECTED(script,"const float table[2] radius;height");
		return NULL;
	}
	if(radius_height[0] <= 0)
	{
		GENERIC_PARSE_ERROR(script,"degenerate radius");
		return NULL;
	}
	if(radius_height[1] <= 0)
	{
		GENERIC_PARSE_ERROR(script,"degenerate height");
		return NULL;
	}
	NEW_GEOMOBJ(cylinder);
	vec3Copy(cylinder->center,center_dir);
	vec3Scale(cylinder->dir,&center_dir[3],1 / dlen);
	cylinder->radius = radius_height[0];
	cylinder->height = radius_height[1];
	return &cylinder->base;
}

#define	GEOMOBJ(name,desc) {#name,parse_##name,desc}

/*
 * Known geomobjects
 */
V_LOCAL geomobject_parse_entry_t csg_geomobjects[] =
{
	GEOMOBJ(plane,"plane vec3[1] float (planar constrain)"),
	GEOMOBJ(box,"box vec3[2] (box constrain)"),
	GEOMOBJ(sphere,"sphere vec3[1] float (spherical constrain)"),
	GEOMOBJ(cylinder,"cylinder vec3[2] float[2] (cylindrical constrain)"),
	{NULL,NULL,NULL}
};

#undef	GEOMOBJ

V_LOCAL void
csg_geomobjects_help(void)
{
	int i;
	conPrintf("Known geomobjects:\n");
	for(i=0;csg_geomobjects[i].name;i++)
		conPrintf("\t\"%s\": %s.\n",csg_geomobjects[i].name,
			csg_geomobjects[i].description);
}

/*
 * Factory function for all descendants; it determines the type
 * of geomobject and than calls appropriate constructor.
 */
V_LOCAL csg_geomobject_p
csg_parse_geomobject(vhandle_t script)
{
	CALLER_NAME(csg_parse_geomobject)
	int i;
	char *token;
	/* Assume we called after csg_get_operand_type() */
	token = lexLast(script); /* can't be NULL */
	for(i=0;csg_geomobjects[i].name;i++)
		if(STR_EQ(csg_geomobjects[i].name,token))
			return csg_geomobjects[i].parse(script);
	PARSE_ERROR(script,"unknown geomobject");
	return NULL;
}

#define	ICSG_OP(name,desc)	{csg_##name,#name,desc}

/*
 * Known CSG operations
 */
V_LOCAL csg_operation_parse_entry_t csg_ops[] =
{
	ICSG_OP(union,"union of two objects"),
	ICSG_OP(intersection,"intersection of two objects"),
	ICSG_OP(subtraction,"subtraction of object #2 from object #1"),
	{csg_max_operation,NULL,NULL}
};

#undef	ICSG_OP

/*
 * Reads next token from 'script', stores operation type in op_ret.
 */
V_LOCAL vboolean
csg_get_operand_type(vhandle_t script,csg_operation_t *op_ret)
{
	int i;
	char *token = lexNext(script);
	if(!token)
		return false;
	for(i=0;csg_ops[i].name;i++)
		if(STR_EQ(csg_ops[i].name,token))
		{
			*op_ret = csg_ops[i].operation;
			return true; /* operation */
		}
	*op_ret = csg_geomobject; /* constrain */
	return true;
}

V_LOCAL void
csg_operation_help(void)
{
	int i;
	conPrintf("Known CSG operations:\n");
	for(i=0;csg_ops[i].name;i++)
		conPrintf("\t\"%s\": %s.\n",csg_ops[i].name,csg_ops[i].description);
}

V_LOCAL void V_CALL csg_node_destroy(void *data)
{
	csg_node_info_p node_info = (csg_node_info_p)data;
	if(node_info && node_info->geomobject)
		node_info->geomobject->destroy(node_info->geomobject);
}

V_LOCAL btree_p
icsgParseOperand(btree_p root_node,int child_index,vhandle_t script)
{
	CALLER_NAME(icsgParseOperand)
	btree_p node = NULL;
	csg_node_info_t node_info;
	if(!csg_get_operand_type(script,&node_info.op))
	{
		SYN_UNEXPECTED_EOF(script);
		return NULL;
	}
	if(node_info.op == csg_geomobject)
	{
		/*
		 * Single constrain becomes a leaf.
		 */
		node_info.geomobject = csg_parse_geomobject(script);
		if(node_info.geomobject)
			return btStore(&node_info,sizeof(csg_node_info_t),root_node,child_index);
		return NULL; /* unknown geomobject: probably syntax error */
	}
	/*
	 * Binary CSG operation becomes a node.
	 * Parse both child nodes recursively.
	 */
	node_info.geomobject = NULL;
	node = btStore(&node_info,sizeof(csg_node_info_t),root_node,child_index);
	if(lexNextIs(script,"{"))
	{
		if(icsgParseOperand(node,0,script))
		{
			if(icsgParseOperand(node,1,script))
			{
				if(lexNextIs(script,"}"))
					return node;
				SYN_EXPECTED(script,"}");
				btDeleteAllNodes(node->children[1],csg_node_destroy);
			}
			btDeleteAllNodes(node->children[0],csg_node_destroy);
		}
	}
	else
		SYN_EXPECTED(script,"{");
	memFree(node);
	return NULL;
}

static void
icsgHelp(void)
{
	csg_operation_help();
	csg_geomobjects_help();
}

/*
 * icsgCheckAgainstNode()
 * Returns 'true' if 'pos' is 'inside' as deep as 'radius'.
 */
V_LOCAL vboolean
icsgCheckAgainstNode(btree_p root,
	vboolean inside,
	vec3_t pos,
	vec_t radius)
{
	CALLER_NAME(icsgCheckAgainstNode) /* for debug macros */
	csg_node_info_p ni;
	/*
	 * Test always passes if no constrains defined.
	 */
	if(!root)
		return true;
	ni = (csg_node_info_p)(root->data);
	switch(ni->op)
	{
		case csg_geomobject:
			/*
			 * This is a single constrain (leaf of binary tree).
			 */
			return ni->geomobject->check(ni->geomobject,pos,radius,inside);
		case csg_union:
			if(inside)
				/*
				 * Check if 'pos' is inside of a union.
				 * Must be inside of at least one node.
				 */
				return icsgCheckAgainstNode(root->children[0],true,pos,radius) ||
					icsgCheckAgainstNode(root->children[1],true,pos,radius);
			/*
			 * Check if 'pos' is outside of a union.
			 * Must be outside of both nodes.
			 */
			return icsgCheckAgainstNode(root->children[0],false,pos,radius) &&
				icsgCheckAgainstNode(root->children[1],false,pos,radius);
		case csg_intersection:
			if(inside)
				/*
				 * Check if 'pos' is inside of an intersection.
				 * Must be inside of both nodes.
				 */
				return icsgCheckAgainstNode(root->children[0],true,pos,radius) &&
					icsgCheckAgainstNode(root->children[1],true,pos,radius);
			/*
			 * Check if 'pos' is outside of an intersection.
			 * Must be outside of at least one node.
			 */
			return icsgCheckAgainstNode(root->children[0],false,pos,radius) ||
				icsgCheckAgainstNode(root->children[1],false,pos,radius);
		case csg_subtraction:
			if(inside)
				/*
				 * Check if 'pos' is inside of a subtraction.
				 * Must be inside of node[0] and outside of node[1].
				 */
				return icsgCheckAgainstNode(root->children[0],true,pos,radius) &&
					icsgCheckAgainstNode(root->children[1],false,pos,radius);
			/*
			 * Check if 'pos' is outside of a subtraction.
			 * Must be outside of node[0] or inside of node[1].
			 */
			return icsgCheckAgainstNode(root->children[0],false,pos,radius) ||
				icsgCheckAgainstNode(root->children[1],true,pos,radius);
		default:
			ERROR_GENERIC("Unknown CSG operation: script parser is broken");
			break;
	}
	return false;
}

/* DEBUG */

V_LOCAL void
indent(vhandle_t out,int level)
{
	char *str;
	if(!level)
		return;
	str = (char*)alloca(level);
	memset(str,32,level);
	fileWrite(out,str,level);
}

/*
 * Saves CSG tree to 'out' in source form, so it can be parsed again
 * later by icsgParseOperand().
 */
static void
icsgDumpTree(vhandle_t out,btree_p root,int level)
{
	char *op_name = NULL;
	csg_node_info_p ni;
	if(!root)
		return;
	ni = (csg_node_info_p)(root->data);
	if(ni->geomobject)
	{
		indent(out,level);
		ni->geomobject->dump(ni->geomobject,out,false);
		return;
	}
	switch(ni->op)
	{
		case csg_union:
			op_name = "union";
			break;
		case csg_intersection:
			op_name = "intersection";
			break;
		case csg_subtraction:
			op_name = "subtraction";
			break;
		default:
			assert(0);
			return;
	}
	indent(out,level);
	filePrintf(out,"%s\n",op_name);
	indent(out,level);
	filePrintf(out,"{\n");
	icsgDumpTree(out,root->children[0],level + 4);
	icsgDumpTree(out,root->children[1],level + 4);
	indent(out,level);
	filePrintf(out,"}\n");
}

/*
 * Saves all CSG constrains to 3DS MAX script.
 */
static void
icsgDumpObjects(vhandle_t out,btree_p root)
{
	csg_node_info_p ni;
	if(!root)
		return;
	ni = (csg_node_info_p)(root->data);
	if(ni->geomobject)
	{
		ni->geomobject->dump(ni->geomobject,out,true);
		return;
	}
	icsgDumpObjects(out,root->children[0]);
	icsgDumpObjects(out,root->children[1]);
}

static void dump(btree_p root)
{
	vhandle_t out = fileOpenWrite("CON");
	icsgDumpTree(out,root,0);
	fileClose(out);
}

static void dump_objs(btree_p root)
{
	vhandle_t out = fileOpenWrite("objects.ms");
	icsgDumpObjects(out,root);
	fileClose(out);
}

static void test(btree_p root,vhandle_t script)
{
	while(lexNext(script))
	{
		int i;
		vec3_t v;
		vec_t r;
		for(i = 0;i < 3;i++)
		{
			assert(lexNextFloat(script,&v[i]));
			assert(lexNext(script));
		}
		assert(lexNextFloat(script,&r));
		printf("pos [%g,%g,%g], radius %g: %s\n",v[0],v[1],v[2],r,
			icsgCheckAgainstNode(root,true,v,r) ? "passed" : "failed");
	}
}

static void prints(vhandle_t out,vec3_t pos,vec_t radius)
{
	filePrintf(out,"geosphere segs:2 pos:[%g,%g,%g] radius:%g\n",
		pos[0],pos[1],pos[2],radius);
}

static void _distrib(btree_p root)
{
#define L	15
#define M	15
#define N	15
	vhandle_t out;
	vec3_t mins = {-100,-100,-100};
	vec3_t maxs = {100,100,100};
	vec3_t d, pos;
	vec_t r;
	int i, j, k;
	vec3Sub(d,maxs,mins);
	d[0] /= L;
	d[1] /= M;
	d[2] /= N;
	r = (d[0] < d[1]) ? (d[0] < d[2] ? d[0] : d[2]) : (d[1] < d[2] ? d[1] : d[2]);
	r *= 0.5 * 0.7;
	out = fileOpenWrite("spheres.ms");
	pos[0] = mins[0] + 0.5 * d[0];
	for(i = 0;i < L;i++)
	{
		pos[1] = mins[1] + 0.5 * d[1];
		for(j = 0;j < M;j++)
		{
			pos[2] = mins[2] + 0.5 * d[2];
			for(k = 0;k < N;k++)
			{
				if(icsgCheckAgainstNode(root,true,pos,r))
					prints(out,pos,r);
				pos[2] += d[2];
			}
			pos[1] += d[1];
		}
		pos[0] += d[0];
	}
	fileClose(out);
}

static void distrib(btree_p root)
{
	vhandle_t out, rg;
	vec3_t mins = {-100,-100,-100};
	vec3_t maxs = {100,100,100};
	vec3_t pos;
	vec_t r;
	int i, n;
	if(getenv("NUM_PARTICLES"))
		n = atoi(getenv("NUM_PARTICLES"));
	else
		n = 500;
	rg = ranCreateGenerator(1);
	out = fileOpenWrite("spheres.ms");
	for(i = 0;i < n;)
	{
		ranNextVec3RangeV(rg,pos,mins,maxs);
		r = ranNextFloatRange(rg,1,5);
		if(icsgCheckAgainstNode(root,true,pos,r))
		{
			prints(out,pos,r);
			i++;
		}
	}
	fileClose(out);
}

static void distribg(btree_p root)
{
	vhandle_t out, rg;
	vec3_t mins = {-100,-100,-100};
	vec3_t maxs = {100,100,100};
	vec3_t pos;
	vec_t r;
	int i, n;
	if(getenv("NUM_PARTICLES"))
		n = atoi(getenv("NUM_PARTICLES"));
	else
		n = 500;
	rg = ranCreateGenerator(1);
	out = fileOpenWrite("spheres.ms");
	for(i = 0;i < n;)
	{
		pos[0] = ranNextFloatRange(rg,-100,100);
		pos[1] = ranNextFloatRange(rg,-100,100);
		pos[2] = ranNextGaussian(rg,-100,60);
		r = ranNextFloatRange(rg,1,5);
		if(icsgCheckAgainstNode(root,true,pos,r))
		{
			prints(out,pos,r);
			i++;
		}
	}
	fileClose(out);
}

void icsg_main(void)
{
	vhandle_t lexer;
	btree_p root;
	
	lexer = lexParseFile("objects.psdl","{}[]();,");
	lexSetInteger(lexer,LEX_COMMENT_CHAR,(u8_t)'#');
	lexEnable(lexer,LEX_ONE_CHAR_COMMENTS);
	root = icsgParseOperand(NULL,0,lexer);

	if(!root)
	{
		lexDestroy(lexer);
		return;
	}

	distribg(root);
	
	dump_objs(root);

	lexDestroy(lexer);
	btDeleteAllNodes(root,csg_node_destroy);
}
