
#include "preact/state.h"

bool isclose(const doublescale& a, const doublescale& b) {
  fp_64 m = (a.scale>b.scale) ? a.scale : b.scale ;
  if (m<=0.) TRANSP_THROW("?preact::isclose(): Unexpected scale <= 0.") ;
  return ((fabs(a.value-b.value)/m)<close_epsilon) ;
} ;

ostream& operator<<(ostream& s, const doublescale& q) {
  s.setf(IOS_BASE::scientific, IOS_BASE::floatfield) ;
  s << " [" ;
  s.precision(15) ;
  s << q.value << ',' ;
  s.precision(2) ;   // not that important
  s << q.scale ;
  s.precision(15) ;  // in case following calls do not change
  s << ']' ;
  return s ;
} ; 

istream& operator>>(istream& s, doublescale& qout) {
  char c = '0';
  doublescale q ;   // temporary
  s >> c ;
  if (s.bad() || c != '[') { s.setstate(IOS_BASE::badbit) ; return s ; } ;
  s >> q.value >> c ;
  if (s.bad() || c != ',') { s.setstate(IOS_BASE::badbit) ; return s ; } ;
  s >> q.scale >> c ;
  if (s.bad() || c != ']') { s.setstate(IOS_BASE::badbit) ; return s ; } ;
  if (s) qout=q ;   // data is ok
  return s ;
} ;

// ----------------- state --------------------
state::state() : vs(0), vi(0), vd(0), vp(0) { } ;

state::state(const state& q) : vs(q.vs), vi(q.vi), vd(q.vd), vp(0) {
  if (q.vp != 0) {
    vp = new vector<state>(0) ; // new state vector with zero elements
    *vp = *(q.vp) ;             // recursive copy of state info
  } ;
} ;

state& state::operator=(const state& q) {
  if (&q != this) {
    vs = q.vs ;
    vi = q.vi ;
    vd = q.vd ;
    if (q.vp == 0) {
      // remove existing state data
      if (vp != 0) {
	delete vp ;
	vp=0 ;
      } ;
    }
    else {
      // need to copy over the new data
      if (vp == 0) vp = new vector<state>(0) ;
      *vp = *(q.vp) ;
    } ;
  } ;
  return *this ;
};

state::~state() {
  delete vp ;
} ;

size_t state::vsize() const  {
  if (vp==0) return 0 ;
  return vp->size() ;
} ;

void state::vresize(size_t s) {
  if (s==0) {
    // leave with null pointer
    if (vp==0) return ;
    delete vp ;
    vp = 0 ;
  }
  else {
    // leave with set pointer
    if (vp==0) {
      vp=new vector<state>(s) ;
    }
    else {
      vp->resize(s) ;
    } ;
  } ;
} ;

state& state::vstate(size_t j) {
  if (vp==0) TRANSP_THROW("preact::state::vstate() : Attempted access of a null vector<state> pointer") ;
  if (j>=vp->size()) TRANSP_THROW("preact::state::vstate() : Attempted access beyond the limits of the vector<state>") ;
  return vp->operator[](j) ;
} ;

const state& state::vstate(size_t j) const {
  if (vp==0) TRANSP_THROW("preact::state::vstate() : Attempted access of a null vector<state> pointer") ;
  if (j>=vp->size()) TRANSP_THROW("preact::state::vstate() : Attempted access beyond the limits of the vector<state>") ;
  return vp->operator[](j) ;
} ;

bool state::operator==(const state& q) const {

  // -- check sizes --
  if (vs.size() != q.vs.size()) return false ;
  if (vi.size() != q.vi.size()) return false ;
  if (vd.size() != q.vd.size()) return false ;
  if (vsize()   != q.vsize())   return false ;

  // -- check values --
  for (size_t i=0 ; i<vs.size() ; i++) {
    if (vs[i] != q.vs[i]) return false ;
  } ;

  for (size_t i=0 ; i<vi.size() ; i++) {
    if (vi[i] != q.vi[i]) return false ;
  } ;

  for (size_t i=0 ; i<vd.size() ; i++) {
    if (! isclose(vd[i],q.vd[i]) ) return false ;
  } ;

  for (size_t i=0 ; i<vsize() ; i++) {
    if (!(vstate(i) == q.vstate(i))) return false ; // recursion
  } ;

  return true ;
} ;

ostream& operator<<(ostream& s, const state& q) {
  s << " < " ;  // head -- add whitespace so can be read with string

  s << q.vs.size() << " " << q.vi.size() << " " << q.vd.size() << " " 
    << q.vsize() << " " ; // sizes

  for (size_t i=0 ; i<q.vs.size() ; i++) {
    s << '"' << q.vs[i] << '"' << " " ;     // delimited string
  } ;

  for (size_t i=0 ; i<q.vi.size() ; i++) {
    s << q.vi[i] << " " ;
  } ;

  for (size_t i=0 ; i<q.vd.size() ; i++) {
    s << q.vd[i] << " " ;                   // saves value and scale
  } ;

  for (size_t i=0 ; i<q.vsize() ; i++) {
    s << q.vstate(i) << " " ;               // recursion
  } ;

  s << "> " ;  // tail
  return s ;
} ;

istream& operator>>(istream& s, state& q) {
  if (s.bad()) return s ;   // no chance
  string t ;
  s >> t ;
  if (t != "<") { s.setstate(IOS_BASE::badbit) ; return s ; } ;

  // -- get sizes --
  size_t ss, si, sd, sv ;
  s >> ss >> si >> sd >> sv ;
  if (s.bad()) return s ;
  q.vs.resize(ss) ;
  q.vi.resize(si) ;
  q.vd.resize(sd) ;
  q.vresize(sv) ;

  char c ;
  // -- strings --
  for (size_t i=0 ; i<q.vs.size() ; i++) {
    t = "" ;
    while (s && (s.get()!='"')) ;   // get first delimiter
    while (s) {
      s.get(c) ;
      if (c=='"') break ;  // look for second delimiter
      t+=c ;               // append string character
    } ;
    if (s.bad()) return s ; // may be missing second delimiter
    q.vs[i]=t ;
  } ;

  // -- integers --
  for (size_t i=0 ; i<q.vi.size() ; i++) {
    s >> q.vi[i] ;
    if (s.bad()) return s ;
  } ;
 
  // -- doublescales --
  for (size_t i=0 ; i<q.vd.size() ; i++) {
    s >> q.vd[i] ;
    if (s.bad()) return s ;
  } ;
 
  // -- states --
  for (size_t i=0 ; i<q.vsize() ; i++) {
    s >> q.vstate(i) ;
    if (s.bad()) return s ;
  } ;
 
  s >> t ;
  if (t != ">") s.setstate(IOS_BASE::badbit) ; 

  return s ;

} ;
