#include "preact/impreaction.h"
#include "preact/impreact_adpak.h"
#include "preact/periodic_table.h"
#include <vector>
#include "preact/iutil_str.h"

using namespace TRANSP ;

ImpReact_Rad::~ImpReact_Rad() { } ;

ImpReact_Rad* ImpReact_Rad::cast(const React& r) {
  return dynamic_cast<ImpReact_Rad*>(aReact::cast(r)) ;
} ;

// ------------ ImpReaction -------------
ImpReaction::ImpReaction(int nucz, option r) : knucz(nucz), kname(" "), kopt(r),
					       rioniz(0), rrecom(0), rrad(0), rbrem(0) {
  // name
  ostringstream c ;
  c << "Impurity Reaction for " << table_name(knucz) << " using " << r ;
  kname = ss2string(c) ;

  // 
  // choose your weapon
  //
  switch(r) {
  case ADPAK_DIRECT:
    rioniz = ImpReactADPAK_Ioniz::create(knucz) ;
    rrecom = ImpReactADPAK_Recom::create(knucz) ;
    rrad   = ImpReactADPAK_Rad::create(knucz) ;
    rbrem  = ImpReactADPAK_Brem::create(knucz) ;
    break ;
  default:
    TRANSP_THROW(string("?preact::ImpReaction(): Unknown build option supplied to constructor -- ")
			+ table_name(knucz)) ;
  } ;
} ;

void ImpReaction::bcheck() const {
  ImpReact_Rad* p = ImpReact_Rad::cast(rrad) ;
  if (p==0) TRANSP_THROW("?preact::ImpReaction::bcheck(): React member rrad does not contain an ImpReact_Rad (huh?)") ;
  if (p->brem()) TRANSP_THROW(string("?preact::ImpReaction::bcheck(): You are not allowed to include brem... ") +
			      "in radiation\n and call brem.. separately.  This is a feature -- you can thank me later") ;
} ;

string ImpReaction::info() const {
  ostringstream c ;
  c << " ================== Impurity Reaction ===================" << endl ;
  c << " Name: " << kname << endl ;
  c << " Nuclear Z: " << knucz << endl << endl ;
  c << rioniz << endl ;
  c << rrecom << endl ;
  c << rrad   << endl ;
  c << rbrem  << endl ;
  c << " ================ End Impurity Reaction =================" << endl ;
  return ss2string(c) ;
} ;

void ImpReaction::r_set_min_policy(size_t i, Range::policy pmin) {
  aReact::cast(rioniz)->set_min_policy(i,pmin) ;
  aReact::cast(rrecom)->set_min_policy(i,pmin) ;
  aReact::cast(rrad)->set_min_policy(i,pmin) ;
  aReact::cast(rbrem)->set_min_policy(i,pmin) ;
} ;

void ImpReaction::r_set_max_policy(size_t i, Range::policy pmax) {
  aReact::cast(rioniz)->set_max_policy(i,pmax) ;
  aReact::cast(rrecom)->set_max_policy(i,pmax) ;
  aReact::cast(rrad)->set_max_policy(i,pmax) ;
  aReact::cast(rbrem)->set_max_policy(i,pmax) ;
} ;

string ImpReaction::table_name(int knucz) {
  return (sname_periodic_table(0,knucz,fp_32(0.)) + " (" +
	  sto_periodic_table(0, knucz, fp_32(0.), -1, 0) + ")") ;
} ;

// ---------- template instantiation --------
template void ImpReaction::coronal(const fwrap<fp_64>&, const fwrap<fp_64>&, fwrap<fp_64>&) ;

template void ImpReaction::coronal_rad(const fwrap<fp_64>&, const fwrap<fp_64>&, fwrap<fp_64>&) ;

// ---------- global ---------
ostream& operator<<(ostream& c, ImpReaction::option r) {
  switch(r) {
  case ImpReaction::ADPAK_DIRECT:
    return c<<"ADPAK_DIRECT" ;
  } ;
  return c << "UNKNOWN_OPTION" ;
} ;

ostream& operator<<(ostream& c, const ImpReaction&  r) {
  return c<<r.name() ;
} ;

// -------------------- ImpReactionTable ---------------
const size_t ImpReactionTable::max_elements ;
const ImpReaction::option ImpReactionTable::init_def_option ;

ImpReactionTable* ImpReactionTable::s=0 ; // null pointer

ImpReactionTable::ImpReactionTable() : dopt(init_def_option), dbrem(false), pr(max_elements+1) {
  for (size_t i=0 ; i<=max_elements ; i++) pr[i]=0 ;
} ;
ImpReactionTable::~ImpReactionTable() { 
  for (size_t i=0 ; i<=max_elements ; i++) delete pr[i] ;
} ;

ImpReaction* ImpReactionTable::operator[](size_t i) {
  check(i) ;
  if (!pr[i]) build(i,dopt,dbrem) ;  // build default if not defined
  return pr[i] ;
} ;

void ImpReactionTable::build(size_t i, Ioption r, bool brem) {
  erase(i) ;
  pr[i] = new ImpReaction(i,r) ;
  ImpReact_Rad* p=ImpReact_Rad::cast(pr[i]->rad()) ;
  if (!p) TRANSP_THROW("?preact::ImpReactionTable::build(): got a null ImpReact_Rad* ???") ;
  p->set_brem(brem) ;
  //cout << "building nucz = " << i << "  opt=" << r << "  brem=" << brem << endl ;
} ;

void ImpReactionTable::erase(size_t i) {
  check(i) ;
  if (pr[i]) {
    delete pr[i] ;
    pr[i]=0 ;
  } ;
} ;

void ImpReactionTable::clear() {
  for (size_t i=0 ; i<=max_elements ; i++) erase(i) ;
} ;

ImpReactionTable* ImpReactionTable::table() {
  if (!s) s = new ImpReactionTable() ;
  return s ;
} ;
