
#include "cmt_std.h"
#include "cmt_string.h"
#include "cmt_system.h"

/*
#---------------------------------------------------------------------
#
# This program should be installed within the loginfo file of CVS
# using the following line :
#
# ...
#.cmtcvsinfos (${CMTROOT}\cmtcvs\v1\VisualC\cmtcvs.exe %s)
# ...
#
# and is used whenever one tries to import a module named .cmtcvsinfos/<module>
#
#---------------------------------------------------------------------
*/


//
//  This global flag is set when the string "/cmtcvstest/" is found in
//  the module argument. This can be used to change the default
//  behaviour of this pluggin.
//
//   Eg. It can be used to experiment new features.
//
static bool cmtcvstest = false;

//---------------------------------------------------------------------
static cmt_string get_cvsroot (cmt_string& result)
{
    //echo ${CVSROOT} | sed -e 's#^:[^:]*:##'
  CmtSystem::get_cvsroot (result);

  int pos = result.find (":");
  if (pos == 0)
    {
      pos = result.find (1, ":");
      result.erase (0, pos+1);
    }

  return (result);
}

static void clear_cmtcvsinfos ()
{
  cmt_string dir;
  get_cvsroot (dir);
  dir += CmtSystem::file_separator ();
  dir += ".cmtcvsinfos";
  
  CmtSystem::cmt_string_vector files;
  CmtSystem::scan_dir (dir, files);
  
  for (int i = 0; i < files.size (); i++)
    {
      const cmt_string& f = files[i];
      CmtSystem::remove_directory (f);
    }
}

//---------------------------------------------------------------------
static bool check_head (const cmt_string& head,
			const cmt_string& version)
{
    /*
      echo "${head} ${version}" | awk '{\
      head = $1; \
      version = $2; \
      nh = split (head, hlist); \
      nv = split (version, vlist); \
      for (i = 0; i < nh; i++) \
      { \
      if (hlist[i] != vlist[i]) \
      { \
      print "0"; \
      next; \
      } \
      } \
      print "1"; \
      }'
    */
  CmtSystem::cmt_string_vector hlist;
  CmtSystem::cmt_string_vector vlist;

  CmtSystem::split (head, " ", hlist);
  CmtSystem::split (version, " ", vlist);

  int nh = hlist.size ();
    //int nv = vlist.size ();

  for (int i = 0; i < nh; ++i)
    {
      const cmt_string& h = hlist[i];
      const cmt_string& v = vlist[i];

      if (h != v) return (false);
    }

  return (true);
}

//---------------------------------------------------------------------
static bool check_newer (const cmt_string& v1,
			 const cmt_string& v2)
{
    /*
      v1=$1;
      v2=$2;

      #echo "check_newer v1=${v1} v2=${v2}"

      if test "${v2}" = ""; then
      echo "1"
      return
      fi

      echo "${v1} ${v2}" | awk '{\
      v1 = $1;\
      v2 = $2;\
      n1 = len (v1);\
      if (n1 == 0) \
      {\
      print "0";\
      next;\
      }\
      n1 = split (v1, list1);\
      if (n1 == 0) \
      {\
      print "0";\
      next;\
      }\
      n2 = split (v2, list2);\
      for (i = 0; i < n1; i++)\
      {\
      if (i > n2)\
      {\
      print "1";\
      next;
      }\
      if (list1[i] > list2[i])\
      {\
      print "1";\
      next;
      }\
      }\
      print "0";\
      }'
    */

  if (v2 == "") return (true);
  if (v1 == "") return (false);

  CmtSystem::cmt_string_vector list1;
  CmtSystem::cmt_string_vector list2;

  CmtSystem::split (v1, ".", list1);
  CmtSystem::split (v2, ".", list2);

  int n1 = list1.size ();
  int n2 = list2.size ();

    //cout << "checknewer v1=" << v1 << " v2=" << v2 << " n1=" << n1 << " n2=" << n2 << endl;

  for (int i = 0; i < n1; ++i)
    {
      if (i >= n2) return (false);

      const cmt_string& s1 = list1[i];
      const cmt_string& s2 = list2[i];

      int i1;
      int i2;

      i1 = atoi (s1.c_str ());
      i2 = atoi (s2.c_str ());

        //cout << "    i1=" << i1 << " i2=" << i2 << endl;

      if (i1 > i2) return (true);
    }

  return (false);
}

//---------------------------------------------------------------------
static void get_tags (const cmt_string& module,
		      CmtSystem::cmt_string_vector& toptags,
		      CmtSystem::cmt_string_vector& tags,
		      CmtSystem::cmt_string_vector& topcvsversions,
		      CmtSystem::cmt_string_vector& cvsversions)
{
    /*
      module=$1
      package=`basename ${module}`
      container=`dirname ${module}`
      container=`basename ${container}`

  #
  #  First figure out which test file will be used to retreive the tags
  #   o requirements file is considered first
  #   o then any other file in the top directory
  #

  if test -r "${CVSROOT}/${module}/cmt/requirements,v" ; then
  test_file="${CVSROOT}/${module}/cmt/requirements,v"
  elif test -r "${CVSROOT}/${module}/mgr/requirements,v" ; then
  test_file="${CVSROOT}/${module}/mgr/requirements,v"
  else
  test_file=`find "${CVSROOT}/${module}" -name '*,v' -print | head -n 1`
  fi

  #  tags=`tr ' ' '\n' < "${test_file}" | \
  #   awk '/symbols/,/locks/ {n=split($0,w,"."); if ((n == 2) || (n == 4) || (n == 6)) print; }'  | \
  #   sed -e 's#:.*##' -e 's#[    ]*##'`
  #  print "${tags}"

  started=0
  finished=0
  top=
  toptags=
  cvstags=

  #symbols
  #       v10:1.1.1.1
  #       v2:1.1.1.1
  #       v1r2:1.1.1.1
  #       v1r1:1.1.1.1
  #       v1:1.1.1.1
  #       cmt:1.1.1;
  #locks; strict;

  #
  # Then extract all tags from the file header
  #  one line per tag :
  #
  #   <tag>:<cvs-version-id>
  #

  alltags=`tr ' ' '\n' < "${test_file}" | \
  awk '/symbols/,/locks/ { \
  if (done == 1) next; \
  n = split ($0,w,"."); \
  if ((n != 2) && (n != 4) && (n != 6)) next; \
  if ($1 == "symbols") \
  { \
  print $2; \
  } \
  else \
  { \
  print $1; \
  } \
  if ($1 == "locks;") done = 1; \
  }'`

  #
  #  Compute the most recent tag (using the cvs version ids)
  #

  for t in ${alltags}; do
  tag=`echo ${t} | sed -e 's#:.*##'`
  v=`echo ${t} | sed -e 's#.*:##'`

  if test `check_newer ${v} ${top}` = "1"; then
  top=${v}
  fi
  done

  has_container=0
  has_package=0

  for t in ${alltags}; do
  tag=`echo ${t} | sed -e 's#:.*##'`
  v=`echo ${t} | sed -e 's#.*:##'`
  name=`echo ${tag} | sed -e 's#[-].*##'`

  if test "${name}" = "${container}"; then
  has_container=1
  elif test "${name}" = "${package}"; then
  has_package=1
  fi
  done

  #  echo container=${container}
  #  echo has_container=${has_container}
  #  echo has_package=${has_package}

  #
  #  Split the tag list into tags that have same cvs-version-id than
  #  the top tag (tags_tops) and older ones (tags).
  #

  tags=

  for t in ${alltags}; do
  tag=`echo ${t} | sed -e 's#:.*##'`
  v=`echo ${t} | sed -e 's#.*:##'`
  name=`echo ${tag} | sed -e 's#[-].*##'`

  if test "${has_package}" = "1"; then
  if test "${name}" = "${package}"; then
  if test "${v}" = "${top}"; then
  tags="${tags} ${tag}(t) "
  else
  tags="${tags} ${tag} "
  fi
  fi
  else
  if test "${v}" = "${top}"; then
  tags="${tags} ${tag}(t) "
  else
  tags="${tags} ${tag} "
  fi
  fi

  done

  echo "${tags}"
    */

  cmt_string package;
  cmt_string container;

  CmtSystem::basename (module, package);
  CmtSystem::dirname (module, container);
  CmtSystem::dirname (container, container);

    //cout << "package=" << package << endl;
    //cout << "container=" << container << endl;

    /*
      #  First figure out which test file will be used to retreive the tags
      #   o requirements file is considered first
      #   o then any other file in the top directory
      #
    */

  cmt_string test_file;

    /*
  if (cmtcvstest)
    */

  {
      //
      // Experimental removal of Atlas specific behaviour which
      // consisted in considering first the PACKAGE file rather than
      // requirements.
      //
    get_cvsroot (test_file);
    test_file += CmtSystem::file_separator ();
    test_file += module;
    test_file += CmtSystem::file_separator ();
    test_file += "cmt";
    test_file += CmtSystem::file_separator ();
    test_file += "requirements,v";
    
    if (!CmtSystem::test_file (test_file))
      {
        get_cvsroot (test_file);
        test_file += CmtSystem::file_separator ();
        test_file += module;
        test_file += CmtSystem::file_separator ();
        test_file += "mgr";
        test_file += CmtSystem::file_separator ();
        test_file += "requirements,v";
        
        if (!CmtSystem::test_file (test_file))
          {
            get_cvsroot (test_file);
            test_file += CmtSystem::file_separator ();
            test_file += module;
            test_file += CmtSystem::file_separator ();
            test_file += "PACKAGE,v";
            
            if (!CmtSystem::test_file (test_file))
              {
                  // not a CMT package? should take the first file.
                return;
              }
          }
      }
  }

    /*
      else
    {
        //
        // Atlas specific behaviour which consists in considering
        // first the PACKAGE file rather than requirements. This
        // should be eventually discarded .
        //
      get_cvsroot (test_file);
      test_file += CmtSystem::file_separator ();
      test_file += module;
      test_file += CmtSystem::file_separator ();
      test_file += "PACKAGE,v";
      
      if (!CmtSystem::test_file (test_file))
        {
          get_cvsroot (test_file);
          test_file += CmtSystem::file_separator ();
          test_file += module;
          test_file += CmtSystem::file_separator ();
          test_file += "cmt";
          test_file += CmtSystem::file_separator ();
          test_file += "requirements,v";
          
          if (!CmtSystem::test_file (test_file))
            {
              get_cvsroot (test_file);
              test_file += CmtSystem::file_separator ();
              test_file += module;
              test_file += CmtSystem::file_separator ();
              test_file += "mgr";
              test_file += CmtSystem::file_separator ();
              test_file += "requirements,v";
              
              if (!CmtSystem::test_file (test_file))
                {
                    // not a CMT package? should take the first file.
                  return;
                }
            }
        }
    }
    */
  
    /*
#symbols
#       v10:1.1.1.1
#       v2:1.1.1.1
#       v1r2:1.1.1.1
#       v1r1:1.1.1.1
#       v1:1.1.1.1
#       cmt:1.1.1;
#locks; strict;

  #
  # Then extract all tags from the file header
  #  one line per tag :
  #
  #   <tag>:<cvs-version-id>
  #
    */

  //bool started = false;
    //bool finished = false;
  cmt_string top;
  
  cmt_string temp;
  temp.read (test_file);
  int pos = temp.find ("symbols");
  temp.erase (0, pos + 7);
  pos = temp.find ("locks");
  temp.erase (pos);

    //cout << "temp=[" << temp << "]" << endl;

  CmtSystem::cmt_string_vector words;
  CmtSystem::split (temp, " \t\n", words);

  int i;

  CmtSystem::cmt_string_vector alltags;

  for (i = 0; i < words.size (); i++)
    {
      const cmt_string& s = words[i];
      CmtSystem::cmt_string_vector w;
      CmtSystem::split (s, ".", w);
      int n = w.size ();

        //cout << "level=" << n << endl;

        //
        // (temporary??) Suppression of the branch detection.  thus
        // only tags in the main trunk are considered.
        //

      if (cmtcvstest)
        {
          if ((n == 2) ||
              (n == 4) ||
              (n == 6))
            {
              cmt_string& t = alltags.add ();
              t = s;
            }
        }
      else
        {
          if ((n == 2) ||
              (n == 4) ||
              (n == 6))
//          if (n == 2)
            {
              cmt_string& t = alltags.add ();
              t = s;
            }
        }
    }


    /*#
      #  Compute the most recent tag (using the cvs version ids)
      #*/

  for (i = 0; i < alltags.size (); ++i)
    {
      const cmt_string& s = alltags[i];
      cmt_string tag = s;
      pos = tag.find (":");
      tag.erase (pos);
      cmt_string v = s;
      v.replace (";", "");
      v.erase (0, pos+1);

        // cout << "cvsv=" << v << " top=" << top << endl;

      if (check_newer (v, top))
        {
          top = v;
        }
    }

    //cout << "cvstopv=" << top << endl;

  bool has_container = false;
  bool has_package = false;
  
  if (cmtcvstest)
    {
        //
        // Experimental removal of an Atlas specific behaviour which
        // consisted in detecting tags with a pattern <package>-xxx
        //
    }
  else
    {
        //
        //  Atlas specific behaviour which consists in detecting tags
        //  with a pattern <package>-xxx
        //

      for (i = 0; i < alltags.size (); ++i)
        {
          const cmt_string& s = alltags[i];
          cmt_string tag = s;
          pos = tag.find (":");
          tag.erase (pos);
          cmt_string v = s;
          v.replace (";", "");
          v.erase (0, pos);
          cmt_string name = tag;
          pos = name.find ("-");
          name.erase (pos);
          
          if (name == container)
            {
              has_container = true;
            }
          else if (name == package)
            {
              has_package = true;
            }
        }
    }

    /*#
      #  Split the tag list into tags that have same cvs-version-id than
      #  the top tag (tags_tops) and older ones (tags).
      #*/

  toptags.clear ();
  tags.clear ();
  topcvsversions.clear ();
  cvsversions.clear ();

  for (i = 0; i < alltags.size (); ++i)
    {
      const cmt_string& s = alltags[i];
      cmt_string tag = s;
      pos = tag.find (":");
      tag.erase (pos);

      cmt_string v = s;
      v.replace (";", "");
      v.erase (0, pos+1);

      cmt_string fullv = s;
      fullv.replace (";", "");

      cmt_string name = tag;
      pos = name.find ("-");
      name.erase (pos);

        //cout << "tag=" << tag << " name=" << name << " v=" << v << " package=" << package << endl;

      if (cmtcvstest)
        {
            //
            // experimental removal of an Atlas specific feature which
            // consists in ignoring tags which do not follow the
            // <package>- pattern
            //
          if (v == top)
            {
              toptags.push_back (tag);
              topcvsversions.push_back (fullv);
            }
          else
            {
              tags.push_back (tag);
              cvsversions.push_back (fullv);
            }
        }
      else
        {
            //
            // Atlas specific feature which consists in ignoring tags
            // which do not follow the <package>- pattern
            //
          if (has_package)
            {
              if (name == package)
                {
                  if (v == top)
                    {
                      toptags.push_back (tag);
                      topcvsversions.push_back (fullv);
                    }
                  else
                    {
                      tags.push_back (tag);
                      cvsversions.push_back (fullv);
                    }
                }
            }
          else
            {
              if (v == top)
                {
                  toptags.push_back (tag);
                  topcvsversions.push_back (fullv);
                }
              else
                {
                  tags.push_back (tag);
                  cvsversions.push_back (fullv);
                }
            }
        }
    }
}

//---------------------------------------------------------------------
static cmt_string get_branches (const cmt_string& module)
{
    /*
      module=$1

      branches=
      for branch in `ls "${CVSROOT}/${module}" | grep -v Attic` ; do
      if test -d "${CVSROOT}/${module}/${branch}" ; then
      if test ! -r "${CVSROOT}/${module}/${branch}/cmt/requirements,v" ; then
      if test ! -r "${CVSROOT}/${module}/${branch}/mgr/requirements,v" ; then
      if test "${branches}" = "" ; then
      branches="${branch}"
      else
      branches="${branches} ${branch}"
      fi
      fi
      fi
      fi
      done
      echo "${branches}"
    */

  cmt_string result;

  cmt_string dir;

  get_cvsroot (dir);
  dir += CmtSystem::file_separator ();
  dir += module;

  CmtSystem::cmt_string_vector files;
  CmtSystem::scan_dir (dir, files);

  for (int i = 0; i < files.size (); i++)
    {
      const cmt_string& branch = files[i];

      if (!CmtSystem::test_directory (branch)) continue;

      cmt_string t;

      CmtSystem::basename (branch, t);

      if (t == "Attic") continue;

      t = branch;
      t += CmtSystem::file_separator ();
      t += "cmt";
      t += CmtSystem::file_separator ();
      t += "requirements,v";
      if (!CmtSystem::test_file (t))
	{
	  t = branch;
	  t += CmtSystem::file_separator ();
	  t += "mgr";
	  t += CmtSystem::file_separator ();
	  t += "requirements,v";
	  
	  if (!CmtSystem::test_file (t))
	    {
	      CmtSystem::basename (branch, t);

	      if (result != "") result += " ";
	      result += t;
	    }
	}
    }

  return (result);
}

//---------------------------------------------------------------------
static cmt_string get_subpackages (const cmt_string& module)
{
    /*
      module=$1

      subpackages=
      for branch in `ls "${CVSROOT}/${module}" | grep -v Attic` ; do
      if test -d "${CVSROOT}/${module}/${branch}" ; then
      if test -r "${CVSROOT}/${module}/${branch}/cmt/requirements,v" ; then
      if test "${subpackages}" = "" ; then
      subpackages="${branch}"
      else
      subpackages="${subpackages} ${branch}"
      fi
      elif test -r "${CVSROOT}/${module}/${branch}/mgr/requirements,v" ; then
      if test "${subpackages}" = "" ; then
      subpackages="${branch}"
      else
      subpackages="${subpackages} ${branch}"
      fi
      fi
      fi
      done
      echo "${subpackages}"
    */
  cmt_string result;

  cmt_string dir;

  get_cvsroot (dir);
  dir += CmtSystem::file_separator ();
  dir += module;

  CmtSystem::cmt_string_vector files;
  CmtSystem::scan_dir (dir, files);

  for (int i = 0; i < files.size (); i++)
    {
      const cmt_string& subpackage = files[i];

      if (!CmtSystem::test_directory (subpackage)) continue;

      cmt_string t;

      CmtSystem::basename (subpackage, t);

      if (t == "Attic") continue;

      t = subpackage;
      t += CmtSystem::file_separator ();
      t += "cmt";
      t += CmtSystem::file_separator ();
      t += "requirements,v";
      if (CmtSystem::test_file (t))
	{
	  CmtSystem::basename (subpackage, t);

	  if (result != "") result += " ";
	  result += t;
	}
      else
	{
	  t = subpackage;
	  t += CmtSystem::file_separator ();
	  t += "mgr";
	  t += CmtSystem::file_separator ();
	  t += "requirements,v";
	  
	  if (CmtSystem::test_file (t))
	    {
	      CmtSystem::basename (subpackage, t);

	      if (result != "") result += " ";
	      result += t;
	    }
	}
    }

  return (result);
}

//---------------------------------------------------------------------
int main (int /*argc*/, char* argv[])
{
  {
    cmt_string uname;
    CmtSystem::get_uname (uname);
    cout << "username=" << uname << endl;
  }

  clear_cmtcvsinfos ();

  cmt_string module = argv[1];
  int pos = module.find (" ");
  if (pos != cmt_string::npos) module.erase (pos);

  module.replace (".cmtcvsinfos/", "");

  pos = module.find ("cmtcvstest");
  if (pos == 0)
    {
      module.replace ("cmtcvstest/", "");
      cmtcvstest = true;
        //cout << "cmtcvstest=true" << endl;
    }

    //cout << "module = " << module << endl;

  if (module == "")
    {
      cout << "error=syntax error" << endl;
      return (1);
    }

  cmt_string tags;
  cmt_string tags_top;
  cmt_string cvsversions;
  cmt_string cvsversions_top;

  cmt_string dir;
  get_cvsroot (dir);
  dir += CmtSystem::file_separator ();
  dir += module;

  cmt_string error;

    //cout << "dir=" << dir << endl;

  if (CmtSystem::test_directory (dir))
    {
      CmtSystem::cmt_string_vector toptag_list;
      CmtSystem::cmt_string_vector tag_list;
      CmtSystem::cmt_string_vector topcvsversion_list;
      CmtSystem::cmt_string_vector cvsversion_list;

      get_tags (module, 
                toptag_list, 
                tag_list, 
                topcvsversion_list, 
                cvsversion_list);

      int i;

      for (i = 0; i < toptag_list.size (); i++)
	{
	  const cmt_string& s = toptag_list[i];
	  const cmt_string& v = topcvsversion_list[i];
	  if (tags_top != "") tags_top += " ";
	  tags_top += s;
	  if (cvsversions_top != "") cvsversions_top += " ";
	  cvsversions_top += v;
	}

      for (i = 0; i < tag_list.size (); i++)
	{
	  const cmt_string& s = tag_list[i];
	  const cmt_string& v = cvsversion_list[i];
	  if (tags != "") tags += " ";
	  tags += s;
	  if (cvsversions != "") cvsversions += " ";
	  cvsversions += v;
	}
    }
  else
    {
      error = "### Module ";
      error += module;
      error += " not found.";
    }

  cmt_string branches;

  if (CmtSystem::test_directory (dir))
    {
      branches = get_branches (module);
    }

  cmt_string subpackages;

  if (CmtSystem::test_directory (dir))
    {
      subpackages = get_subpackages (module);
    }

  if (error != "")
    {
      cout << "error=" << error << endl;
    }

  cout << "tags_top=" << tags_top << endl;
  cout << "tags=" << tags << endl;
  cout << "cvsversions_top=" << cvsversions_top << endl;
  cout << "cvsversions=" << cvsversions << endl;
  cout << "branches=" << branches << endl;
  cout << "subpackages=" << subpackages << endl;

  exit (1);
}
