// This may look like C code, but it is really -*- C++ -*-
// RTSS --- Railway Total System Simulator
// (c) TAKAGI Ryo (at Kogakuin University)
// diapatrn.cc --- class diapattern functions
// -----
// ChangeLog:
// 2007. 12. 14
//  delete [] bug in diapattern::setbuf(...)
// 2007. 11. 21
//  Redirected log messages to l_file.
// -----


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

#include "diapatrn.hh"
#include "StrPrintf.hh"

using std :: cerr ;
using std :: endl ;


#define POWERRATEGREAT 0.2
#define TIMERATEGREAT 0.03
#define POWERRATEMEDIUM 0.1
#define TIMERATEMEDIUM 0.01
#define POWERRATELEAST 0.04	// setbuf 用フラグ
#define TIMERATELEAST 0.005	// setbuf 用フラグ

//#define DEBUG




// ----
// Destructor of _NextstaPtrs
// ----
_NextstaPtrs :: ~_NextstaPtrs
()
{
  for ( np_iterator ii = np_begin () ; ii < np_end () ; ++ ii )
  {
    if ( * ii )
    {
      delete * ii ;
    }
  }
}



// empty constructor
diapattern :: diapattern
()
  : _NextstaPtrs ()
{
  twhole = phase = pos = vel = tdept = taudx = 0 ;
  ndpt = cars = dptm = 0 ;
  stat = Power ;
  dcmd = No_comm ;
  bufss = bufset = false ;
}




// メンバコピー
void
diapattern :: membercopy
( diapattern & dpx )
{
  twhole = dpx . twhole ;
  cars = dpx . cars ;
  phase = dpx . phase ;
  dptm = dpx . dptm ;
  pos = dpx . pos ;
  vel = dpx . vel ;
  stat = dpx . stat ;
  tdept = dpx . tdept ;
  taudx = dpx . taudx ;
  bufss = dpx . bufss ;
  bufset = dpx . bufset ;
  dcmd = dpx . dcmd ;
  ndpt = dpx . ndpt ;
  train_type_key = dpx . train_type_key ;

}

// X(X&)
diapattern :: diapattern
( diapattern & dpx )
  : _NextstaPtrs ( dpx )
{
  membercopy ( dpx ) ;
}



// 代入演算子
diapattern &
diapattern :: operator=
( diapattern & dpx )
{
  if ( & dpx == this )
    return * this ;
  _NextstaPtrs :: operator= ( dpx ) ;
  membercopy ( dpx ) ;
  return * this ;
}



// nextsta object 内の nxx をセット
void
diapattern :: nextnxx
()
{
  // nxx: 次の「次駅データ」へのポインタ
  for ( diapattern :: np_iterator i = np_begin () ; i < np_end () - 1 ; ++ i )
  {
    if ( ! * i )
    {
      cerr << "Error in diapattern::nextnxx(): diapattern having null pointer"
           << endl ;
      exit ( 1 ) ;
    }
    ( * i ) -> set_nextnxx ( * ( i + 1 ) ) ;
  }
  ( * ( np_end () - 1 ) ) -> set_nextnxx ( * np_begin () ) ;
}



// nextsta object に番号を教える
void
diapattern :: setnxx
()
{
  int j = 0 ;
  for ( diapattern :: np_iterator i = np_begin () ; i < np_end () ; ++ i )
  {
    if ( ! * i )
    {
      cerr << "Error in diapattern::setnxx(): diapattern having null pointer"
           << endl ;
      exit ( 1 ) ;
    }
    ( * i ) -> set_ndpt ( ndpt ) ;  // ndpt は diapattern の番号
    ( * i ) -> set_ndp ( j ) ;	    // ndp は diapattern 配列内の番号
    ++ j ;
  }
}



// dcmd のセット
void
diapattern :: setdc
( dp_command x )
{
  dcmd = x ;
  for ( diapattern :: np_iterator i = np_begin () ; i < np_end () ; ++ i )
  {
    if ( ! * i )
    {
      cerr << "Error in diapattern::setdc(): diapattern having null pointer"
           << endl ;
      exit ( 1 ) ;
    }
    // diapattern 内すべて同一値
    ( * i ) -> setdc ( dcmd ) ;
  }
}



void
diapattern :: prtinit
()
{
  for ( diapattern :: np_iterator i = np_begin () ; i < np_end () ; ++ i )
  {
    if ( ! * i )
    {
      cerr << "Error in diapattern::prtinit(): diapattern having null pointer"
           << endl ;
      exit ( 1 ) ;
    }
    ( * i ) -> prtinit () ;
  }
}



void
diapattern :: prtreset
()
{
  for ( diapattern :: np_iterator i = np_begin () ; i < np_end () ; ++ i )
  {
    if ( ! * i )
    {
      cerr << "Error in diapattern::prtreset(): diapattern having null pointer"
           << endl ;
      exit ( 1 ) ;
    }
    ( * i ) -> prtreset () ;
  }
}



struct bufsort
{
  double prate ;
  int i ;
  double bz ;
} ;



// setbuf 用ソートプログラム
int sortbuff
( const void * ax ,
  const void * bx )
{
  bufsort * a = ( bufsort * ) ax ;
  bufsort * b = ( bufsort * ) bx ;
  if ( a -> prate > b -> prate ) return 1 ;
  else if ( a -> prate < b -> prate ) return -1 ;
  else return 0 ;
}



// 余裕時分の調整
bool
diapattern :: setbuf
()
{
  //**********************************************************************
  // この機能は高木の修論（1992）の研究のために付け加えられた機能。詳細は
  // 修論（東京大学大学院工学系研究科電気工学専攻、修士論文）を参照せよ。
  // （1994年の段階ではもう何のことだか忘れちゃった、あはは ^_^;）
  //**********************************************************************
  if ( dcmd != Buffer_yes )
      return true ;		// Buffer_yes のみ
  if ( bufset )
    return true ;
  for ( diapattern :: np_iterator i = np_begin () ; i < np_end () - 1 ; ++ i )
  {
    if ( ! * i )
    {
      cerr << "Error in diapattern::setbuf(): diapattern having null pointer"
           << endl ;
      exit ( 1 ) ;
    }
    // 一周期 一度のみ
    if ( ! ( * i ) -> prtset () )
    {
      return true ;
    }
  }
  static bufsort * bsort = new bufsort [ np_size () ] ;
  static int bsortm = np_size () ;
  double bufm = 0.0 ;
  if ( bsortm < np_size () )
  {
    delete [] bsort ;
    bsort = new bufsort [ bsortm = np_size () ] ;
  }
  for ( int i = 0 ; i < np_size () ; ++ i )
  {
    bsort [ i ] . i = i ;
    bsort [ i ] . bz = 0.0 ;
    bufm += bsort [ i ] . prate = getElement ( i ) -> prate () ;
  }
  bufm /= np_size () ;
  qsort ( bsort , np_size () , sizeof ( bufsort ) , & sortbuff ) ;

  int gl = -10 ;
  int ml = -10 ;
  int ll = -10 ;
  int lg = -10 ;
  int mg = -10 ;
  int gg = -10 ;
  for ( int i = 0 ; i < np_size () ; ++ i )
  {
    if ( bsort [ i ] . prate > bufm - POWERRATEGREAT )
      gl = i ;
    else continue ;
    if ( bsort [ i ] . prate > bufm - POWERRATEMEDIUM )
      ml = i ;
    else continue ;
    if ( bsort [ i ] . prate > bufm - POWERRATELEAST )
    {
      ll = i ;
      break ;
    }
  }
  for ( int i = np_size () - 1 ; i >= 0 ; -- i )
  {
    if ( bsort [ i ] . prate < bufm + POWERRATEGREAT )
      gg = i ;
    else continue ;
    if ( bsort [ i ] . prate < bufm + POWERRATEMEDIUM )
      mg = i ;
    else continue ;
    if ( bsort [ i ] . prate < bufm + POWERRATELEAST )
    {
      lg = i ;
      break ;
    }
  }

  double bufzl = 0.0 ;
  double bufzg = 0.0 ;
  for ( int i = 0 ; i < gl ; ++ i )
    bufzl += getElement ( bsort [ i ] . i ) -> tarrive ( true )
      * TIMERATEGREAT ;
  for ( int i = gl ; i < ml ; ++ i )
    bufzl += getElement ( bsort [ i ] . i ) -> tarrive ( true )
      * TIMERATEMEDIUM ;
  for ( int i = ml ; i < ll ; ++ i )
    bufzl += getElement ( bsort [ i ] . i ) -> tarrive ( true )
      * TIMERATELEAST ;
  for ( int i = np_size () - 1 ; i > gg ; -- i )
    bufzg += getElement ( bsort [ i ] . i ) -> tarrive ( true )
      * TIMERATEGREAT ;
  for ( int i = gg ; i > mg ; -- i )
    bufzg += getElement ( bsort [ i ] . i ) -> tarrive ( true )
      * TIMERATEMEDIUM ;
  for ( int i = mg ; i > lg ; -- i )
    bufzg += getElement ( bsort [ i ] . i ) -> tarrive ( true )
      * TIMERATELEAST ;

  for ( int i = 0 ; i < gl ; ++ i )
  {
    bsort [ i ] . bz = - getElement ( bsort [ i ] . i ) -> tarrive ( true ) ;
    bsort [ i ] . bz *= TIMERATEGREAT ;
    bsort [ i ] . bz /= bufzl ;
  }
  for ( int i = gl ; i < ml ; ++ i )
  {
    bsort [ i ] . bz = - getElement ( bsort [ i ] . i ) -> tarrive ( true ) ;
    bsort [ i ] . bz *= TIMERATEMEDIUM ;
    bsort [ i ] . bz /= bufzl ;
  }
  for ( int i = ml ; i < ll ; ++ i )
  {
    bsort [ i ] . bz = - getElement ( bsort [ i ] . i ) -> tarrive ( true ) ;
    bsort [ i ] . bz *= TIMERATELEAST ;
    bsort [ i ] . bz /= bufzl ;
  }
  for ( int i = np_size () - 1 ; i > gg ; -- i )
  {
    bsort [ i ] . bz = getElement ( bsort [ i ] . i ) -> tarrive ( true ) ;
    bsort [ i ] . bz *= TIMERATEGREAT ;
    bsort [ i ] . bz /= bufzg ;
  }
  for ( int i = gg ; i > mg ; -- i )
  {
    bsort [ i ] . bz = getElement ( bsort [ i ] . i ) -> tarrive ( true ) ;
    bsort [ i ] . bz *= TIMERATEMEDIUM ;
    bsort [ i ] . bz /= bufzg ;
  }
  for ( int i = mg ; i > lg ; -- i )
  {
    bsort [ i ] . bz = getElement ( bsort [ i ] . i ) -> tarrive ( true ) ;
    bsort [ i ] . bz *= TIMERATELEAST ;
    bsort [ i ] . bz /= bufzg ;
  }

  if ( bufzl > bufzg )
    bufzl = bufzg ;

  for ( int i = 0 ; i < np_size () ; ++ i )
  {
    gg = bsort [ i ] . i ;
    bufzg = bsort [ i ] . bz * bufzl ;
    getElement ( gg ) -> setbuf ( bufzg ) ;
  }
  prtreset () ;
  if ( bufss )
    bufset = true ;
  else
    bufss = true ;
  return false ;
}



// phase を計算し直し，時間に「足し合わせられる」ものにする
void
diapattern :: realphase_cal
()
{
  addphase = - phase ;
  while ( addphase < - INVLIM )
    addphase += twhole / cars ;
  l_ofs << StrPrintf
    ( "Diapattern %d: real phase = %10.3f" , ndpt , addphase )
        << endl ;
  return ;
}
