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

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

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

#define INVLIM 1e-5

//#define TIMEPRINT
//#define PRATEBUG

//#define DEBUG

// ブレーキ中の次位置演算
void
train :: nm_brake
( double tracff )
{
  // ----
  // NOTE: 2010. 1. 20 -- emrn no longer used.
  // ----
  // tracff *= emrn ;

  enableMechanicalPowerCalculation () ;
  nposb ( tracff ) ;	// 次位置演算
  disableMechanicalPowerCalculation () ;

  if ( g_sw_station_object_valid && g_sw_disttrain_ended )
  {
    if ( pos * direc ()
         >= nxx -> parrive () * direc () - nxx -> out_of_station () )
    {
      // 駅間停止位置を過ぎたと認定
      sw_x_btws = false ;
    }
  }

  bool sw_last_brake = false ;	// 次駅データ終了条件を示す

  if ( brakev < - INVLIM )
  {
    // ontime の条件を満たすときブレーキ終了

    double ontx = ontimesub ( pos , pgrad , vel , t_sim ) ;

    double x_tard = 0.0 ;	// 到着遅れ時分の期待値
    if ( g_sw_station_object_valid && g_sw_disttrain_ended )
    {
      // 信号系有効時のみチェック
      // **** nxx -> tarr_delay ( this ) : 信号系無効時は zero なのだが
      x_tard = nxx -> tarr_delay ( this ) ;	// これが通常の話
      if ( g_sw_stop_between_oriented && sw_x_btws )
      {
        // 駅間停止指向で駅間停止位置を過ぎていない場合
        x_tard = 0.0 ;	// 遅れなしとしてパターンを生成
      }
    }

    if ( ontx - tdept + taudx >= nxx -> tarrive ( pfirst ) + x_tard )
    {
      iamconst = false ;
      iamcoast = true ;
      if ( tresist ( vel , pgrad ) > 0 || vel < nxx -> vlimit ( pgrad )
           || nxx -> vlimit ( pgrad ) < INVLIM )
      {
        // ontime 運転条件
        stat = Coast ;
        rnotchold = rnotch = 0.0 ;
      }
      else
      {
        stat = Const_vel ;
      }
    }
    return ;
  }

  if ( g_sw_station_object_valid && g_sw_disttrain_ended )
  {
    // 信号系有効時のみチェック

    if ( sw_btws && ( ! nxx -> stnobj () -> voie_occupied ( this ) ) )
    {
      // 駅間停止のためのブレーキの必要性がなくなった
      // →駅間走行時分を信号系の要求に合わせる
      sw_btws = false ;
      if ( nm_stnobj_brake () )
      {
        return ;
      }
      if ( nm_stnobj_power () )
      {
        return ;
      }

      // 惰行領域へ
      iamconst = false ;
      iamcoast = true ;
      if ( tresist ( vel , pgrad ) > 0
           || ( vel < nxx -> vlimit ( pgrad )
                || nxx -> vlimit ( pgrad ) < INVLIM ) )
      {
        // 制限速度にかからないケース
        //  -> Coast 状態へ遷移
        // **** 速度が制限速度より低い
        // **** 速度が制限速度以上だが惰行時速度が落ちる
        stat = Coast ;
        rnotchold = rnotch = 0.0 ;
#ifdef DEBUG
        cerr << StrPrintf
          ( "Btws Brake -> Coast, vlimit = %.1f km/h" ,
            nxx -> vlimit ( pgrad ) ) << endl ;
#endif
        return ;			// ontime 運転条件
      }
      else
      {
        // 制限速度にひっかかるケース
        //  -> Const_vel 状態へ遷移
        // **** 速度が制限速度以上で惰行時速度が落ちない
        stat = Const_vel ;
#ifdef DEBUG
        cerr << "Btws Brake -> Const, vlimit, vel = "
             << StrPrintf ( "%.1f, %.1f km/h" ,
                            nxx -> vlimit ( pgrad ) , vel ) << endl ;
#endif
        return ;
      }
    }
  }

  if ( pos * direc ()
       > nxx -> parrive () * direc () - DINVLIM )
  {
    // 次駅データ終了条件
    sw_last_brake = true ;
  }

  if ( sw_last_brake && vel > brakev )
  {
    // pos が arp を飛び出さないように抑える
    pos = nxx -> parrive () - INVLIM * direc () ;
    return ;
  }

  if ( vel <= brakev )
  {
    // ブレーキ終了条件・速度を見ればＯＫ

    // vel が負にならないように抑える
    if ( vel < 0.0 )
    {
      vel = 0.0 ;
    }

    // 最後ではない: 速度制限によるブレーキ
    if ( ( ! sw_last_brake ) && brakev > INVLIM && vel > INVLIM )
    {
      // brakev != 0
      if ( iamconst )
      {
        // 力行領域にある場合: 定速走行モードに移行
        stat = Const_vel ;
        iamcoast = false ;
        iamconst = true ;
        return ;
      }
      stat = Coast ;
      iamconst = false ;
      iamcoast = true ;
      rnotchold = rnotch = 0.0 ;
      return ;
    }

    if ( sw_btws )
    {
      // 最後ではない: 駅進入時隔による制限
      stat = Between_stop ;	// 駅間停車状態
      return ;
    }

    /*************************************
     * ここまで来たら 次駅データ入れ換え *
     *************************************/

    double tde = tdept - taudx ;	// 次駅データ出発時遅れ時分の決定
    taudx = t_sim - nxx -> tarrive ( true ) - tde ;	// 遅れ

    l_ofs << StrPrintf
      ( "car %i: pos = %.3f, direc %d(%d-%d), delay %.2f sec." ,
        carno , pos , direc () , nxx -> dptnum () , nxx -> dpttbl () ,
        taudx ) << endl ;
    cerr << StrPrintf
      ( "car %i: pos = %.3f, direc %d(%d-%d), delay %.2f sec." ,
        carno , pos , direc () , nxx -> dptnum () , nxx -> dpttbl () ,
        taudx ) << endl ;

    nxx -> setpf ( taudx , pfirst ) ;		// 遅れを通信

    if ( fabs ( taudx ) > ontprec_delay_set )
    {
      // 遅れが大きい: フリーラン中で ontprec 未セットならば nfree 増
      if ( g_sw_freerun_cycle && ( ! nxx -> get_ontprec () )
           && n_inc_nfree != n_simcycle )
      {
        ++ nfree ;
        l_ofs << StrPrintf ( "nfree increased to %d" , nfree ) << endl ;
        cerr << StrPrintf ( "nfree increased to %d" , nfree ) << endl ;

        // nfree 増を1サイクル2度行わない
        n_inc_nfree = n_simcycle ;
      }
      nxx -> set_ontprec () ;			// ontprec をセット
    }

    nextsta_pattern nxxpx = nxx -> nxxptn () ;

    double x_tard ;	// 到着遅れ時分の期待値

    // 停車時分決定部分
    switch ( nxxpx )
    {
    case Station :		// 駅停車モードの nxx であるとき
    case Pass_station :		// 通過→停車モードの nxx も
      /* double */ x_tard = 0.0 ;	// 到着遅れ時分の期待値
      if ( g_sw_station_object_valid && g_sw_disttrain_ended )
      {
        // 信号系有効時のみチェック
        // **** nxx -> tarr_delay ( this ) : 信号系無効時は zero なのだが
        x_tard = nxx -> tarr_delay ( this ) ;	// これが通常の話
        if ( g_sw_stop_between_oriented && sw_x_btws )
        {
          // 駅間停止指向で駅間停止位置を過ぎていない場合
          x_tard = 0.0 ;	// 遅れなしとしてパターンを生成
        }
      }
      if ( taudx < x_tard )
      {
        // 停車時間決定と駅出発時遅れ時分の修正
        tdept = nxx -> tarrive ( true ) ;
        tdept += nxx -> stoptime () + tde ;
        tdept += x_tard ;		// 遅れ不足は停車時分延長で
        taudx = x_tard ;
      }
      else
      {
        // 規定停車時分
        tdept = t_sim + nxx -> stoptime () ;
      }
      break ;

    case Station_pass :	// 発車→通過モードの nxx のとき
    case Pass_pass :	// 通過→停車モードの nxx も
      tdept = t_sim ;		// 停車時分は存在しない
      break ;			// 負の taudx をゼロにできない→することなし
    }

    // 次状態決定部分 ( その1 )
    switch ( nxxpx )
    {
    case Station :		// 駅停車モードの nxx であるとき
    case Pass_station :		// 通過→停車モードの nxx も
      if ( nxx -> stoptime () < INVLIM )
      {
        // 停車時分ゼロ: 状態「力行」に遷移
        stat = Power ;
        iamconst = true ;
        iamcoast = false ;
        ontprec = nxx -> get_ontprec () ;	// 注意！ added 94-6-1
        rnotchold = rnotch = 1.0 ;
        pfirst = 0 ;
      }
      else
      {
        // 停車時分 non-zero: 状態「駅停車」に遷移
        stat = Stn_stop ;
        rnotchold = rnotch = 0.0 ;
      }
      break ;

    case Station_pass :		// 発車→通過モードの nxx のとき
    case Pass_pass:		// 通過→停車モードの nxx も
      break ;	// 入れ換えないと ontimesub が計算できないので
    }

    prate = ptime / nxx -> tarrive ( true ) ;		// 力行率
    nxx -> prate ( prate ) ;				// 力行率通信
#ifdef PRATEBUG
    cerr << StrPrintf ( "prate = %.7f" , prate ) << endl ;
#endif

    addlen () ;						// 総走行距離校正
    stpos = pos = nxx -> nextnxx () -> pstart () ;	// 列車位置校正
    setNextstaPointer ( nxx -> nextnxx () ) ;
    pgrad = nxx -> dpgrad ( pos ) ;			// 次駅データ入換
    ptime = 0.0 ;					// 力行時間更新
    tarr_real = t_sim ;	// 駅到着時刻を記憶（あまり意味なし）
    sw_x_btws = true ;	// 駅間停車位置より先行駅側にいることにする

    if ( g_sw_station_object_valid && g_sw_disttrain_ended )
    {
      // to_station データを from_station データに置き換え
      stnobj_now -> from_station_generate ( this ) ;
    }

    // 次状態決定部分 ( その2 )
    switch ( nxxpx )
    {
    case Station :	// 駅停車モードの nxx であるとき
    case Pass_station:	// 通過→停車モードの nxx も
      if ( g_sw_station_object_valid && nxx -> stoptime () < INVLIM )
      {
        // 停車時間がゼロであるなら入れ換え
        if ( g_sw_disttrain_ended )
        {
          stnobj_now->this_train_will_depart ( this ) ;
        }
        stnobj_now = nxx -> stnobj () ;
      }
      break ;

    case Station_pass :	// 発車→通過モードの nxx のとき
    case Pass_pass :	// 通過→停車モードの nxx も
      if ( g_sw_station_object_valid )
      {
        if ( g_sw_disttrain_ended )
        {
          // 通過も可
          stnobj_now -> this_train_will_depart ( this ) ;
        }
        stnobj_now = nxx -> stnobj () ;
        if ( g_sw_disttrain_ended )
        {
          // disttrain が終了した後のみ行う
          // to_station を新しい station_obj に通信,
#ifdef DEBUG
          cerr << "communicate with the new station_obj" << endl ;
#endif
          stnobj_comm () ;	// 時隔計算を新たに行う
        }
      }	// 入れ換え

      double ontx = ontimesub ( pos , pgrad , vel , t_sim ) ;
      double x_tard = 0.0 ;	// 到着遅れ時分の期待値
      if ( g_sw_station_object_valid && g_sw_disttrain_ended )
      {
        // 信号系有効時のみチェック
        // **** nxx -> tarr_delay ( this ) : 信号系無効時は zero なのだが
        x_tard = nxx -> tarr_delay ( this ) ;	// これが通常の話
        if ( g_sw_stop_between_oriented && sw_x_btws )
        {
          // 駅間停止指向で駅間停止位置を過ぎていない場合
          x_tard = 0.0 ;	// 遅れなしとしてパターンを生成
        }
      }

      if ( ontx - tdept + taudx <= nxx -> tarrive ( pfirst ) + x_tard )
      {
        // ブレーキ状態への転向条件
        stat = Brake ;
        brakev = - 1.0 ;	// 負の目標速度：ontime Brake 条件
        rnotchold = rnotch = - 1.0 ;
        iamcoast = true ;		// 惰行領域を設定
        iamconst = false ;
      }
      else
      {
        // 力行状態への転向条件
        iamcoast = false ;			// 力行領域を設定
        iamconst = true ;
        cpsw = false ;				// cpsw リセット
        if ( vel >= nxx -> vlimit ( pgrad )
             && nxx -> vlimit ( pgrad ) >= INVLIM
             && tresist ( vel , pgrad ) < 0 )
        {
          // 速度制限にかかる → 定速走行へ転向
          stat = Const_vel ;
        }
        else
        {
          // 速度制限にかからない → 力行状態へ転向
          stat = Power ;
          rnotchold = rnotch = 1.0 ;
        }
      }
      break ;
    }
  }
}
