// This may look like C code, but it is really -*- C++ -*-
// RTSS --- Railway Total System Simulator
// (c) TAKAGI Ryo (at Kogakuin University)
// Ref_SOC_Curve.cc --- class Ref_SOC_Curve implementations
// -----
// ChangeLog:
// 2009. 11. 25
//  Using lower_bound function instead of find_if in getData(double).
// 2009. 11. 25
//  File created.
// -----


#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <string>
#include <algorithm>
#include "Ref_SOC_Curve.hh"
#include "globvar.hh"

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



// ----
// Constructor of base class SOC_FeedForward_Data
// 基底クラス SOC_FeedForward_Data のコンストラクタ。
// ----
SOC_FeedForward_Data :: SOC_FeedForward_Data
( TiXmlNode const * const & curnode )
{
  if ( ! curnode )
  {
    cerr << "Error: node not set in SOC_FeedForward_Data constructor."
         << endl ;
    exit ( 1 ) ;
  }
  if ( curnode -> ValueStr () != "data" )
  {
    cerr << "Error: <" << curnode -> ValueStr ()
         << "> tag not allowed in SOC_FeedForward_Data constructor." << endl ;
    exit ( 1 ) ;
  }
  const TiXmlAttribute * attr = curnode -> ToElement () -> FirstAttribute () ;
  bool sw_name = false ;
  while ( attr )
  {
    if ( string ( attr -> Name () ) == "name" )
    {
      // Attribute "name"
      if ( sw_name )
      {
        // Strange ... two attributes of same name?
        // 同じ属性が2つある？... エラー
        cerr << "Error: two \"name\" attributes in "
             << "SOC_FeedForward_Data constructor?" << endl ;
        exit ( 2 ) ;
      }
      setName ( attr -> ValueStr () ) ;
      sw_name = true ;
    }
    attr = attr -> Next () ;
  }
  if ( ! sw_name )
  {
    cerr << "Error: <data> tag without attribute \"name\"."
         << endl ;
    exit ( 1 ) ;
  }
}



// -----
// Constructor of class Ref_SOC_Data_Cell using two XML attributes
// Ref_SOC_Data_Cell クラスを2つのXML属性で初期化する
// -----
Ref_SOC_Data_Cell :: Ref_SOC_Data_Cell
( Ref_SOC_Data_Cell :: Init const & x )
{
  // -----
  // Two attributes are contained in TiXmlNode pointers x._pos and x._value.
  // No pointer validity check is done here.
  // 2つの属性は x._pos と x._value という TiXmlNode ポインタの先にある.
  // ポインタの有効性チェックはしない.
  // -----
  sscanf ( x . _pos -> Value () , "%lf" , & _pos ) ;
  sscanf ( x . _value -> Value () , "%lf" , & _value ) ;
}



// -----
// Constructor of class Ref_SOC_Curve using a file
// Ref_SOC_Curve クラスをファイルひとつで初期化する
// -----
Ref_SOC_Curve :: Ref_SOC_Curve
( TiXmlNode const * const & _ch )
  : SOC_FeedForward_Data ( _ch ) ,
    vector < Ref_SOC_Data_Cell > ()
{
  // -----
  // Getting child elements of tag <data>, which must all be <soc>.
  // <data> タグの子を取得. すべて <soc> タグ。
  // -----
  Ref_SOC_Data_Cell _yi ( 0 , 0 ) ;
  bool is_not_first = false ;
  for ( TiXmlNode const * _cc = _ch -> FirstChild () ;
        _cc ; _cc = _cc -> NextSibling () )
  {
    // -----
    // Ignore non-Element node.
    // エレメント以外は無視。
    // -----
    if ( _cc -> Type () != TiXmlNode :: TINYXML_ELEMENT )
      continue ;

    // -----
    // Check tag name.
    // タグ名チェック。
    // -----
    string _cc_val = string ( _cc -> Value () ) ;
    if ( _cc_val != "soc" )
    {
      cerr << "Error: \"" << _cc_val << "\" tag not allowed." << endl ;
      exit ( 1 ) ;
    }

    TiXmlAttribute const * _pos = 0 ;
    bool _sw_pos = true ;
    TiXmlAttribute const * _value = 0 ;
    bool _sw_val = true ;
    for ( TiXmlAttribute const * attr
            = _cc -> ToElement () -> FirstAttribute () ;
          attr ; attr = attr -> Next () )
    {
      if ( string ( attr -> Name () ) == "pos" )
      {
        _sw_pos = false ;
        _pos = attr ;
      }
      else if ( string ( attr -> Name () ) == "value" )
      {
        _sw_val = false ;
        _value = attr ;
      }
      else
      {
        cerr << "ERROR: tag <soc> with incorrect attribute "
             << attr -> Name () << endl ;
        exit ( 1 ) ;
      }
    }
    if ( _sw_pos || _sw_val )
    {
      cerr << "ERROR: <soc> data incorrect in <rtss_soc>" << endl ;
      exit ( 1 ) ;
    }
    Ref_SOC_Data_Cell :: Init _dci ( _pos , _value ) ;

    // -----
    // Create a Ref_SOC_Data_Cell object instance, and put it in
    // the vector by push_back().
    // Ref_SOC_Data_Cell オブジェクトのインスタンスを生成し，
    // push_back() 関数で vector に挿入する.
    // -----
    Ref_SOC_Data_Cell _xi = Ref_SOC_Data_Cell ( _dci ) ;
    if ( is_not_first )
    {
      if ( _xi . _pos < _yi . _pos )
      {
	cerr << "ERROR: <soc> tag must be sorted by position in the "
	     << "ascending order" << endl ;
	exit ( 1 ) ;
      }
    }
    else
    {
      is_not_first = true ;
    }
    _yi = _xi ;
    push_back ( _xi ) ;
  }
}



// -----
// Functor for Ref_SOC_Curve::getData(double).
// Ref_SOC_Curve::getData(double)関数のためのファンクタ
// -----
class SOC_Pos_Comp
{
public :
  bool operator()
  ( Ref_SOC_Data_Cell const & x , Ref_SOC_Data_Cell const & y ) const
  { return x . _pos < y . _pos ; }
} ;



// -----
// Get data.
// データを得る.
// -----
SOC_FeedForward_Data :: _Data
Ref_SOC_Curve :: getData
( double _x )
  const
{
  SOC_Pos_Comp _pc ;
  Ref_SOC_Data_Cell _xdc ( _x , 0 ) ;
  Ref_SOC_Curve :: const_iterator ii
    = lower_bound ( begin () , end () , _xdc , _pc ) ;
  if ( ii == end () )
  {
    cerr << "ERROR: position " << _x << " not found in reference SOC curve"
	 << endl ;
    exit ( 1 ) ;
  }
  else if ( ii == begin () && fabs ( ii -> _pos - _x ) >= INVLIM )
  {
    cerr << "ERROR: position " << _x << " not found in reference SOC curve"
	 << endl ;
    exit ( 1 ) ;
  }
  else if ( ii == begin () )
  {
    ++ ii ;
  }
  _Data ret_val ;
  double _soc_y = ( ii - 1 ) -> _value ;
  double _soc_x = ii -> _value ;
  double _pos_y = ( ii - 1 ) -> _pos ;
  double _pos_x = ii -> _pos ;
  ret_val . _soc
    = _soc_y + ( _soc_x - _soc_y ) * ( _x - _pos_y ) / ( _pos_x - _pos_y ) ;
  ret_val . _dsoc_dx = ( _soc_x - _soc_y ) / ( _pos_x - _pos_y ) ;
  return ret_val ;
}
