// parser2: parser source not generated with bison

#include "xrmlP.H"
#include "parser.H"
#include "file.H"
#include "Children.H"
#include "Bindable.H"
#include "ExternProto.H"
#include "Inline.H"
#include "nodeCatalog.H"

namespace vrml2_utf8 {

PoolImpl(context)
PoolImpl(parser)

/* for reporting parse errors */
void parser::yyerror(char *s)
{
  if (errlev==0) {
    xrml::current_linenr = yylineno;	// make sure we have correct line nr.
    Error(NULL, "%s near '%s'", s, yytext);
    if (ctx->curnode) errlev++;
  }
}

class xrml::importer* parser::instantiate(class file* fh)
{
  // TODO: if fh!=0, we should check whether the file 
  // really is a VRML file.
  return new parser;
}

bool parser::parse(class world *w, char **strpoolptr)
{
  world = w;
  scanfp = world->file->fp;
  sbuf = strpoolptr;

  ctx = new context;	// will become root context
  errlev = 0;
  xrml::current_filename = world->filename;
  xrml::current_linenr = yylineno = 1;
  yyparse();

  // append scene graph, named nodes table and protos table (only pointers to
  // the nodes are copied, not the nodes themselves!)
  if (ctx->sceneGraph) world->sceneGraph->concat(*ctx->sceneGraph);
  world->namedNodes.concat(ctx->namedNodes);
  world->protos.concat(ctx->protos);

  return (ctx->sceneGraph != 0);
}

context::context()
{
  sceneGraph = new MFNode;
  namedNodes.default_value=0; namedNodes.default_key=0; 
  protos.default_value=0; protos.default_key=0;
  curnode = 0;
  master = 0;
}

int context::add_proto(class Proto* proto)
{
  return protos.add((char*)proto->typeId, proto);
}

Proto *context::lookup_proto(char *typeId)
{
  int i = protos(typeId);
  return (i>=0) ? protos[i] : (Proto*)0;
}

int context::add_namedNode(char *name, class SFNode* node)
{
  node->nameId = name;
  return node->nameIndex = namedNodes.add((char*)name, node);
}

class SFNode* context::lookup_namedNode(char* nameId)
{
  int i = namedNodes((char*)nameId);
  return (i>=0) ? namedNodes[i] : (SFNode*)0;
}

// called after parsing a VRML header
void parser::Start(void)
{
  Info(NULL, "Parsing VRML file %s ...", world->file->url);
  rootctx = ctx;
}

/* looks up a PROTO node with given typeId, first in the current context,
 * next in the previous contexts if not found in the current context. */
Proto *parser::LookupProto(char *typeId)
{
  Proto *proto = ctx->lookup_proto(typeId);
  int i = contextStack.size-1;
  while (!proto && i>=0) {
    proto = contextStack[i]->lookup_proto(typeId);
    i--;
  }
  return proto;
}

/* appends the node to the current context scene graph if it is a root node 
 * and there are no errors. */
void parser::AppendNode(SFNode* node)
{
  if (ctx->curnode==0 /*node is root node*/ && errlev==0 /*no errors*/) {
    if (ctx==rootctx && !IsChildren(node->firstnode()))
      Warning(NULL, "root node %s (type '%s') should be a children node", 
	    node->name(), node->firstnode()->typeId);
    else
      ctx->sceneGraph->append(node);
  }
}

// NAME statement
void parser::DefineNamedNode(char *nodeNameId, SFNode *node)
{
  node->nameIndex = ctx->add_namedNode(nodeNameId, node);
}

// DEF statement
void parser::AppendNamedNode(char *nodeNameId, SFNode *node)
{
  DefineNamedNode(nodeNameId, node);
  AppendNode(node);
}

// USE statement
SFNode* parser::UseNamedNode(char *nodeNameId)
{
  SFNode *node = ctx->lookup_namedNode(nodeNameId);
  if (!node)
    Error(NULL, "no node named '%s' in the current context", nodeNameId);
  else
    AppendNode(node);
  return node;
}

// called after PROTO interface description has been parsed and before
// parsing the body.
void parser::BeginProto(char *typeId)
{
  if (ctx->lookup_proto(typeId))
    Error(NULL, "duplicate PROTO '%s'", typeId);

  contextStack.push(ctx); ctx = new context;
  ctx->master = new SFNode(typeId, interface.size, &interface[0]);
  interface.size = 0;  
}

// called after parsing PROTO body.
void parser::EndProto(void)
{
  Proto *proto = new Proto(ctx->master, ctx->sceneGraph, ctx->namedNodes, ctx->protos);
  ctx = contextStack.pop();
  ctx->add_proto(proto);
}

// called after parsing EXTERNPTOTO interface description
void parser::NewExternProto(char *typeId, MFString* URLList)
{
  if (ctx->lookup_proto(typeId))
    Error(NULL, "duplicate PROTO '%s'", typeId);

  SFNode *master = new SFNode(typeId, interface.size, &interface[0]);
  interface.size = 0;

  ExternProto *proto = new ExternProto(master);
  proto->fetch(URLList);
  ctx->add_proto(proto);
}

// called when starting to parse a node instance
void parser::BeginNodeInstance(char *typeId)
{
  ctx->nodeStack.push(ctx->curnode);
  if (errlev>0) errlev++;

  SFNode *prototype = builtin_nodes->lookup(typeId);
  if (!prototype)
    prototype = LookupProto(typeId);

  if (prototype) {
    bool save_init_bindables = init_bind_bindables;
    // also affects initial binding of bindable nodes in the
    // implementation of a root PROTO being instantiated.
    init_bind_bindables = (ctx == rootctx);

    ctx->curnode = prototype->instantiate();

    init_bind_bindables = save_init_bindables;
  } else {
    Error(NULL, "unknown node type '%s'", typeId);
    ctx->curnode = 0; errlev++;
  }
}

// called after parsing a node instance
SFNode* parser::EndNodeInstance(void)
{
  SFNode *node = ctx->curnode; 

  ctx->curnode = ctx->nodeStack.pop(); 
  if (errlev>0) errlev--; 
  return node;
}

// called when starting to parse a Script node
void parser::BeginScript(void)
{
  ctx->nodeStack.push(ctx->curnode); 
  if (errlev>0) errlev++;
  ctx->curnode = builtin_nodes->lookup("Script")->instantiate();
}

// called after parsing script node
SFNode* parser::EndScript(void)
{
  SFNode *scriptnode = ctx->curnode;
  ctx->curnode = ctx->nodeStack.pop(); 
  if (errlev>0) errlev--;
  return scriptnode;
}

/* appends member to the interface of ctx->curnode (Script nodes) */
void parser::AppendMember(class interfaceMember *member)
{
  char *Id = (char*)member->Id;		// discard const
  if (!ctx->curnode)
    return;
  else if (ctx->curnode->get(Id)) 
    Error(NULL, "Current Script node %s already has a member named '%s'",
	  ctx->curnode->name(), Id);
  else {
    int n = ctx->curnode->nrInterfaceMembers;
    interfaceMember **newif = new interfaceMember* [n+1];
    for (int i=0; i<n; i++)		// copy pointers to old members
      newif[i] = ctx->curnode->interface[i];
    newif[n] = member;

    delete[] ctx->curnode->interface;	// delete old array
    ctx->curnode->interface = newif;	// replace by new
    ctx->curnode->nrInterfaceMembers = n+1;
  }
}

// IS-statements: replaces ctx->curnode member named 'dstId' by a 
// link to ctx->master (enclosing proto definition) member named 'srcId'
void parser::MakeLink(char *dstId, char *srcId)
{
  interfaceMember *src, *dst;
  interfaceMember *link;
  if (errlev>0)
    { /* skip it */ }
  else if (!ctx->master)
    Error(NULL, "No enclosing PROTO");
  else if (!(src = ctx->master->get(srcId)))
    Error(NULL, "Enclosing %s PROTO doesn't have a '%s' member", ctx->master->typeId, srcId);
  else if (!(dst = ctx->curnode->get(dstId)))
    Error(NULL, "%s nodes don't have a '%s' member", ctx->curnode->typeId, dstId);
  else if (fieldType(*src) != fieldType(*dst))
    Error(NULL, "%s.%s and %s.%s don't have the same value type (%s versus %s)",
	  ctx->curnode->typeId, dstId, ctx->master->typeId, srcId,
	  fieldTypeName(fieldType(*dst)), fieldTypeName(fieldType(*src)));
  else if (!(link = dst->link(ctx->curnode, ctx->master, src)))
    Error(NULL, "Linking %s.%s with %s.%s is not allowed", ctx->curnode->typeId, dstId, ctx->master->typeId, srcId);
  else {
    int i=0;	// locate dst interface member in ctx->curnodes interface
    while (ctx->curnode->interface[i] != dst) i++;
    ctx->curnode->interface[i] = link;
    delete dst;
  }
}

/* returns the current node's field with given Id. */
interfaceMember *parser::GetField(char *Id)
{
  interfaceMember *f = ctx->curnode->get(Id);

  if (!f || !is_field(f)) {
    Error(NULL, "%s nodes don't have a '%s' field",
	  ctx->curnode->typeId, Id);
    SkipFieldValue();
    return 0;
  }
  return f;
}

/* handles fieldId - value pairs when the value is not an SFNode or
 * MFNode (SFNode and MFNode values are parsed using the yacc generated
 * parser, other "simple" types are parsed with ParseValue()) */
void parser::NonNodeFieldIdValuePair(char *fieldId)
{
  if (errlev>0) return;
  interfaceMember *f;
  fieldValue value;

  if (!(f = GetField(fieldId)))
    return;
  value = ParseValue(fieldType(*f));

  f->initialize(ctx->curnode, value);
  //  value.destroy();
}

/* handles field Id-value pairs when the value is of type SFNode or
 * MFNode. */
void parser::NodeFieldIdValuePair(char *fieldId, fieldValue value)
{
  if (errlev>0) return;
  interfaceMember *f;

  if (!(f = GetField(fieldId)))
    return;

  if (fieldType(*f)!=tSFNode && fieldType(*f)!=tMFNode) {
    Error(NULL, "value of a %s.%s field shall be a %s value and not a %s",
	  ctx->curnode->typeId, fieldId, 
	  fieldTypeName(fieldType(*f)), 
	  fieldTypeName(fieldType(value)));
    return;
  }

  /* convert single-node values between SFNode and MFNode if necessary */
  if (fieldType(*f) == tSFNode && fieldType(value) == tMFNode) {
    MFNode* mfval = (MFNode*)value;
    if (mfval && mfval->size > 0) {
      SFNode *sfnode = (*mfval)[0];
      delete mfval;
      value = new SFNode*(sfnode);
    } else
      value = new SFNode*(0);
  }

  else if (fieldType(*f) == tMFNode && fieldType(value) == tSFNode) {
    if ((SFNode**)value) {
      MFNode *mfnodep = new MFNode;
      mfnodep->append((SFNode*&)value);
      delete (SFNode **)value;
      value = mfnodep;
    } else
      value = new MFNode(0);
  }

  f->initialize(ctx->curnode, value);
  //  value.destroy();
}

// ROUTE statement: adds a route from the eventOut/exposedField 
// srcNodeNameId.srcEventOutId to the eventIn/exposedField 
// dstNodeNameId.dstEventInId.
void parser::AddRoute(char *srcNodeNameId, char *srcEventOutId, char *dstNodeNameId, char *dstEventInId)
{
  int srcIndex =  ctx->namedNodes(srcNodeNameId);
  if (srcIndex < 0) {
    Error(NULL, "No node named '%s' in the current context", srcNodeNameId); return;
  }
  SFNode *srcNode = ctx->namedNodes[srcIndex];
  interfaceMember *srcifm = srcNode->get(srcEventOutId);
  if (!srcifm) {
    Error(NULL, "%s nodes don't have a %s member", srcNode->typeId, srcEventOutId); return;
  }
  if (!is_eventOut(srcifm) && !is_exposedField(srcifm)) {
    Error(NULL, "%s.%s member is not an eventOut or exposedField", srcNode->typeId, srcifm->Id); return;
  }

  int dstIndex =  ctx->namedNodes(dstNodeNameId);
  if (dstIndex < 0) {
    Error(NULL, "No node named '%s' in the current context", dstNodeNameId); return;
  }
  SFNode *dstNode = ctx->namedNodes[dstIndex];
  interfaceMember *dstifm = dstNode->get(dstEventInId);
  if (!dstifm) {
    Error(NULL, "%s nodes don't have a %s member", dstNode->typeId, dstEventInId); return;
  }
  if (!is_eventIn(dstifm) && !is_exposedField(dstifm)) {
    Error(NULL, "%s.%s member is not an eventIn or exposedField", dstNode->typeId, dstifm->Id); return;
  }

  if (fieldType(*srcifm) != fieldType(*dstifm)) {
    Error(NULL, "%s.%s and %s.%s have not the same value type (%s versus %s)",
	  srcNode->typeId, srcifm->Id, dstNode->typeId, dstifm->Id,
	  fieldTypeName(fieldType(*srcifm)), 
	  fieldTypeName(fieldType(*dstifm)));
    return;
  }

  srcifm->add_route(dstNode, dstifm);
}

/* ********************************************************************** */ 
/* returns TRUE if the token is something that can occur in a field value
 * and FALSE if it is something that can not. */
int parser::IsFieldValueAtomOrSquareBracket(int token)
{
  switch (token) {
  case tTRUE: case tFALSE: case tNULL: case tSTRING: 
  case tFLOAT: case tINT: case tHEX: case '[': case ']':
    return TRUE;
  }
  return FALSE;
}

/* skips the remainder of a field value, for recovering after
 * fieldId lookup failure. */
void parser::SkipFieldValue(void)
{
  while (IsFieldValueAtomOrSquareBracket(yylex())) {}
  UnputToken();	/* we read one token too much */
}

/* ******************* field value parsing routines ********************** */
/* We can't leave field value parsing to yacc, because the type of a field
 * value to be parsed depends on the semantics of the field being parsed
 * (field value parsing is not context-free, see the DIS appendix.A). Only
 * non-trivial SFNode and MFNode values are left to yacc since they can
 * be distinguished from other value types anyways and are hard to do
 * ourselves. */
double parser::ParseFloat(int cachedtoken)
{
  switch (cachedtoken ? cachedtoken : yylex()) {
  case tFLOAT: 	return yylval.f;
  case tINT:	return (double)yylval.i;
  default:    	yyerror("parse error (expecting a float)");
  }
  return 0.;
}

long parser::ParseInt(int cachedtoken)
{
  switch (cachedtoken ? cachedtoken : yylex()) {
  case tINT:	return yylval.i;
  case tHEX:	return yylval.i;
  default:	yyerror("parse error (expecting an integer)");
  }
  return 0;
}

SFBool parser::ParseSFBool(int cachedtoken)
{
  switch (cachedtoken ? cachedtoken : yylex()) {
  case tTRUE: 	return TRUE;
  case tFALSE: 	return FALSE;
  default:	yyerror("parse error (expecting a boolean)");
  }
  return FALSE;
}
 
SFColor parser::ParseSFColor(int cachedtoken) 
{
  SFColor col;
  col.r = ParseFloat(cachedtoken);
  col.g = ParseFloat();
  col.b = ParseFloat();
  return col;
}
 
SFFloat parser::ParseSFFloat(int cachedtoken)
{
  return (SFFloat)ParseFloat(cachedtoken);
}
 
SFInt32 parser::ParseSFInt32(int cachedtoken) 
{ 
  return (SFInt32)ParseInt(cachedtoken);
}

SFImage *parser::ParseSFImage(int cachedtoken)
{
  int width = ParseSFInt32(cachedtoken);
  int height = ParseSFInt32();
  int num_components = ParseSFInt32();
  SFImage *img = new SFImage(width, height, num_components);
  for (int i=0; i<img->width * img->height; i++)
    img->pixels[i] = ParseSFInt32();

  return img;
}
  
SFNode *parser::ParseSFNode(int cachedtoken) 
{ 
  switch (cachedtoken ? cachedtoken : yylex()) {
  case tNULL:
    return (SFNode *)NULL;
  default:
    // non-null SFNode values are parsed using yacc rules.
    Error("ParseSFNode", "can't parse non-null nodes");
  }
  return (SFNode *)NULL;
}
 
SFRotation parser::ParseSFRotation(int cachedtoken) 
{ 
  SFRotation rot;
  rot.x = ParseFloat(cachedtoken);
  rot.y = ParseFloat();
  rot.z = ParseFloat();
  rot.radians = ParseFloat();
  return rot;
}
 
SFString parser::ParseSFString(int cachedtoken) 
{ 
  if ((cachedtoken ? cachedtoken : yylex()) == tSTRING)
    return (SFString)yylval.s;
  else
    yyerror("parse error (expecting a string)");
  return (SFString)NULL;
}
 
SFTime parser::ParseSFTime(int cachedtoken)
{
  return (SFTime)ParseFloat(cachedtoken);
}
 
SFVec2f parser::ParseSFVec2f(int cachedtoken)
{ 
  SFVec2f v;
  v.s = ParseFloat(cachedtoken);
  v.t = ParseFloat();
  return v;
}

SFVec3f parser::ParseSFVec3f(int cachedtoken)
{
  SFVec3f v;
  v.x = ParseFloat(cachedtoken);
  v.y = ParseFloat();
  v.z = ParseFloat();
  return v;
}

#define PARSEARRAY(type, parsel) { 	\
  type *p = new type; int tok;		\
					\
  switch ((tok=(cachedtoken ? cachedtoken : yylex()))) { \
  case '[':				\
    while ((tok=yylex()) != ']') {	\
      p->append(parsel(tok));		\
    }					\
    break;				\
  default:				\
    p->append(parsel(tok));		\
  }					\
					\
  return p;				\
}

MFColor *parser::ParseMFColor(int cachedtoken)
{
  PARSEARRAY(MFColor, ParseSFColor);
}

MFFloat *parser::ParseMFFloat(int cachedtoken) 
{
  PARSEARRAY(MFFloat, ParseSFFloat);
} 

MFInt32 *parser::ParseMFInt32(int cachedtoken) 
{ 
  PARSEARRAY(MFInt32, ParseSFInt32);
} 

MFNode *parser::ParseMFNode(int cachedtoken)
{
  PARSEARRAY(MFNode, ParseSFNode);
}
 
MFRotation *parser::ParseMFRotation(int cachedtoken)
{
  PARSEARRAY(MFRotation, ParseSFRotation);
}

MFString *parser::ParseMFString(int cachedtoken) 
{ 
  PARSEARRAY(MFString, ParseSFString);
}

MFTime *parser::ParseMFTime(int cachedtoken)
{
  PARSEARRAY(MFTime, ParseSFTime);
}
 
MFVec2f *parser::ParseMFVec2f(int cachedtoken) 
{ 
  PARSEARRAY(MFVec2f, ParseSFVec2f);
}

MFVec3f *parser::ParseMFVec3f(int cachedtoken)
{
  PARSEARRAY(MFVec3f, ParseSFVec3f);
}

/* parses a value of the given type */
fieldValueBase parser::ParseValue(fieldType type, int cachedtoken)
{
  fieldValue val;	/* not a fieldValueBase, because we'd like to use
			 * the type-dependent constructors that are not 
			 * possible with fieldValueBase. */

  switch(type) {
  case tSFBool:		val = new SFBool(ParseSFBool(cachedtoken));	break;
  case tSFFloat:	val = new SFFloat(ParseSFFloat(cachedtoken));	break; 
  case tSFInt32:	val = new SFInt32(ParseSFInt32(cachedtoken));	break; 
  case tSFTime:		val = new SFTime(ParseSFTime(cachedtoken));	break; 
  case tSFColor:	val = new SFColor(ParseSFColor(cachedtoken));	break;
  case tSFRotation: 	val = new SFRotation(ParseSFRotation(cachedtoken)); break;
  case tSFVec2f:	val = new SFVec2f(ParseSFVec2f(cachedtoken));	break; 
  case tSFVec3f:	val = new SFVec3f(ParseSFVec3f(cachedtoken));	break;
  case tSFString:	val = new SFString(ParseSFString(cachedtoken));	break;
  case tSFNode:		val = new SFNode*(ParseSFNode(cachedtoken)); 	break;
  case tSFImage:	val = ParseSFImage(cachedtoken);		break;
  case tMFColor:	val = ParseMFColor(cachedtoken);		break; 
  case tMFFloat:	val = ParseMFFloat(cachedtoken);		break; 
  case tMFInt32:	val = ParseMFInt32(cachedtoken);		break; 
  case tMFNode:		val = ParseMFNode(cachedtoken);			break; 
  case tMFRotation: 	val = ParseMFRotation(cachedtoken); 		break; 
  case tMFString:	val = ParseMFString(cachedtoken); 		break; 
  case tMFTime:		val = ParseMFTime(cachedtoken);			break; 
  case tMFVec2f:	val = ParseMFVec2f(cachedtoken);		break; 
  case tMFVec3f:	val = ParseMFVec3f(cachedtoken);		break; 
  default:
    Fatal(-1, "ParseValue", "invalid type %d\n", type);
  }

  return val;
}

} // namespace vrml2_utf8

