#ifndef _BOUBBLE_HH_
#define _BOUBBLE_HH_

#include <list>
#include <iostream>
#include <sstream>

#include "thesymbols.hh"


enum Dir {UP=0,DOWN=1,AT_TARGET=2};

// class BoubbleAction
// {
// public:
//   void apply() {};
// private:
  
// };



class Boubble
{
public:
  Boubble(list<Role> u, list<Role> d, LongRel l, int s=-1);
  Boubble(const char* pathstr, const char* l, int s=-1);

  Dir dir();
  LongRel rel();
  int src();
  void src(int s);

  Role next();

  Boubble* step(Role r, Dir d);
  
  bool is_at_target();

  bool operator==(Boubble const& b) const;
  bool operator!=(Boubble const& b) const;
  bool operator<(Boubble const& b) const;

  void as_cstr(char* s);
  friend std::ostream& operator<<(std::ostream&, const Boubble&);

private:

  int                  _src;
  list<Role>           _upath;
  list<Role>           _dpath;
  LongRel              _rel;

};

//----------------------------------------------------------------------------------------------------

inline
Boubble::Boubble(list<Role> u, list<Role> d, LongRel l, int s) : _upath(u), _dpath(d), _rel(l), _src(s) {}

//----------------------------------------------------------------------------------------------------

inline
Boubble::Boubble(const char* pathstr, const char* l, int s)
{
  Dir dir = UP;
  const char* p = pathstr;
  while(*p)
    {
      if(*p=='^') { dir = DOWN; p++; }
      else if(isalpha(*p))
	{
	  char buf[80];
	  sscanf(p,"%[a-zA-Z0-9_]",buf);
	  dir == UP ? _upath.push_back(Role(buf)) : _dpath.push_back(Role(buf));
	  p += strlen(buf);
	}
      else
	p++;
    }
  
  _rel = LongRel(l);
  _src = s;
}

//----------------------------------------------------------------------------------------------------

inline
Dir Boubble::dir()
{ 
  if(!_upath.empty())
    return UP;
  else if(!_dpath.empty())
    return DOWN;
  else return AT_TARGET;
}

//----------------------------------------------------------------------------------------------------

inline
LongRel Boubble::rel()
{ return _rel; }

//----------------------------------------------------------------------------------------------------

inline
int Boubble::src()
{ return _src; }

//----------------------------------------------------------------------------------------------------

inline
void Boubble::src(int s)
{ _src=s; }

//----------------------------------------------------------------------------------------------------

inline
Role Boubble::next()
{ 
  if(!_upath.empty())
    return _upath.front();
  else if(!_dpath.empty())
    return _dpath.front();
  else return Role("NULL"); 
}

//----------------------------------------------------------------------------------------------------

inline
Boubble* Boubble::step(Role r, Dir d)
{
  if(d==UP && !_upath.empty() && _upath.front() == r)
    {
      Boubble* newboubble = new Boubble(_upath,_dpath,_rel,_src);
      newboubble->_upath.pop_front();
      return newboubble;
    }
  
  if(d==DOWN && _upath.empty() && !_dpath.empty())
    {
      Boubble* newboubble = new Boubble(_upath,_dpath,_rel,_src);
      newboubble->_dpath.pop_front();
      return newboubble;
    }
  return NULL;
}

//----------------------------------------------------------------------------------------------------

inline
bool Boubble::is_at_target()
{ return _upath.empty() && _dpath.empty(); }

//----------------------------------------------------------------------------------------------------

inline
bool Boubble::operator==(Boubble const& b) const
{
  return _src==b._src && _upath==b._upath && _dpath==b._dpath && _rel==b._rel;
}

//----------------------------------------------------------------------------------------------------

inline
bool Boubble::operator!=(Boubble const& b) const
{
  return !(*this==b);
}

//----------------------------------------------------------------------------------------------------

inline
bool Boubble::operator<(Boubble const& b) const
{
  if(_src < b._src) return true;
  if(_rel < b._rel) return true;
  if(this < &b) return true;
  return false;
}

//----------------------------------------------------------------------------------------------------

inline
std::ostream& operator<<(std::ostream& o, const Boubble& b)
{
  o << "[";

  o << b._src << "|";

  bool cont=false;
  for(list<Role>::const_iterator i = b._upath.begin(); i != b._upath.end(); ++i)
    {
      if(cont) o << ',';
      o << i->str();
      cont = true;
    }
  o << '^';
  cont=false;
  for(list<Role>::const_iterator i = b._dpath.begin(); i != b._dpath.end(); ++i)
    {
      if(cont) o << ',';
      o << i->str();
      cont = true;
    }
  o << ':';
  o << b._rel.str();
  o << "]";
}

//----------------------------------------------------------------------------------------------------

inline
void Boubble::as_cstr(char* s)
{
  stringstream oss;
  oss << *this;
  strcpy(s,oss.str().c_str());
}

//====================================================================================================

#endif
