// This may look like C code, but it is really -*- C++ -*-
// RTSS --- Railway Total System Simulator
// (c) TAKAGI Ryo (at Kogakuin University)
// nextsta.cc --- functions for class nextsta
// -----
// ChangeLog:
// 2007. 11. 21
//  Redirected log messages to l_file.
// -----

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>

#include "matrix.hh"
#include "enumerat.hh"
#include "nextsta.hh"
#include "train.hh"
#include "StrPrintf.hh"

//#define DEBUG



nextsta :: nextsta
()
  : gradcrv () ,
    greq_gnum () ,
    greq_renposf () ,
    greq_gzpos () ,
    _soc_name ()
{
  nextsw = prt = bufcont = pfcont = 0.0 ;
  greq_x = nreq_kz = nreq_kx = pfi = 0 ;

  // isnextsw added 1997.10.6 to work around bug in
  // checking of twhole in dpt-rf.cc --- RT
  isnextsw = setprt = sw_nreq = ontprec = false ;
  nxx = 0 ;
  x_stnobj = 0 ;
  _soc = 0 ;
  _wants_soc = false ;
}



// constructor: X(X&)
nextsta :: nextsta
( nextsta & x )
  : gradcrv ( x ) ,
    greq_gnum () ,
    greq_renposf () ,
    greq_gzpos ()
{
  direc = x . direc ;
  startp = x . startp ;
  arp = x . arp ;
  arv = x . arv ;
  art = x . art ;
  nfline = x . nfline ;
  nxp = x . nxp ;
  tstop = x . tstop ;
  vnoff = x . vnoff ;
  ndpt = x . ndpt ;
  ndp = x . ndp ;
  nxx = x . nxx ;
  dcmd = x . dcmd ;
  ontprec = x . ontprec ;

  // isnextsw added 1997.10.6 to work around bug in
  // checking of twhole in dpt-rf.cc --- RT
  nextsw = x . nextsw ;
  isnextsw = x . isnextsw ;
  prt = bufcont = pfcont = 0.0 ;
  greq_x = nreq_kz = nreq_kx = pfi = 0 ;
  setprt = sw_nreq = false ;

  _soc = x . _soc ;
  _wants_soc = x . _wants_soc ;
  _soc_name = x . _soc_name ;
}



// ８変数の代入ないしは初期化
void
nextsta :: setvar
( double stp ,
  int drec ,
  double app ,
  double avv ,
  double att ,
  int nfl ,
  double tst ,
  double vno )
{
  startp = stp ;
  direc = drec ;
  arp = app ;
  arv = avv ;
  art = att ;
  nfline = nfl ;
  tstop = tst ;
  vnoff = vno ;
}



// 代入用演算子のオーバーロード
nextsta &
nextsta :: operator=
( const nextsta & x )
{
  if ( & x == this )
    return * this ;
  direc = x . direc ;
  startp = x . startp ;
  arp = x . arp ;
  arv = x . arv ;
  art = x . art ;
  nfline = x . nfline ;
  nxp = x . nxp ;
  tstop = x . tstop ;
  vnoff = x . vnoff ;
  ndpt = x . ndpt ;
  ndp = x . ndp ;
  nxx = x . nxx ;
  dcmd = x . dcmd ;
  ontprec = x . ontprec ;
  nextsw = x . nextsw ;
  isnextsw = x . isnextsw ;
  bufcont = x . bufcont ;
  pfcont = x . pfcont ;
  prt = x . prt ;
  pfi = x . pfi ;
  setprt = x . setprt ;
  x_stnobj = x . x_stnobj ;

  _soc = x . _soc ;
  _wants_soc = x . _wants_soc ;
  _soc_name = x . _soc_name ;

  gradcrv :: operator= ( gradcrv ( x ) ) ;

  return * this ;
}



// 代入演算子
nextsta &
nextsta :: operator=
( const gradcrv & x )
{
  if ( & x == this )
    return * this ;
  gradcrv :: operator= ( x ) ;
  return * this ;
}



// 勾配データの配列上の現在位置
nextsta :: gc_size_type
nextsta :: dpgrad
( double pos )
  const
{
  gc_size_type i = dqgrad () ;
  while ( spoint ( i ) * direc > pos * direc )
    bpgrad ( i ) ;
  return i ;
}



// ----
// Stepping forward in gcvel vector.
// gcvel ベクトルにおける1個前進.
// ----
bool
nextsta :: fpgrad
( nextsta :: gc_size_type & zz )
  const
{
  if ( direc > 0 && zz < gc_size () - 1
       || direc < 0 && zz > 0 )
  {
    zz += direc ;
    return false ;
  }
  else
  {
    return true ;
  }
}



// ----
// Stepping backward in gcvel vector.
// gcvel ベクトルにおける1個後進.
// ----
bool
nextsta :: bpgrad
( nextsta :: gc_size_type & zz )
  const
{
  if ( direc > 0 && zz > 0
       || direc < 0 && zz < gc_size () - 1 )
  {
    zz -= direc ;
    return false ;
  }
  else
  {
    return true ;
  }
}


// 勾配データ配列上の現在位置合わせ
void
nextsta :: adjpgrad
( nextsta :: gc_size_type & jgrad ,
  double pos )
  const
{
  chkdirec () ;
  while ( spoint ( jgrad ) * direc > pos * direc )
  {
    if ( bpgrad ( jgrad ) )
    {
      break ;
    }
    if ( jgrad < 0 )
    {
      jgrad = 0 ;
      break ;
    }
    if ( jgrad >= gc_size () )
    {
      jgrad = gc_size () - 1 ;
      break ;
    }
  }
  while ( epoint ( jgrad ) * direc <= pos * direc )
  {
    if ( fpgrad ( jgrad ) )
    {
      break ;
    }
    if ( jgrad < 0 )
    {
      jgrad = 0 ;
      break ;
    }
    if ( jgrad >= gc_size () )
    {
      jgrad = gc_size () - 1 ;
      break ;
    }
  }
}



// 到着時刻
double
nextsta :: tarrive
( int pfirst )
  const
{
  if ( dcmd == No_comm )
    return tarrive () ;
  if ( dcmd == Delay_yes )
    if ( pfirst > pfi )
      return tarrive () ;
    else
      return tarrive () - pfcont ;
  if ( pfirst > pfi )
    return tarrive () + bufcont ;
  else
    return tarrive () + bufcont - pfcont ;
}



// 到着時刻
double
nextsta :: tarrive
( bool x )
  const
{
  if ( dcmd == Buffer_yes )
    return tarrive () + bufcont ;
  else
    return tarrive () ;
}



// greq_* の中身を返す
bool
nextsta :: get_greq
( gc_size_type & gr_gnum ,
  int & gr_renposf ,
  double & gr_gzpos )
{
  if ( greq_x < greq_gnum . size () )
  {
    gr_gnum = greq_gnum [ greq_x ] ;
    gr_renposf = greq_renposf [ greq_x ] ;
    gr_gzpos = greq_gzpos [ greq_x ] ;
    ++ greq_x ;
    l_ofs << StrPrintf ( "gradcrv %d %d %10.4f" ,
                         gr_gnum , gr_renposf , gr_gzpos ) << endl ;
    return true ;
  }
  else
  {
    l_ofs << StrPrintf ( "gradcrv end." ) << endl ;
    return false ;
  }
}



// 速度制限を返す関数
double
nextsta :: vlimit
( nextsta :: gc_size_type pgrad )
  const
{
  double vl = getElement ( pgrad ) . vlimit ;
  if ( vl < INVLIM )
    return vl ;
  else
    return vl - g_vlimitmikomi ;	// 見込みを入れて低めに走る
}



// Station_pass, Pass_pass の nextsta_pattern において arv を変更
void
nextsta :: arv_pass
()
{
  // この場合以外関係ない
  if ( nxp != Station_pass && nxp != Pass_pass )
    return ;
  if ( arv < INVLIM )
    arv = nxx -> vlimit ( nxx -> dsgrad () ) ;
  else if ( arv > nxx -> vlimit ( nxx -> dsgrad () ) )
    arv = nxx -> vlimit ( nxx -> dsgrad () ) ;
}



// 遅れるべき時間を返す
double
nextsta :: tarr_delay
( const train * trptr_arg )
  const
{
  if ( g_sw_station_object_valid && g_sw_disttrain_ended )
  {
    return x_stnobj -> tarr_delay ( trptr_arg ) ;
  }
  else
    return 0.0 ;
}



// to_station オブジェクトを station_obj にたたきこむ
// 初期化時なので時隔計算を同時には行わない
void
nextsta :: push_to_station_init
( to_station emitted )
{
  if ( g_sw_station_object_valid )
  {
#ifdef DEBUG
    cerr << "push_to_station_init: pushing emitted, Car No. " ;
    cerr << emitted . tr_ptr -> car_number () << ", stnobj.get_stnno (): " ;
    cerr << x_stnobj -> get_stnno () << "\n" ;
#endif
    ( * x_stnobj ) += emitted ;
#ifdef DEBUG
    cerr << "push_to_station_init: pushed emitted, Car No. " ;
    cerr << ( * x_stnobj ) [ x_stnobj -> number () - 1 ] . tr_ptr
      -> car_number () ;
    cerr << ", stnobj.get_stnno (): " ;
    cerr << x_stnobj -> get_stnno () << "\n" ;
#endif
  }
}



// from_station オブジェクトを station_obj にたたきこむ
// 初期化時なので時隔計算を同時には行わない
void
nextsta :: push_from_station_init
( from_station emitted )
{
  if ( g_sw_station_object_valid )
  {
#ifdef DEBUG
    cerr << "push_from_station_init: pushing emitted, Car No. " ;
    cerr << emitted . tr_ptr -> car_number () << ", stnobj.get_stnno(): " ;
    cerr << x_stnobj -> get_stnno () << "\n" ;
#endif
    x_stnobj -> from_station_init ( emitted ) ;
#ifdef DEBUG
    cerr << "push_from_station_init: pushed emitted, Car No. " ;
    cerr << x_stnobj -> train_departure [ 0 ] . tr_ptr -> car_number () ;
    cerr << ", stnobj.get_stnno(): " ;
    cerr << x_stnobj -> get_stnno () << "\n" ;
#endif
  }
}



// to_station オブジェクトを station_obj にたたきこむ
// 初期化時でないので時隔計算を同時に行う
void
nextsta :: push_to_station
( to_station emitted )
{
  if ( g_sw_station_object_valid )
  {
    push_to_station_init ( emitted ) ;
    x_stnobj -> headway_cal () ;
  }
}



// to_station オブジェクトに定義されている out_of_station を返す
double
nextsta :: out_of_station
()
  const
{
  return stnobj () -> outofstn_pos () * direction () ;
}
