#include <sstream>
using namespace std;

#include <print.h>
#include <message.h>

#include <vrmlshapeparse.h>


// DEBUGGING THE PARSER
// Turn on or off the debugging the parser.  
// By comparing the output debug.txt with the vrml file
// the parsers state can be observed by seeing which
// tokens it was trying to compare. So the parsers state
// is easily observed.
#define DEBUG_VRMLSHAPEPARSE

#ifdef DEBUG_VRMLSHAPEPARSE
// This is very convenient, but be aware that it does
// introduce a global symbol into the *.o space
// at the linking stage.
messagefile dbg("debug.txt",true);
#define DEBUG_VRML(x) cout << #x << endl
#else
#define DEBUG_VRML(x)
#endif

#ifdef DEBUG_VRMLSHAPEPARSE

#endif


void vrmlshapetoken::depth( vrmlshapeparse & p ) const
{ 
  ++p.tokenstream; 
}

void vrmlshapetoken::consumescope 
(
  vrmlshapeparse & p,
  stringc & left, 
  stringc & right
) const
{
  ++p.tokenstream;
  // Insist that client knows what they are
  // doing and is expecting the scope.
  assert(p.tokenstream()==left);

  int count = 1;

  ++p.tokenstream;
  while ((count>0) && !p.tokenstream)
  {
    stringc & s(p.tokenstream());
    if (s==left)
    {
      ++count;
      ++p.tokenstream;
    }
    else
    {
      if (s==right)
      {
        --count;
        ++p.tokenstream;
      }
      else
        depth(p);
    };
  }
}


void vrmlshapetoken::eval
( 
  bool & handled, 
  vrmlshapeparse & p 
) const
{
  assert(!p.tokenstream);

#ifdef DEBUG_VRMLSHAPEPARSE
//messagefile("debug.txt")() << p.tokenstream() << "==" << id << endl;
dbg() << p.tokenstream() << "==" << id << endl;
#endif

  if (p.tokenstream()==id)
  {
    handled = true;
    this->eval(p);
  }
  else
    handled = false;
}

void vrmlshapetokengroup::eval( vrmlshapeparse & p ) const
{
  uint kmax = tokens.size();
  uint k=0;
  bool res(false);
  for ( ; k<kmax; ++k)
  {

    tokens[k]->eval(res,p);
    if (res)
      return;
  }

  if (res==false)
    ++p.tokenstream;

/*

  vector< vrmlshapetoken* >::const_iterator k = tokens.begin();
  vector< vrmlshapetoken* >::const_iterator kend = tokens.end();

  bool res = false;
// cout << "> " << p.tokenstream() << endl;
  while ((k!=kend) && (res==false))
  {
    (*k)->eval(res,p);
    ++k;
  }
  // If the token was not consumed/processed, consume it.
  if (res==false)
    ++p.tokenstream;
*/
}

vrmlshapetokengroup::~vrmlshapetokengroup()
{
  if( tokens.empty()==false)
  {
    for (uint i=0; i<tokens.size(); ++i)
    {
      delete tokens[i];
      tokens[i]=0;
    }
  }
}



boolc vrmlshapeparse::readfile
(
  stringc & filename
)
{
  //if (!tokenstream.readfile(filename.c_str()))
  //  return false;
  string str;

  if( !tokenizermisc::readfile(str,filename) )
    return false;

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << "File read" << endl;
#endif

  tokenstream.readaslines(str);


  tokenstream.stripcomment("#");

  tokenstream.subtract(",");
  tokenstream.atomize("{");
  tokenstream.atomize("}");
  tokenstream.atomize("[");
  tokenstream.atomize("]");

  tokenstream.remove_if();  

  // This is not entirely correct. I am using
  // this to eliminate leading tabs...
  // But a general subtract functional object
  // would be best. Or replace bad characters with
  // a space then subtract.
  tokenstream.trim();
  tokenstream.subtract(" ");

  tg.tokens.push_back( new vrmlshapeShape() );

  // The iterator is responsible for incrementing the stream.
  for (tokenstream.reset(); !tokenstream; )
    tg.eval(*this);

  return true;
}

vrmlshapeIndexedFaceSet::vrmlshapeIndexedFaceSet()
  : vrmlshapetoken("IndexedFaceSet") 
{
  tg.tokens.push_back(new vrmlshapepoint());
  tg.tokens.push_back(new vrmlshapecoordIndex());
  tg.tokens.push_back(new vrmlshapeNormal());
}

void vrmlshapeIndexedFaceSet::eval( vrmlshapeparse & p ) const
{
  assert(p.vshp.empty()==false);
  p.vshp.back().istriangles = true;
    
  consumescope(p,"{","}");    

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}

vrmlshapeMaterial::vrmlshapeMaterial()
  : vrmlshapetoken("Material")
{
  tg.tokens.push_back(new vrmlshapediffuseColor());
  tg.tokens.push_back(new vrmlshapeemissiveColor());
  tg.tokens.push_back(new vrmlshapeambientIntensity());
}

void vrmlshapeMaterial::eval( vrmlshapeparse & p ) const
{
  assert(p.vshp.empty()==false);
    
  consumescope(p,"{","}");    

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}



void vrmlshapecoordIndex::eval( vrmlshapeparse & p ) const
{
  assert(p.vshp.empty()==false);
  ++p.tokenstream;
  assert(p.tokenstream()=="[");
  ++p.tokenstream;
  int x;
  while((p.tokenstream()!="]") && !p.tokenstream)
  {
    stringstream s(p.tokenstream());
    s >> x;
    p.vshp.back().coordIndex.push_back(x);

    ++p.tokenstream;
  }
  ++p.tokenstream;

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}


void vrmlshapeNormal::eval( vrmlshapeparse & p ) const
{
  assert(p.vshp.empty()==false);
  ++p.tokenstream;
  assert(p.tokenstream()=="{");
  ++p.tokenstream;
  assert(p.tokenstream()=="vector");
  ++p.tokenstream;

  assert(p.tokenstream()=="[");
  ++p.tokenstream;
  double d;
  while((p.tokenstream()!="]") && !p.tokenstream)
  {
    stringstream s(p.tokenstream());
    s >> d;
    p.vshp.back().normal.push_back(d);

    ++p.tokenstream;
  }
  ++p.tokenstream;

  assert(p.tokenstream()=="}");
  ++p.tokenstream;

  assert((p.vshp.back().normal.size()%3)==0);

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}



void vrmlshapepoint::eval( vrmlshapeparse & p ) const
{
  assert(p.vshp.empty()==false);
  ++p.tokenstream;
  assert(p.tokenstream()=="[");
  ++p.tokenstream;
  double d;
  while((p.tokenstream()!="]") && !p.tokenstream)
  {
    stringstream s(p.tokenstream());
    s >> d;
    p.vshp.back().point.push_back(d);

    ++p.tokenstream;
  }
  ++p.tokenstream;

  assert((p.vshp.back().point.size()%3)==0);

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}



void vrmlshapediffuseColor::eval
( 
  vrmlshapeparse & p 
) const 
{
  assert(p.vshp.empty()==false);
  ++p.tokenstream;
  double x;
  stringstream(p.tokenstream()) >> x;
  p.vshp.back().diffuseColor[0] = x;
  ++p.tokenstream;
  stringstream(p.tokenstream()) >> x;
  p.vshp.back().diffuseColor[1] = x;
  ++p.tokenstream;
  stringstream(p.tokenstream()) >> x;
  p.vshp.back().diffuseColor[2] = x;
  ++p.tokenstream;

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}

void vrmlshapeemissiveColor::eval
( 
  vrmlshapeparse & p 
) const 
{
  assert(p.vshp.empty()==false);
  ++p.tokenstream;
  double x;
  uintc last(p.vshp.size()-1);
  stringstream(p.tokenstream()) >> x;
  p.vshp[last].emissiveColor[0] = x;
  ++p.tokenstream;
  stringstream(p.tokenstream()) >> x;
  p.vshp[last].emissiveColor[1] = x;
  ++p.tokenstream;
  stringstream(p.tokenstream()) >> x;
  p.vshp[last].emissiveColor[2] = x;
  ++p.tokenstream;

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}


void vrmlshapeambientIntensity::eval
(
  vrmlshapeparse & p 
) const 
{
  assert(p.vshp.empty()==false);
  ++p.tokenstream;
  double x;
  stringstream(p.tokenstream()) >> x;
  p.vshp.back().ambientIntensity = x;

  ++p.tokenstream;

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}

vrmlshapeIndexedLineSet::vrmlshapeIndexedLineSet()
  : vrmlshapetoken("IndexedLineSet") 
{
  tg.tokens.push_back(new vrmlshapepoint());
  tg.tokens.push_back(new vrmlshapecoordIndex());
}

void vrmlshapeIndexedLineSet::eval( vrmlshapeparse & p ) const
{
  assert(p.vshp.empty()==false);
  p.vshp.back().isline = true;
    
  consumescope(p,"{","}");    

#ifdef DEBUG_VRMLSHAPEPARSE
dbg() << id << endl;
#endif
}

vrmlshapeShape::vrmlshapeShape() 
  : vrmlshapetoken("Shape") 
{
DEBUG_VRML(vrmlshapeShape::vrmlshapeShape());

  tg.tokens.push_back( new vrmlshapeIndexedLineSet() );
  tg.tokens.push_back( new vrmlshapeIndexedFaceSet() );
  tg.tokens.push_back( new vrmlshapeMaterial() );
}

void vrmlshapeShape::eval( vrmlshapeparse & p ) const
{
DEBUG_VRML(void vrmlshapeShape::eval( vrmlshapeparse & p ) const);

  p.vshp.push_back( vrmlshaperaw() );

  consumescope(p,"{","}"); 
}




