#include <cassert>

#include <pathstuff.h>

#ifndef NDEBUG
// Toggle Debug code.
//#define DEBUG_PATHSTUFF
#endif



// Implementation
// ------------------------------------------------------------ 
//


string const pathstuff::root = "/";
string const pathstuff::fwdsep = "/";
string const pathstuff::parent = "..";



/* Forwards processing to rpnprogramstackstate::findprogram. */
/* I haven't moved code out of there to here because at
 *  present I do not wish to break working code. */
void pathstuff::pwd(vector<string>& v)
{
  string path;
  bool found(false);

  deque<rpnprogram*> & ps( * rpnprogramstackstate().ps );
  for ( unsigned int i=0; (i<ps.size())&&(!found); ++i )
  {
    rpnprogramstackstate().findprogram(
      found,path,ps[i] );
  }
//
//  Replaced with for loop because the top of the
//    stack could be a program.
//
//  rpnprogramstackstate().findprogram(
//    found,path,rpnprogramstackstate().ps->front() );

if (found==false)
{
cout << "error:";
cout << "  pathstuff::pwd( vector<string>& ) pathstuff 114";
cout << endl;
  cout << "  found=false" << endl;
}

//cout << "path=" << path << endl;

  
  convert(v,path);
}

void pathstuff::findrelativetree
(
  bool& found,
  rpnprogram*& prog,
  rpnprogram* const current,
  vector<string> const & pathtree
)
{
  found = false;

  if (pathtree.empty())
  {
    prog = current;
    found = true;
  
    return;
  }

  /* Reject absolute paths. */
  if (pathtree[0]==root)
    return;

  /* Reject relative paths. */
  bool res;
  contains(res,pathtree,parent);
  if (res)
    return;

#ifdef DEBUG_PATHSTUFF
string t;
convert(t,pathtree);
cout << "pathtree=" << t << endl;
#endif

  rpnprogram* p = current;
  rpnvar* z;

  bool foundlocal;

#ifdef DEBUG_PATHSTUFF
cout << "found=" << found << endl;
#endif

  for (unsigned int i=0, imax = pathtree.size(); i<imax; ++i)
  {
    deque<rpnvar*>& var = p->variables;
    if (var.empty())
      return;

    foundlocal = false;
    for (unsigned int k=0, kmax=var.size(); k<kmax; ++k)
    {
       z = var[k];

       if ( (z->varname==pathtree[i]) && (z->x->isprogram()) )
       {
         foundlocal = true;
         p = (rpnprogram*)(z->x);
         k = kmax;
       }
    }
    if (!foundlocal)
      return;
  }

  found = true;
  prog = p;
}


bool const pathstuff::ispurerelative( vector<string> const & path )
{
  if(path.empty())
    return true;

  if (path[0]==root)
    return false;

  bool res;
  contains(res,path,parent);

  return !res;
}

void pathstuff::findrelativetree
(
  bool& found,
  deque<rpnprogram*>& programlist,
  rpnprogram* const current,
  vector<string> const & pathtree
)
{
  found = false;
  programlist.clear();
  programlist.push_back(current);

  if (pathtree.empty())
  {
    found = true;

    return;
  }

  if (!ispurerelative(pathtree))
  {
    programlist.clear();
    return;
  }

  rpnprogram* p = current;
  rpnvar* z;

  bool foundlocal;

  for (unsigned int i=0, imax = pathtree.size(); i<imax; ++i)
  {
    deque<rpnvar*>& var = p->variables;
    if (var.empty())
      return;

    for (unsigned int k=0, kmax=var.size(); k<kmax; ++k)
    {
       z = var[k];
       if (z->varname==pathtree[i])
       {
         foundlocal = true;
         p = (rpnprogram*)(z->x);
         programlist.push_back(p);
         k = kmax;
       }
    }
    if (!foundlocal)
    {
      programlist.clear();
      return;
    }
  }

  found = true;
}


void pathstuff::contains
(
  bool& res, 
  vector<string> const & path, 
  string const & targ
)
{
  res = false;

  if (path.empty())
    return; 

  for (unsigned int i=0, imax=path.size(); i<imax; ++i)
  {
    if (path[i]==targ)
    {
      res = true;
      return;
    }
  }
}


void pathstuff::convert(vector<string>& v, string const & path) const
{
  assert(false);

// TODO - looking at alternative
//  tokenizer tk(path);
//  tk.subtract(fwdsep);

// Commented out non-existing function.
//  linesplit(v,path,fwdsep);
  if( path[0]=='/')
    v.insert(v.begin(),root);
}

void pathstuff::convert(string& s, vector<string> const & pth) const
{
  vector<string> path(pth);

  s = "";
  if (path.empty())
    return;

  s = path[0];

  if (path.size()==1)
    return;

  if (path[0] != root)
    s += fwdsep;
  s += path[1]; 

  if (path.size()==2)
    return;

  for (unsigned int i=2, imax=path.size(); i<imax; ++i)
    s += ( fwdsep + path[i] );
}

void pathstuff::convert(rpnprogram& p, vector<string> const & path) const
{
  if (!p.v.empty())
    return;

  if (path.empty())
    return;

  unsigned int imax = path.size();
  p.v.resize(imax);
  rpnstring *w;
  for (unsigned int i=0; i<imax; ++i)
  {
    
    p.v[i] = w = new rpnstring();
    w->str = path[i];
  }

}

void pathstuff::convert(vector<string>& path, rpnprogram const & p) const
{
  path.clear();

  if (p.v.empty())
    return;

  path.resize( p.v.size() );

  /* Verify that each element is an rpnstring. */
  rpnbase* w;
  for (unsigned int i=0, imax=p.v.size(); i<imax; ++i)
  {
    w = p.v[i];
    if (!w->isstring())    
    {
      path.clear();
      return;
    }

    path[i] = ((rpnstring*)w)->str;
  }

}
  

//  Resolve .. as much as possible.
//  eg  h1 .. g1   to  g1
//  but .. cant be resolved because there is no 
//  information to reslove it.
void pathstuff::resolveparent
(
  bool& valid, 
  vector<string>& path, 
  vector<string> const & path0
)
{
  valid = false;
  path.clear();

  bool res;
  contains(res,path0,parent);
  if (!res)
  {
    valid = true;
    path = path0;
    return;
  }

  if (path0[0]==parent)
    return;

  path.push_back( path0[0] );
  
  for (unsigned int i=1, imax=path0.size(); i<imax; ++i)
  {
    if (path0[i]==parent)
    {
      if (path.empty())
      {
        path.clear();
        return;
      }

      if (path.back()==root)
      {
        path.clear();
        return;
      }

      path.pop_back();
    }
    else
      path.push_back(path0[i]);
  }

  valid = true;

}


void pathstuff::findpath
(
  bool& found,
  rpnprogram*& prog,
  vector<string> const & path
)
{
  found = false;

  if (path.empty())
    return;

  vector<string> v;
  vector<string> v2;

  bool res;
  absolute(res,v,path);
 
  if (res)
  {
    /* Remove / from the front. */

    unsigned int k(0);
    if (v[0]=="/")
      k=1;
    v2.resize(v.end()-v.begin()-k);
    ::copy(v.begin()+k,v.end(),v2.begin());


#ifdef DEBUG_PATHSTUFF
string t;
convert(t,v2);
cout << "-~ :" << t << endl;
#endif

    /* From the home directory find the path. */
    findrelativetree
    (
      found,
      prog,
      &(rpnprogramstackstate().rpnhome),
      v2
    ); 

#ifdef DEBUG_PATHSTUFF
cout << "found=" << found << endl;
#endif

  }

}

void pathstuff::absolute
(
  bool& found, 
  vector<string>& pathfromhome, 
  vector<string> const & path 
)
{
  found = false;

  if (path.empty())
    return;

  vector<string> v(path);
  vector<string> v2;

  /* Make the path be relative to the home directory. */
  if (v[0] != root)
  {
    pwd(v2);
    ::copy(v.begin(),v.end(),back_inserter(v2));
  }
  else
    v2 = v;

#ifdef DEBUG_PATHSTUFF
string t;
convert(t,v2);
cout << "~:" << t << endl;
#endif

  /* Realize the path, resolving .. . */
  resolveparent(found,pathfromhome,v2);
}


void pathstuff::findpath
(
  bool& found,
  deque<rpnprogram*>& programlist,
  vector<string> const & path
)
{
  found = false;

  if (path.empty())
    return;

  vector<string> v;
  vector<string> v2;

  bool res;
  absolute(res,v,path);

#ifdef DEBUG_PATHSTUFF
cout << "res=" << res << endl;
string t;
convert(t,v);
cout << "..:" << t << endl;
#endif

  if (res)
  {
    /* Remove / from the front. */
    v2.resize(v.end()-v.begin()-1);
    ::copy(v.begin()+1,v.end(),v2.begin());

#ifdef DEBUG_PATHSTUFF
convert(t,v2);
cout << "-~ :" << t << endl;
#endif

    /* From the home directory find the path. */
    findrelativetree
    (
      found,
      programlist,
      rpnprogramstackstate().ps->back(),
      v2
    );

#ifdef DEBUG_PATHSTUFF
cout << "found=" << found << endl;
#endif

  }

}





