#include "preact/f77imp.h"
#include "preact/impreaction.h"
#include <iostream>

extern "C" {

  void F77NAME(imptable_def_option)(const int& opt) { 
    try {
      ImpReactionTable::table()->set_def_option(ImpReaction::option(opt)) ;
    } FCATCH() ; 
  } ;

  void F77NAME(imptable_def_brem)(const int& ibrem) {
    try {
      bool b = (ibrem != 0) ;
      ImpReactionTable::table()->set_def_brem(b) ;
    } FCATCH() ;
  } ;
                                             
                                             
  void F77NAME(imptable_build)(const int& nucz, const int& opt, const int& ibrem) {
    try {
      bool b = (ibrem != 0) ;
      ImpReactionTable::table()->build(nucz, ImpReaction::option(opt), b) ;
    } FCATCH() ;
  } ;  

  void F77NAME(imptable_print)(const int& nucz) {
    try {
      cout << ImpReactionTable::Element(nucz)->info() << endl ;
    } FCATCH() ;
  } ;

  void F77NAME(imptable_ioniz_policy)(const int& nucz, const int& iaxis, const int& pmin, const int& pmax) {
    try {
      aReact::cast(ImpReactionTable::Element(nucz)->ioniz())->set_min_policy(iaxis,Range::policy(pmin)) ;
      aReact::cast(ImpReactionTable::Element(nucz)->ioniz())->set_max_policy(iaxis,Range::policy(pmax)) ;
    } FCATCH() ;
  } ;

  void F77NAME(imptable_recom_policy)(const int& nucz, const int& iaxis, const int& pmin, const int& pmax) {
    try {
      aReact::cast(ImpReactionTable::Element(nucz)->recom())->set_min_policy(iaxis,Range::policy(pmin)) ;
      aReact::cast(ImpReactionTable::Element(nucz)->recom())->set_max_policy(iaxis,Range::policy(pmax)) ;
    } FCATCH() ;
  } ;

  void F77NAME(imptable_rad_policy)(const int& nucz, const int& iaxis, const int& pmin, const int& pmax){
    try {
      aReact::cast(ImpReactionTable::Element(nucz)->rad())->set_min_policy(iaxis,Range::policy(pmin)) ;
      aReact::cast(ImpReactionTable::Element(nucz)->rad())->set_max_policy(iaxis,Range::policy(pmax)) ;
    } FCATCH() ;
  }  ;

  void F77NAME(imptable_brem_policy)(const int& nucz, const int& iaxis, const int& pmin, const int& pmax){
    try {
      aReact::cast(ImpReactionTable::Element(nucz)->brem())->set_min_policy(iaxis,Range::policy(pmin)) ;
      aReact::cast(ImpReactionTable::Element(nucz)->brem())->set_max_policy(iaxis,Range::policy(pmax)) ;
    } FCATCH() ;
  }  ;

  void F77NAME(imptable_policy)(const int& nucz, const int& iaxis, const int& pmin, const int& pmax) {
    try {
      ImpReactionTable::Element(nucz)->r_set_min_policy(iaxis, Range::policy(pmin)) ;
      ImpReactionTable::Element(nucz)->r_set_max_policy(iaxis, Range::policy(pmax)) ;
    } FCATCH() ;
  } ;


  void F77NAME(imptable_ioniz_r8)(const int& nucz, const int& nvec, fp_64* te, fp_64* ne, const int& mch, fp_64* result) {
    try {
      if (mch<=nucz) TRANSP_THROW("?preact::imptable_ioniz_r8(): Dimensionsed size of result is not large enough") ;
      ap_64 ate(nvec,te), ane(nvec,ne), are(mch,nvec,result) ;
      ImpReactionTable::Element(nucz)->ioniz().vector2(ate,ane,are,imptable_skip_check) ;
    } FCATCH() ;
  } ;

  void F77NAME(imptable_recom_r8)(const int& nucz, const int& nvec, fp_64* te, fp_64* ne, const int& mch, fp_64* result){
    try {
      if (mch<=nucz) TRANSP_THROW("?preact::imptable_recom_r8(): Dimensionsed size of result is not large enough") ;
      ap_64 ate(nvec,te), ane(nvec,ne), are(mch,nvec,result) ;
      ImpReactionTable::Element(nucz)->recom().vector2(ate,ane,are,imptable_skip_check) ;
    } FCATCH() ;
  }  ;

  void F77NAME(imptable_rad_r8)(const int& nucz, const int& nvec, fp_64* te, fp_64* ne, const int& mch, fp_64* result){
    try {
      if (mch<=nucz) TRANSP_THROW("?preact::imptable_rad_r8(): Dimensionsed size of result is not large enough") ;
      ap_64 ate(nvec,te), ane(nvec,ne), are(mch,nvec,result) ;
      ImpReactionTable::Element(nucz)->rad().vector2(ate,ane,are,imptable_skip_check) ;
    } FCATCH() ;
  }  ;

  void F77NAME(imptable_brem_r8)(const int& nucz, const int& nvec, fp_64* te, fp_64* ne, const int& mch, fp_64* result){
    try {
      if (mch<=nucz) TRANSP_THROW("?preact::imptable_brem_r8(): Dimensionsed size of result is not large enough") ;
      ap_64 ate(nvec,te), ane(nvec,ne), are(mch,nvec,result) ;
      ImpReactionTable::Element(nucz)->brem().vector2(ate,ane,are,imptable_skip_check) ;
    } FCATCH() ;
  }  ;

  void F77NAME(impreaction_coronal_r8)(const int& nucz, const int& nvec, fp_64* ioniz, fp_64* recom, const int& mch, fp_64* result) {
    try {
      if (mch<=nucz) TRANSP_THROW("?preact::impreaction_coronal(): Dimensionsed arrays is not large enough") ;
      dimsize s(nucz+1,mch), t(nvec) ;
      ap_64 aioniz(s,t,ioniz), arecom(s,t,recom), are(s,t,result) ;
      ImpReaction::coronal(aioniz, arecom, are) ;
    } FCATCH() ;
  } ;

  void F77NAME(impreaction_coronal_rad_r8)(const int& nucz, const int& nvec, fp_64* coron, fp_64* rad, const int& mch, fp_64* result) {
    try {
      if (mch<=nucz) TRANSP_THROW("?preact::impreaction_coronal(): Dimensionsed arrays is not large enough") ;
      dimsize s(nucz+1,mch), t(nvec) ;
      ap_64 acoron(s,t,coron), arad(s,t,rad), are(t,result) ;
      ImpReaction::coronal_rad(acoron, arad, are) ;
    } FCATCH() ;
  } ;

  void F77NAME(preact_impurity_test)(const int* fnucz, const fp_64* fte, const fp_64* fne, 
				     fp_64* fcoron, fp_64* fcoron_rad) {
    //
    // -- input --
    // fnucz = fortran integer for nuclear Z of impurity
    //
    // -- outputs --
    // fcoron     = fortran array of type real*8, dimension(nucz+1)
    //              which will contain the coronal equilibrium as a return value
    // fcoron_rad = fortran variable of type real*8
    //              which will contain the total radiation in (joules/sec) at
    //              the coronal equilibrium.
    //
    int nucz = *fnucz ;

    //
    // ================ fwrap ======================
    // lots of different ways to build fwrap<> objects
    //
    ap_64 rioniz(nucz+1,1) ;                        // ionization    -- logical=physical size
    ap_64 rrecom(dimsize(nucz+1,109),dimsize(1)) ;  // recombination -- logical<physical size

    //ap_64 rrad(109,1) ;                    // radiation -- logical size too large
    //rrad.set_size(0,nucz+1) ;              // change logical size after construction
    ap_64 rrad(nucz+1,1) ;                   // changing the size is no longer allowed

    ap_64 rcoron(nucz+1,1,fcoron) ;        // use a pointer to fp_64 for the actual storage
    ap_64 rcoron_rad(1,fcoron_rad) ;       

    ap_64 rte(1), rne(1) ;                 // hold arguments for vector2() reaction calls

    ap_64 sarg(2) ;                        // arguments for a single reaction call
    ap_64 sioniz(nucz+1) ;                 // ionization result after single reaction call

    //
    // ======== ImpReaction object =========
    //
    ImpReaction r(nucz) ;                  // build an ImpReaction object with Z=nucz

    r.r_set_min_policy(0,Range::WARN) ;    // change policy at minimum of Te range

    cout << "\n\n --- Impurity Object Reaction ---- " << endl ;
    cout << r << endl ;
    //cout << r.info() << endl ;                    // more then you want to know

    cout << "\n  Note: " << endl ;
    cout << "  All ap_32,ap_64 arrays are written in the format " << endl; 
    cout << "    rank" << endl ;
    cout << "    0  logical_size  physical_size  <-- for first dimension" << endl ;
    cout << "    1  logical_size  physical_size  <-- for second dimension" << endl ;
    cout << "      ... and so on for rank lines" << endl ;
    cout << "    0 0 data(0,0)" << endl ;
    cout << "    1 0 data(1,0)" << endl ;
    cout << "    2 0 data(2,0)" << endl ;
    cout << "      ... and so on for dim0*dim1*...*dim_rank-1 lines" << endl << endl ;

    // --- single reaction call ---
    sarg(0) = *fte ;            // Te in keV -- see fwrap.h for indexing notation
    sarg(1) = *fne ;            // Ne in m-3

    r.ioniz()(sarg,sioniz) ;  // acts like a function

    cout << "\nSingle reaction operator() call: " << endl ;
    cout << "  Te: " << sarg(0) << " keV" << endl ;
    cout << "  Ne: " << sarg(1) << " m-3" << endl ;
    cout << "  ionization rate (sec-1): " << endl ;
    cout << "  ------------------------ " << endl ;
    cout << sioniz << endl ;
  
    // --- vector call ---
    rte(0) = *fte ;
    rne(0) = *fne ;

    r.ioniz().vector2(rte, rne, rioniz) ;

    cout << "\nVector reaction call: " << endl ;
    cout << "  Te: " << rte(0) << " keV" << endl ;
    cout << "  Ne: " << rne(0) << " m-3" << endl ;
    cout << "  ionization rate (sec-1): " << endl ;
    cout << "  ------------------------ " << endl ;
    cout << rioniz << endl ;

    // 
    // =============== ImpReactionTable ===============
    //
    cout << "\n\n --- Impurity Table Reaction ---- " << endl ;
    cout << "  Element: " << ImpReaction::table_name(nucz) << endl ;
    cout << "  Te: " << rte(0) << " keV" << endl ;
    cout << "  Ne: " << rne(0) << " m-3" << endl ;

    ImpReactionTable::Element(nucz)->r_set_min_policy(0,Range::WARN) ;  // change policy at minimum of Te range

    ImpReactionTable::Element(nucz)->ioniz().vector2(rte, rne, rioniz) ;
    ImpReactionTable::Element(nucz)->recom().vector2(rte, rne, rrecom) ;
    ImpReactionTable::Element(nucz)->rad().vector2(rte, rne, rrad) ;

    cout << "\n ionization rate (sec-1): " << endl ;
    cout << " ------------------------ " << endl ;
    cout << rioniz << endl ;
    cout << "\n recombination rate (sec-1): " << endl ;
    cout << " ------------------------ " << endl ;
    cout << rrecom << endl ;
    cout << "\n radiation rate (joule/sec): " << endl ;
    cout << " ------------------------ " << endl ;
    cout << rrad << endl ;

    //
    // once you have the rates
    //
    ImpReaction::coronal(rioniz, rrecom, rcoron) ;       // get coronal equilibrium
    ImpReaction::coronal_rad(rcoron, rrad, rcoron_rad) ; // get radiation

    //
    // data was copied into rcoron, rcoron_rad which wrap the
    // arguments fcoron, fcoron_rad -- so fcoron and fcoron_rad
    // will return this data to the calling routine.
    //
  } ;

} ;
