// =====================================================================
//  $Id: TCommandTable.cc,v 1.4 2004/03/07 10:30:28 goiwai Exp $
//  $Name: CLDAQ-1-14-03 $
//  $Log: TCommandTable.cc,v $
//  Revision 1.4  2004/03/07 10:30:28  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.3  2003/10/06 17:02:37  goiwai
//  *** empty log message ***
//
//  Revision 1.2  2003/07/30 16:17:31  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TCommandTable.hh"
#include "TCommand.hh"

TCommandTable::TCommandTable()
  : Tvector<TCommandSpecified>()
{;}

TCommandTable::~TCommandTable()
{;}

TCommand* TCommandTable::FindCommand( const Tstring& fullname ) const
{
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    if ( (*this)[ i ].GetFullName() == fullname ) {
      return (*this)[ i ].GetCommand();
    }
  }
  return 0;
}

TCommand* TCommandTable::FindCommand( const Tstring& name, const Tstring& path ) const
{
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    if ( (*this)[ i ].GetName() == name && (*this)[ i ].GetAbsolutePath() == path ) {
      return (*this)[ i ].GetCommand();
    }
  }
  return 0;
}

Tbool TCommandTable::AlreadyExist( TCommand* command ) const
{
  Tstring fullname = command -> GetCommandName();
  return AlreadyExist( fullname );
}

Tbool TCommandTable::AlreadyExist( const Tstring& fullname ) const
{
  if ( FindCommand( fullname ) ) {
    return Ttrue;
  } else {
    return Tfalse;
  }
}

Tbool TCommandTable::AlreadyExist( const Tstring& name, const Tstring& path ) const
{
  if ( FindCommand( name, path ) ) {
    return Ttrue;
  } else {
    return Tfalse;
  }
}

Tbool TCommandTable::AlreadyExistDirectory( const Tstring& path ) const
{
  if ( path == "/" ) {
    return Ttrue;
  }

  Tstring newpath;
  Tstring name;

  Tstring strbuf = path;

  Tsize_t pos = strbuf.rfind( '/' );
  if ( pos == 0 ) {
    newpath = strbuf[ 0 ];
    name = strbuf.substr( 1, strbuf.size() - 1 );
  } else if ( pos > 0 && pos < strbuf.size() ) {
    newpath = strbuf.substr( 0, pos );
    name = strbuf.substr( pos + 1, strbuf.size() - pos );
  } else {
    return Tfalse;
  }
  
  return AlreadyExistDirectory( name, newpath );
}

Tbool TCommandTable::AlreadyExistDirectory( const Tstring& name, const Tstring& path ) const
{
  TstringList dirlist = GetDirectoryList( path );
  Tstring dir;
  if ( name[ name.size() - 1 ] != '/' ) {
    dir = name + "/";
  }
  for ( Tsize_t i = 0; i < dirlist.size(); i ++ ) {
    if ( dirlist[ i ] == dir ) {
      return Ttrue;
    }
  }
  return Tfalse;
}

Tvoid TCommandTable::AddCommand( TCommand* command )
{
  push_back( convert( command ) );
  return;
}
 
TCommandSpecified TCommandTable::convert( TCommand* command ) const
{
  Tstring fullname = command -> GetCommandName();

  Tint nslash = 0;
  for ( Tsize_t i = 0; i < fullname.size(); i ++ ) {
    if ( fullname[ i ] == '/' ) {
      nslash ++;
    }
  }

  Tint depth;
  Tbool flag;
  Tstring name;
  Tstring path;
  if ( nslash > 0 ) {
    flag = Tfalse;
    depth = nslash - 1;
    Tsize_t slashpos = fullname.rfind( '/' );
    path = fullname.substr( 0, slashpos );
    name = fullname.substr( slashpos + 1, fullname.size() - slashpos - 1 );
  } else {
    flag = Ttrue;
    depth = TCommandSpecified::tBuiltinDepth;
    name = fullname;
    path = "";
  }

  return TCommandSpecified( name, fullname, path, depth, flag, command );
}

TstringList TCommandTable::GetCommandList( const Tstring& path ) const
{
  TstringList strlist;
  Tstring strbuf;

  //  カレントのコマンドだけを集める
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    if ( (*this)[ i ].GetAbsolutePath() == path ) {
      strbuf = (*this)[ i ].GetName();
      if ( (*this)[ i ].IsAliasedCommand() ) {
	strbuf += "@";
      } else {
	strbuf += "*";
      }
      strlist.push_back( strbuf );
    }
  }
  
  return strlist;
}

TstringList TCommandTable::GetDirectoryList( const Tstring& path ) const
{
  TstringList strlist;
  Tstring strbuf;
  TstringList unique;

  // まずユニークなパスだけを集める
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    Tbool hasalready = Tfalse;
    for ( Tsize_t j = 0; j < unique.size(); j ++ ) {
      if ( unique[ j ] == (*this)[ i ].GetAbsolutePath() ) {
	hasalready = Ttrue;
	break;
       }
    }
    if ( hasalready == Tfalse ) {
      unique.push_back( (*this)[ i ].GetAbsolutePath() );
    }
  }



  // 先頭がパスと一致したらば,先頭を消してけつにスラつけてプッシュ
  for ( Tsize_t i = 0; i < unique.size(); i ++ ) {
    strbuf = unique[ i ];
    if ( strbuf.substr( 0, path.size() ) == path ) {

      if ( path != "/" && strbuf.size() > path.size() && strbuf[ path.size() ] != '/' ) {
	continue;
      }

      strbuf.erase( 0, path.size() );

      if ( strbuf != "/" && strbuf[ 0 ] == '/' ) {
	strbuf.erase( 0, 1 );
      }

      Tsize_t pos = strbuf.find( '/' );
      if ( pos < strbuf.size() ) {
	strbuf.erase( pos, strbuf.size() - pos );
      }

      if ( ! strbuf.empty() ) {
	strbuf += "/";
	strlist.push_back( strbuf );
      }

    }
  }


  // もういっちょユニークなものだけにする
  TstringList newstrlist;
  for ( Tsize_t i = 0; i < strlist.size(); i ++ ) {
    Tbool hasalready = Tfalse;
    for ( Tsize_t j = 0; j < strlist.size(); j ++ ) {
      if ( i != j && strlist[ i ] == strlist[ j ] ) {
	hasalready = Ttrue;
	break;
      }
    }
    if ( hasalready == Ttrue ) {
      strlist.erase( strlist.begin() + i );
    }
  }
  
  return strlist;
}

TstringList TCommandTable::Sort( const Tstring& path ) const
{
  TstringList comlist = GetCommandList( path );
  TstringList dirlist = GetDirectoryList( path );
  for ( Tsize_t i = 0; i < dirlist.size(); i ++ ) {
    comlist.push_back( dirlist[ i ] );
  }
  return Sort( comlist );
}

TstringList TCommandTable::Sort( const TstringList& sort ) const
{
  TstringList sorted = sort;
  Tsize_t sortlen = sorted.size();
  if ( sortlen > 1 ) {
    Tbool sortcomplete = Tfalse;
    Tstring strbuf;
    do {
      for ( Tsize_t i = 0; i < sortlen - 1; i ++ ) {
	if ( sorted[ i ] > sorted[ i + 1 ] ) {
	  strbuf = sorted[ i + 1 ];
	  sorted[ i + 1 ] = sorted[ i ];
	  sorted[ i ] = strbuf;
	}
	Tbool result = Ttrue;
	for ( Tsize_t j = 0; j < sortlen - 1; j ++ ) {
	  result &= ( sorted[ j ] < sorted[ j + 1 ] );
	}
	sortcomplete = result;
      }
    } while ( sortcomplete != Ttrue );
  }

  return sorted;
}

Tvoid TCommandTable::List( Tint column, const Tstring& path ) const
{
  TstringList comlist = GetCommandList( path );
  TstringList dirlist = GetDirectoryList( path );
  for ( Tsize_t i = 0; i < dirlist.size(); i ++ ) {
    comlist.push_back( dirlist[ i ] );
  }
  List( column, comlist );
  return;
}

Tvoid TCommandTable::List( Tint column, const TstringList& strlist ) const
{
  Tsize_t maxlen = 0;
  TintList lenlist;
  TintList lastlenlist;
  Tint totallen = 0;

  Tint ncol = 0;
  Tint nrow = 0;

  for ( Tsize_t ncolid = strlist.size(); ncolid > 0; ncolid -- ) {
    lastlenlist = lenlist;
    lenlist.clear();
    totallen = 0;
    for ( Tsize_t i = 0; i < strlist.size(); i ++ ) {
      Tsize_t collen = strlist[ i ].size();
      if ( maxlen < collen ) {
	maxlen = collen;
      }
	
      if ( strlist.size() % ncolid == 0 ) {
	Tint tmp = strlist.size() / ncolid;
	if ( ( i + 1 ) % tmp == 0 ) {
	  lenlist.push_back( maxlen );
	  maxlen = 0;
	}
      } else {
	Tint tmp = strlist.size() / ncolid + 1;
	if ( ( i + 1 ) % tmp == 0 ) {
	  lenlist.push_back( maxlen );
	  maxlen = 0;
	} else if ( i == strlist.size() - 1 ) {
	  lenlist.push_back( maxlen );
	  maxlen = 0;
	}
      }
    }
      

    for ( Tsize_t i = 0; i < lenlist.size(); i ++ ) {
      totallen += lenlist[ i ];
      if ( i != lenlist.size() - 1 ) {
	totallen += 2;
      }
    }

    if ( totallen < column ) {
      ncol = ncolid;
      break;
    }

  }

  for ( Tsize_t i = 0; i < lenlist.size(); i ++ ) {
    if ( i != lenlist.size() - 1 ) {
      lenlist[ i ] += 2;
    }
  }


  ncol = lenlist.size();
  if ( strlist.size() % ncol == 0 ) {
    nrow = strlist.size() / ncol;
  } else {
    nrow = strlist.size() / ncol;
    nrow += 1;
  }


  for ( Tint row = 0; row < nrow; row ++ ) {
    for ( Tint col = 0; col < ncol; col ++ ) {
      Tint comid = nrow * col;
      comid += row;
      if ( comid >= (Tint)strlist.size() ) {
	break;
      }
      const Tchar* cbuf = strlist[ comid ].c_str();
      Tcout.setf( Tios::left );
      Tcout << setw( lenlist[ col ] ) << cbuf;
    }
    Tcout << Tendl;
  }

}

Tvoid TCommandTable::RemoveCommand( Tint index )
{
  erase( begin() + index );
  return;
}

Tint TCommandTable::GetSize() const
{
  return (Tint)size();
}

Tvoid TCommandTable::Clear()
{
  clear();
  return;
}

TCommandSpecified TCommandTable::GetCommandSpecified( Tint index ) const
{
  TCommandSpecified spec;
  static const Tstring head = "TCommandTable::GetCommandSpecified: ";
  if ( index < 0 || index >= (Tint)size() ) {
    Tcerr << head << "invalid index " << index << "." << Tendl;
    return spec;
  }
  spec = ( *this )[ index ];
  return spec;
}

TCommandSpecified TCommandTable::GetCommandSpecified( const Tstring& fullname ) const
{
  TCommandSpecified spec;
  TCommand* command = FindCommand( fullname );
  if ( command ) {
    return convert( command );
  }
  return spec;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TCommandTable)
#endif
