// This may look like C code, but it is really -*- C++ -*-
// RTSS --- Railway Total System Simulator
// (c) TAKAGI Ryo (at Kogakuin University)
// main.cc --- main routine
// -----
// ChangeLog:
// 2007. 11. 22
//  Bug fix of path process algorithm.
// 2007. 11. 10
//  Added the "working directory" related code. Uses "unistd.h".
// 2007. 10. 19
//  Added -o option, which does nothing.
// 2007. 10. 15
//  Preparation of new invokation method, using a control XML file.
//  Uses the "TinyXML" library.
// -----


#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <pcre.h>
#include <unistd.h>
#include <vector>
#include <sstream>

#include "tinyxml.h"
#include "rtss_path.hh"

// This file is the "main routine". Tell it to "globvar.hh".
// これで globvar.hh が main 仕様になる。
#define __GLOB_MAIN_ROUTINE

#include "kidenrt.hh"

using std :: cout ;
using std :: cerr ;
using std :: endl ;
using std :: string ;
using std :: vector ;
using std :: ostringstream ;


ostringstream l_tmp ;



// Prepare command list.
// コマンド配列関係を用意
void
readycommand
()
{
  comnum = 0 ;
  while ( comlist [ comnum ] . cmdval != END_OF_DATA )
  {
    ++ comnum ;
  }
  qsort ( comlist , comnum , sizeof ( commandlist ) , sortcommands ) ;
}


void
endc
( ostringstream & x_out )
{
  ostringstream e_os ;
  e_os << "g_file: " << g_file << endl ;
  if ( n_xml )
  {
    e_os << "n_file: " ;
    if ( is_backslash )
    {
      e_os << n_drive_str ;
    }
    e_os << n_dir_str ;
    if ( is_backslash )
    {
      e_os << "\\" ;
    }
    else
    {
      e_os << "/" ;
    }
    e_os << n_file_str << " [XML]" << endl ;
  }
  else
  {
    e_os << "n_file: " << n_file << endl ;
  }
  if ( s_xml )
  {
    e_os << "s_file: " ;
    if ( is_backslash )
    {
      e_os << s_drive_str ;
    }
    e_os << s_dir_str ;
    if ( is_backslash )
    {
      e_os << "\\" ;
    }
    else
    {
      e_os << "/" ;
    }
    e_os << s_file_str << " [XML]" << endl ;
  }
  else
  {
    e_os << "s_file: " << s_file << endl ;
  }
  e_os << "f_file: " << f_file << endl ;
  if ( p_xml )
  {
    e_os << "p_file: " ;
    if ( is_backslash )
    {
      e_os << p_drive_str ;
    }
    e_os << p_dir_str ;
    if ( is_backslash )
    {
      e_os << "\\" ;
    }
    else
    {
      e_os << "/" ;
    }
    e_os << p_file_str << " [XML]" << endl ;
  }
  else
  {
    e_os << "p_file: " << p_file << endl ;
  }
  e_os << "r_file: " << r_file << endl << "l_file: " << l_file << endl
       << "c_file: " << c_file << endl << "t_file: " << t_file << endl ;

  // -----
  // Two output streams...
  // 出力先はふたつ...
  // -----
  cout << e_os . str () ;
  x_out << e_os . str () ;
}


void
cpcpct
( char * zb ,
  char * file ,
  char * defa )
{
  strcpy ( zb , file ) ;
  strcpy ( file , defa ) ;
  strcat ( file , zb ) ;
}


void
lslash
( char * zb )
{
  char za [] = "/" ;
  strncpy ( za , ( zb + strlen ( zb ) - 1 ) , 1 ) ;
  if ( strcmp ( za , "/" ) != 0 )
    strcat ( zb , "/" ) ;
}


void
ddefa
( char * zb )
{
  lslash ( zb ) ;
  strcpy ( d_defa , zb ) ;
  cpcpct ( zb , g_file , d_defa ) ;
  cpcpct ( zb , n_file , d_defa ) ;
  cpcpct ( zb , s_file , d_defa ) ;
  cpcpct ( zb , f_file , d_defa ) ;
  cpcpct ( zb , p_file , d_defa ) ;
  cpcpct ( zb , r_file , d_defa ) ;
  cpcpct ( zb , l_file , d_defa ) ;
  cpcpct ( zb , c_file , d_defa ) ;
  cpcpct ( zb , t_file , d_defa ) ;
}


int
main
( int argc ,
  char * argv [] )
{

  // -----
  // Clearing the string in l_tmp.
  // l_tmp のなかの文字列をクリア.
  // -----
  l_tmp . str ( "" ) ;

  // -----
  // Get the "current working directory" for this program.
  // 現在のワーキングディレクトリを取得。
  // -----
  char curpath [ PATH_MAX ] ;

  if ( getcwd ( curpath , PATH_MAX ) == NULL )
  {
    cerr << "Error: Getting present working directory failed" << endl ;
    exit ( 1 ) ;
  }
  else
  {
    l_tmp << "CWD: " << curpath << endl ;
  }
  current_directory = curpath ;
  current_drive = "" ;

  // -----
  // Check if the directory is "Windows" style. If windows, then separate the
  // drive name and the file path.
  // ディレクトリ名がウィンドウズスタイルかどうか判定。ウィンドウズなら，ドラ
  // イブ名とディレクトリ部を分離。
  // -----
  RTSS_Path_String check_win_path ( curpath ) ;
  is_backslash = check_win_path . isWindowsStyleFullPath ( current_drive ) ;
  if ( is_backslash )
  {
    current_directory = check_win_path ;
    l_tmp << "Windows path, dir = " << current_directory << ", drive = "
          << current_drive << "!" << endl ;
  }
  else
  {
    l_tmp << "Unix path, dir = " << current_directory << ", drive = "
          << current_drive << "!" << endl ;
  }

  char za [] = "-g" ;
  char zb [ FILENAME_MAX ] ;

  // -----
  // Prepare the command table.
  // コマンド配列を準備
  // -----
  readycommand () ;

  if ( argc == 1 )
    goto theend ;

  // New invocation style. One argument interpreted as control XML file.
  // XMLファイルによる初期化。
  if ( argc == 2 )
  {
    // -----
    // Loading file.
    // ファイルロード。
    // -----
    RTSS_Path_String control_file ( * ++ argv ) ;
    // -----
    // Filename check (2 and thereafter) ... all processed here.
    // ファイル名チェック(2 以降)… すべてここで.
    // -----
    string controlfiledrive = "" ;
    string controlfiledir = "" ;
    string controlfilename = "" ;
    if ( ! control_file . processPath
         ( is_backslash , current_drive , current_directory ,
           controlfiledrive , controlfiledir , controlfilename ) )
    {
      cerr << l_tmp . str ()
           << "ERROR: unknown error in processPath (control file)" << endl ;
      exit ( 1 ) ;
    }
    current_drive = controlfiledrive ;
    current_directory = controlfiledir ;
    string s_control_filename = controlfiledrive ;
    s_control_filename += controlfiledir ;
    if ( is_backslash )
    {
      s_control_filename += "\\" ;
    }
    else
    {
      s_control_filename += "/" ;
    }
    s_control_filename += controlfilename ;
    l_tmp << "Control file: " << s_control_filename << endl << " dir = "
          << current_directory << ", drive = " << current_drive << endl ;
    TiXmlDocument doc ( s_control_filename ) ;
    bool loadOkay = doc . LoadFile () ;
    if ( ! loadOkay )
    {
      cerr << l_tmp . str ()
           << "Failed to load file \"" << * argv << "\"." << endl ;
      exit ( 1 ) ;
    }

    // -----
    // The file must have one "root" element of tag <rtss_controls>.
    // If multiple <rtss_controls> tags exist, only the first one is read.
    // このXMLファイルには <rtss_controls> というタグがなければならない。
    // <rtss_controls> タグが複数あれば最初のだけを読む。
    // -----
    const TiXmlNode * doc_ch = doc . FirstChild () ;
    while ( doc_ch && doc_ch -> Type () != TiXmlNode :: TINYXML_ELEMENT )
    {
      doc_ch = doc_ch -> NextSibling () ;
    }
    if ( ! doc_ch
         || doc_ch -> Type () != TiXmlNode :: TINYXML_ELEMENT
         || string ( doc_ch -> Value () ) != "rtss_controls" )
    {
      cerr << l_tmp . str () << "Failed to find element \"rtss_controls\""
           << " in file \"" << * argv << "\"." << endl ;
      exit ( 1 ) ;
    }

    // -----
    // Getting child element(s) of tag <rtss_controls>.
    // All the child elements under this tag must be of tag <rtssfile>.
    // <rtss_controls> の子を取得. <rtssfile> タグでなければならない.
    // -----
    for ( const TiXmlNode * doc_cc = doc_ch -> FirstChild () ;
          doc_cc ; doc_cc = doc_cc -> NextSibling () )
    {
      // -----
      // Ignore non-Element node.
      // エレメント以外は無視。
      // -----
      if ( doc_cc -> Type () != TiXmlNode :: TINYXML_ELEMENT )
        continue ;
      // -----
      // Error if not <rtssfile>.
      // <rtssfile> タグでなければエラー。
      // -----
      if ( string ( doc_cc -> Value () ) != "rtssfile" )
      {
        cerr << l_tmp . str ()
             << "\"" << doc_cc -> Value () << "\" tag not allowed." << endl ;
        exit ( 1 ) ;
      }
      // -----
      // Process attributes.
      // 属性の解析。
      // -----
      const TiXmlAttribute * attr
        = doc_cc -> ToElement () -> FirstAttribute () ;
      enum EH_FileType {
        G_FILE , N_FILE , S_FILE , F_FILE , P_FILE , R_FILE , L_FILE ,
        C_FILE , H_FILE , T_FILE , UNKNOWN
      } en_ftype = UNKNOWN ;
      bool sw_ftype = false ;
      bool sw_name = false ;
      string filename ;
      string filedir ;
      string filedrive ;
      int id ;
      double dtime ;
      bool is_xml = false ;
      while ( attr )
      {
        if ( string ( attr -> Name () ) == "type" )
        {
          // Attribute "type"
          if ( sw_ftype )
          {
            // Strange ... two attributes of same name?
            // 同じ属性が2つある？... エラー
            cerr << l_tmp . str ()
                 << "Error: two attributes of same name?" << endl ;
            exit ( 1 ) ;
          }
          string filetype_str = attr -> ValueStr () ;
          if ( filetype_str == "F" || filetype_str == "f" )
          {
            en_ftype = F_FILE ;
          }
          else if ( filetype_str == "G" || filetype_str == "g" )
          {
            en_ftype = G_FILE ;
          }
          else if ( filetype_str == "N" || filetype_str == "n" )
          {
            en_ftype = N_FILE ;
          }
          else if ( filetype_str == "S" || filetype_str == "s" )
          {
            en_ftype = S_FILE ;
          }
          else if ( filetype_str == "P" || filetype_str == "p" )
          {
            en_ftype = P_FILE ;
          }
          else if ( filetype_str == "R" || filetype_str == "r" )
          {
            en_ftype = R_FILE ;
          }
          else if ( filetype_str == "L" || filetype_str == "l" )
          {
            en_ftype = L_FILE ;
          }
          else if ( filetype_str == "C" || filetype_str == "c" )
          {
            en_ftype = C_FILE ;
          }
          else if ( filetype_str == "H" || filetype_str == "h" )
          {
            en_ftype = H_FILE ;
          }
          else if ( filetype_str == "T" || filetype_str == "t" )
          {
            en_ftype = T_FILE ;
          }
          else
          {
            cerr << l_tmp . str ()
                 << "Error: <rtssfile>: wrong file type." << endl ;
            exit ( 1 ) ;
          }
          sw_ftype = true ;
        }
        else if ( string ( attr -> Name () ) == "name" )
        {
          // Attribute "name"
          if ( sw_name )
          {
            // -----
            // Strange ... two attributes of same name?
            // 同じ属性が2つある？... エラー
            // -----
            cerr << l_tmp . str ()
                 << "Error: two attributes of same name?" << endl ;
            exit ( 2 ) ;
          }
          filename = string ( attr -> Value () ) ;
          sw_name = true ;

          // -----
          // Filename check (1) ... if it ends with ".xml", then it is
          // assumed as an XML file (new file format).
          // ファイル名チェック(1)… もし ".xml" で終わる名称なら XML ファ
          // イル（新しいファイルフォーマット）と仮定する。
          // -----
          RTSS_Path_String path_fn = filename ;
          is_xml = path_fn . hasXmlExtension () ;

          // -----
          // Filename check (2 and thereafter) ... all processed here.
          // ファイル名チェック(2 以降)… すべてここで.
          // -----
          if ( ! path_fn . processPath
               ( is_backslash , current_drive , current_directory ,
                 filedrive , filedir , filename ) )
          {
            cerr << l_tmp . str ()
                 << "ERROR: unknown error in processPath" << endl ;
            exit ( 1 ) ;
          }
        }
        else
        {
          cerr << l_tmp . str ()
               << "ERROR: tag <rtssfile> with incorrect attribute "
               << attr -> Name () << endl ;
          exit ( 1 ) ;
        }
        attr = attr -> Next () ;
      }
      if ( ! ( sw_name && sw_ftype ) )
      {
        cerr << l_tmp . str ()
             << "Error: <rtssfile> tag not correct." << endl ;
        exit ( 1 ) ;
      }
      // -----
      // Setting filename to strings. Only s_file supports XML.
      // filename を各変数に格納. XML対応は s_file のみ.
      // -----
      string s_copy_filename = filedrive ;
      s_copy_filename += filedir ;
      if ( is_backslash )
      {
        s_copy_filename += "\\" ;
      }
      else
      {
        s_copy_filename += "/" ;
      }
      s_copy_filename += filename ;
      switch ( en_ftype )
      {
      case G_FILE :
        strcpy ( g_file , s_copy_filename . c_str () ) ;
        break ;
      case N_FILE :
        if ( n_xml = is_xml )
        {
          n_file_str = filename ;
          n_dir_str = filedir ;
          n_drive_str = filedrive ;
        }
        else
        {
          strcpy ( n_file , s_copy_filename . c_str () ) ;
        }
        break ;
      case S_FILE :
        if ( s_xml = is_xml )
        {
          s_file_str = filename ;
          s_dir_str = filedir ;
          s_drive_str = filedrive ;
        }
        else
        {
          strcpy ( s_file , s_copy_filename . c_str () ) ;
        }
        break ;
      case F_FILE :
        strcpy ( f_file , s_copy_filename . c_str () ) ;
        break ;
      case P_FILE :
        if ( p_xml = is_xml )
        {
          p_file_str = filename ;
          p_dir_str = filedir ;
          p_drive_str = filedrive ;
        }
        else
        {
          strcpy ( p_file , s_copy_filename . c_str () ) ;
        }
        break ;
      case R_FILE :
        strcpy ( r_file , s_copy_filename . c_str () ) ;
        break ;
      case L_FILE :
        strcpy ( l_file , s_copy_filename . c_str () ) ;
        break ;
      case C_FILE :
        use_c_file = true ;
        strcpy ( c_file , s_copy_filename . c_str () ) ;
        break ;
      case H_FILE :
        strcpy ( h_file , s_copy_filename . c_str () ) ;
        break ;
      case T_FILE :
        strcpy ( t_file , s_copy_filename . c_str () ) ;
        break ;
      default :
        cerr << l_tmp . str ()
             << "Error: unknown file type in <rtssfile> found." << endl ;
        exit ( 1 ) ;
      }
    }
    goto theend ;
  }

  while ( -- argc > 0 )
  {
    if ( strcmp ( * ++ argv , "-o" ) == 0 )
    {
      // New option: -o ... just skip this. This option is used when
      // you want to specify only one option in old style. If there is only
      // one command-line option (i.e. argc == 2), the program now assumes
      // that this is the filename of an XML control file.
      // 新しいオプション: -o ... 読み飛ばすだけ。このオプションは，古い
      // スタイルのオプションを1つだけ指定するときに用いる。もしオプショ
      // ンが1つしかコマンドラインにない（つまり argc == 2）と，プログラ
      // ムは当該オプションがXMLコントロールファイルのファイル名だと解し
      // てしまう。
      continue ;
    }

    if ( strcmp ( * argv , "-d" ) == 0 || strcmp ( * argv , "-g" ) == 0
	 || strcmp ( * argv , "-n" ) == 0 || strcmp ( * argv , "-s" ) == 0
	 || strcmp ( * argv , "-f" ) == 0 || strcmp ( * argv , "-p" ) == 0
	 || strcmp ( * argv , "-r" ) == 0 || strcmp ( * argv , "-l" ) == 0
	 || strcmp ( * argv , "-h" ) == 0 || strcmp ( * argv , "-c" ) == 0 
	 || strcmp ( * argv , "-t" ) == 0 )
    {
      if ( -- argc > 0 )
      {
	strcpy ( za , * argv ) ;
	strcpy ( zb , * ++ argv ) ;
      }
      else
      {
        cerr << l_tmp . str ()
             << "Error: Incomplete option \"" << * argv << "\"" << endl ;
	exit ( 3 ) ;
      }
    }
    else
    {
      strncpy ( za , * argv , 2 ) ;
      strcpy ( zb , * argv + 2 ) ;
    }
    if ( strcmp ( za , "-d" ) == 0 ) ddefa ( zb ) ;
    if ( strcmp ( za , "-g" ) == 0 ) strcpy ( g_file , zb ) ;
    if ( strcmp ( za , "-n" ) == 0 ) strcpy ( n_file , zb ) ;
    if ( strcmp ( za , "-s" ) == 0 ) strcpy ( s_file , zb ) ;
    if ( strcmp ( za , "-f" ) == 0 ) strcpy ( f_file , zb ) ;
    if ( strcmp ( za , "-p" ) == 0 ) strcpy ( p_file , zb ) ;
    if ( strcmp ( za , "-r" ) == 0 ) strcpy ( r_file , zb ) ;
    if ( strcmp ( za , "-l" ) == 0 ) strcpy ( l_file , zb ) ;
    if ( strcmp ( za , "-c" ) == 0 ) {
      use_c_file = true ;
      strcpy ( c_file , zb ) ;
    }
    if ( strcmp ( za , "-h" ) == 0 ) strcpy ( h_file , zb ) ;
    if ( strcmp ( za , "-t" ) == 0 ) strcpy ( t_file , zb ) ;
  }

 theend:
  endc ( l_tmp ) ;
  l_ofs . open ( l_file ) ;
  if ( l_ofs . fail () )
  {
    cerr << l_tmp . str ()
         << "Error: Log file \"" << l_file << "\" open failed." << endl ;
    exit ( 1 ) ;
  }
  l_ofs << l_tmp . str () ;
  feeder fr ;
  fr . simulation () ;
  exit ( 0 ) ;
}
