/**
 *  @file surfaceSolver2.cpp
 *
 */

// This file is part of Cantera. See License.txt in the top-level directory or
// at https://cantera.org/license.txt for license and copyright information.

//  Example
//
//  Read a surface growth mechanism and calculate the solution
//  using Placid.
//

#define MSSIZE 200

/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
static void printUsage()
{
}

#include "cantera/thermo/ThermoFactory.h"
#include "cantera/kinetics.h"
#include "cantera/kinetics/ImplicitSurfChem.h"
#include "cantera/kinetics/InterfaceKinetics.h"
#include "cantera/kinetics/solveSP.h"
#include "cantera/base/fmt.h"
#include <cstdio>
#include <fstream>

using namespace std;
using namespace Cantera;

void printGas(ostream& oooo, ThermoPhase* gasTP, InterfaceKinetics* iKin_ptr, double* src)
{
    double x[MSSIZE];
    double C[MSSIZE];
    oooo.precision(3);
    string gasPhaseName          = "gas";
    gasTP->getMoleFractions(x);
    gasTP->getConcentrations(C);
    double Temp = gasTP->temperature();
    double p = gasTP->pressure();
    oooo << "Gas Temperature = " << Temp << endl;
    oooo << "Gas Pressure    = " << p << endl;
    size_t kstart = iKin_ptr->kineticsSpeciesIndex(0, 0);
    oooo << "Gas Phase:  " << gasPhaseName << "   "
         << "(" << kstart << ")" << endl;
    oooo << "                       Name      "
         << "     Conc              MoleF       SrcRate " << endl;
    oooo << "                                 "
         << "   (kmol/m^3)                   (kmol/m^2/s) " << endl;
    double sum = 0.0;
    size_t nspGas = gasTP->nSpecies();
    for (size_t k = 0; k < nspGas; k++) {
        kstart = iKin_ptr->kineticsSpeciesIndex(k, 0);
        fmt::print(oooo, "{:4d} {:>24s}   {:14g} {:14g}  {:14e}\n",
                   k, gasTP->speciesName(k), C[k], x[k], src[kstart]);
        sum += x[k];
    }
    oooo << "Sum of gas mole fractions= " << sum << endl;
    oooo << endl;


}

void printBulk(ostream& oooo,ThermoPhase* bulkPhaseTP, InterfaceKinetics* iKin_ptr, double* src)
{
    double x[MSSIZE];
    double C[MSSIZE];
    oooo.precision(3);
    string bulkParticlePhaseName = bulkPhaseTP->id();
    bulkPhaseTP->getMoleFractions(x);
    bulkPhaseTP->getConcentrations(C);
    size_t kstart = iKin_ptr->kineticsSpeciesIndex(0, 1);
    double dens = bulkPhaseTP->density();
    oooo << "Bulk Phase:  " << bulkParticlePhaseName << "   "
         << "(" << kstart << ")" << endl;
    double Temp = bulkPhaseTP->temperature();
    double p = bulkPhaseTP->pressure();
    oooo << "Bulk Temperature = " << Temp << endl;
    oooo << "Bulk Pressure    = " << p << endl;
    oooo << "                       Name      "
         << "     Conc              MoleF       SrcRate " << endl;
    oooo << "                                 "
         << "   (kmol/m^3)                   (kmol/m^2/s) " << endl;
    double sum = 0.0;
    double Wsum = 0.0;
    const vector_fp& molecW = bulkPhaseTP->molecularWeights();
    size_t nspBulk = bulkPhaseTP->nSpecies();
    for (size_t k = 0; k < nspBulk; k++) {
        kstart = iKin_ptr->kineticsSpeciesIndex(k, 1);
        fmt::print(oooo, "{:4d} {:>24s}   {:14g} {:14g}  {:14e}\n",
                   k, bulkPhaseTP->speciesName(k), C[k], x[k], src[kstart]);
        sum += x[k];
        Wsum += src[kstart] * molecW[k];
    }
    oooo << "Bulk Weight Growth Rate = " << Wsum << " kg/m^2/s" << endl;
    double gr = Wsum / dens;
    oooo << "Bulk Growth Rate = " << gr << " m/s" << endl;
    oooo << "Bulk Growth Rate = " << gr * 1.0E6 * 3600.
         << " microns / hour" << endl;
    oooo << "Density of bulk phase = " << dens << " kg / m^3 "<< endl;
    oooo << "                      = " << dens / 1.0E3
         <<" gm / cm^3 " << endl;
    oooo << "Sum of bulk mole fractions= " << sum << endl;
    oooo << endl;
}

void printSurf(ostream& oooo, ThermoPhase* surfPhaseTP,
               InterfaceKinetics* iKin_ptr, double* src)
{
    double x[MSSIZE];
    oooo.precision(3);
    string surfParticlePhaseName = surfPhaseTP->id();
    surfPhaseTP->getMoleFractions(x);
    size_t kstart = iKin_ptr->kineticsSpeciesIndex(0, 2);
    oooo << "Surface Phase:  " << surfParticlePhaseName
         << " (" << kstart << ")" << endl;
    double Temp = surfPhaseTP->temperature();
    double p = surfPhaseTP->pressure();
    oooo << "Surface Temperature = " << Temp << endl;
    oooo << "Surface Pressure    = " << p << endl;
    oooo << "                       Name      "
         << "   Coverage         SrcRate " << endl;
    double sum = 0.0;
    size_t nspSurf = surfPhaseTP->nSpecies();
    for (size_t k = 0; k < nspSurf; k++) {
        kstart = iKin_ptr->kineticsSpeciesIndex(0, 2);
        double srcK = src[kstart];
        if (fabs(srcK) < 1.0E-8) {
            srcK = 0.0;
        }
        fmt::print(oooo, "{:4d} {:>24s}   {:14g}   {:14e}\n",
                   k, surfPhaseTP->speciesName(k), x[k], srcK);
        sum += x[k];
    }
    oooo << "Sum of coverages = " << sum << endl;
}

int main(int argc, char** argv)
{
    string infile;
    int i, k;
    int ioflag = 1;
    // look for command-line options
    if (argc > 1) {
        string tok;
        for (int j = 1; j < argc; j++) {
            tok = string(argv[j]);
            if (tok[0] == '-') {
                int nopt = static_cast<int>(tok.size());
                for (int n = 1; n < nopt; n++) {
                    if (tok[n] == 'h') {
                        printUsage();
                        exit(0);
                    } else if (tok[n] == 'd') {
                        int lvl = 0;
                        if (j < (argc - 1)) {
                            string tokla = string(argv[j+1]);
                            if (strlen(tokla.c_str()) > 0) {
                                lvl = atoi(tokla.c_str());
                                n = nopt - 1;
                                j += 1;
                                ioflag = lvl;
                            }
                        }
                    } else {
                        printUsage();
                        exit(1);
                    }
                }
            } else if (infile == "") {
                infile = tok;
            } else {
                printUsage();
                exit(1);
            }
        }
    }
    if (infile == "") {
        infile = "diamond.cti";
    }

    try {
        /*************************************************************/

        /*
         *  FILL IN THESE NAMES FOR EACH PROBLEM
         */
        /*
         * ProblemNumber = 0 : diamond.cti
         *               = 1 : haca.cti
         */
        int ProblemNumber = 1;
        string gasPhaseName          = "gas";
        string bulkParticlePhaseName = "diamond";
        string surfParticlePhaseName = "diamond_100";
        if (ProblemNumber == 1) {
            gasPhaseName          = "gas";
            bulkParticlePhaseName = "soot";
            surfParticlePhaseName = "soot_interface";
        }

        /************************************************************/
        XML_Node* xc = get_XML_File(infile);
        XML_Node* const xg = (XML_Node*) findXMLPhase(xc, gasPhaseName);
        if (!xg) {
            printf("ERROR: Could not find gas phase named, %s, in file\n",
                   gasPhaseName.c_str());
            exit(-1);
        }
        ThermoPhase* gasTP = newPhase(*xg);
        size_t nspGas = gasTP->nSpecies();
        cout << "Number of species = " << nspGas << endl;

        XML_Node* const xd =
            (XML_Node*) findXMLPhase(xc, bulkParticlePhaseName);
        if (!xd) {
            printf("ERROR: Could not find bulk phase named, %s, in file\n",
                   bulkParticlePhaseName.c_str());
            exit(-1);
        }
        ThermoPhase* bulkPhaseTP = newPhase(*xd);
        size_t nspBulk = bulkPhaseTP->nSpecies();
        cout << "Number of species in bulk phase named " <<
             bulkParticlePhaseName << " = " << nspBulk << endl;


        XML_Node* const xs =
            (XML_Node*) findXMLPhase(xc, surfParticlePhaseName);
        if (!xs) {
            printf("ERROR: Could not find surf Particle phase named,"
                   "%s, in file\n",
                   surfParticlePhaseName.c_str());
            exit(-1);
        }
        ThermoPhase* surfPhaseTP = newPhase(*xs);
        size_t nsp_d100 = surfPhaseTP->nSpecies();
        cout << "Number of species in surface phase, " << surfParticlePhaseName
             << " = " << nsp_d100 << endl;

        vector<ThermoPhase*> phaseList { gasTP, bulkPhaseTP, surfPhaseTP };
        InterfaceKinetics* iKin_ptr = new InterfaceKinetics();
        importKinetics(*xs, phaseList, iKin_ptr);
        size_t nr = iKin_ptr->nReactions();
        cout << "Number of reactions = " << nr << endl;

        ofstream ofile("results2.txt");


        // create a second copy of the same surface phase
        // (this is a made up problem btw to check the software capability)
        ThermoPhase* surfPhaseTP2 = newPhase(*xs);
        size_t nsp2 = surfPhaseTP2->nSpecies();
        string pname = surfPhaseTP2->id();
        cout << "Number of species in 2nd surface phase, " << pname
             << " = " << nsp2 << endl;

        vector<ThermoPhase*> phaseList2 { gasTP, bulkPhaseTP, surfPhaseTP2 };

        // create the second  InterfaceKinetics object based on the
        // second surface phase.
        InterfaceKinetics* iKin2_ptr = new InterfaceKinetics();
        importKinetics(*xs, phaseList2, iKin2_ptr);
        nr = iKin_ptr->nReactions();
        cout << "Number of reactions = " << nr << endl;

        double x[MSSIZE], p = OneAtm;

        /*
         * Set the Gas State:
         * -> note that the states are set in the XML files too
         */
        for (i = 0; i < MSSIZE; i++) {
            x[i] = 0.0;
        }
        if (ProblemNumber == 0) {
            x[0] = 0.0010;
            x[1] = 0.9888;
            x[2] = 0.0002;
            x[3] = 0.0100;
            p = 20.0*OneAtm/760.0;
            gasTP->setState_TPX(1200., p, x);
        }

        /*
         * Set the surface initial state
         *  other problem numbers take their initial state from the XML files.
         */
        for (i = 0; i < MSSIZE; i++) {
            x[i] = 0.0;
        }
        if (ProblemNumber == 0) {
            size_t i0 = surfPhaseTP->speciesIndex("c6H*");
            if (i0 != npos) {
                x[i0] = 0.1;
            }
            size_t i1 = surfPhaseTP->speciesIndex("c6HH");
            if (i1 != npos) {
                x[i1] = 0.9;
            }
            surfPhaseTP->setState_TX(1200., x);
        }

        /*
         * Set the bulk Phase State
         */
        for (i = 0; i < MSSIZE; i++) {
            x[i] = 0.0;
        }
        if (ProblemNumber == 0) {
            x[0] = 1.0;
            bulkPhaseTP->setState_TPX(1200., p, x);
        }

        /*
         *  Set-up the Surface Problem
         *    This problem will consist of 2 identical InterfaceKinetics objects
         */
        vector<InterfaceKinetics*> vecKinPtrs { iKin_ptr, iKin2_ptr };

        // Create the ImplicitSurfChem problem
        // Initialize it and call the pseudo steadystate capability.
        ImplicitSurfChem* surfaceProb = new ImplicitSurfChem(vecKinPtrs);
        surfaceProb->initialize();
        surfaceProb->setIOFlag(ioflag);
        surfaceProb->solvePseudoSteadyStateProblem();

        /*
         * Download the source terms for the rate equations
         */
        double src[MSSIZE];
        double src2[MSSIZE];
        iKin_ptr->getNetProductionRates(src);
        iKin2_ptr->getNetProductionRates(src2);

        double sum = 0.0;
        if (ProblemNumber == 0) {
            double naH;
            for (k = 0; k < 13; k++) {
                if (k < 4) {
                    naH = gasTP->nAtoms(k, 0);
                } else if (k == 4) {
                    naH = 0;
                } else if (k > 4) {
                    int itp = k - 5;
                    naH = surfPhaseTP->nAtoms(itp, 0);
                }
                cout << k << "  " << naH << "  " ;
                if (fabs(src[k]) < 2.0E-17) {
                    cout << " nil" << endl;
                } else {
                    cout << src[k] << endl;
                }
                sum += naH * src[k];
            }
            cout << "sum = " << sum << endl;
        }


        printGas(cout, gasTP, iKin_ptr, src);
        printBulk(cout, bulkPhaseTP, iKin_ptr, src);
        printSurf(cout, surfPhaseTP, iKin_ptr, src) ;
        printSurf(cout, surfPhaseTP2, iKin2_ptr, src2) ;


        printGas(ofile, gasTP, iKin_ptr, src);
        printBulk(ofile, bulkPhaseTP, iKin_ptr, src);
        printSurf(ofile, surfPhaseTP, iKin_ptr, src) ;
        printSurf(ofile, surfPhaseTP2, iKin2_ptr, src2) ;

        /*****************************************************************************/
        /*  Now Tweak the inputs and do a quick calculation */
        /****************************************************************************/

        /*
         * Set the Gas State:
         * -> note that the states are set in the XML files too
         */
        double pres = gasTP->pressure();
        gasTP->getMoleFractions(x);
        double tmp = 0.3 * std::min(x[0], x[1]);
        x[0] += tmp;
        x[1] -= tmp;
        gasTP->setState_PX(pres, x);

        surfaceProb->solvePseudoSteadyStateProblem();
        iKin_ptr->getNetProductionRates(src);
        iKin2_ptr->getNetProductionRates(src2);

        printGas(cout, gasTP, iKin_ptr, src);
        printBulk(cout, bulkPhaseTP, iKin_ptr, src);
        printSurf(cout, surfPhaseTP, iKin_ptr, src) ;
        printSurf(cout, surfPhaseTP2, iKin2_ptr, src2) ;

        /*****************************************************************************/
        /*  Now Tweak the inputs and do a quick calculation */
        /****************************************************************************/

        pres = surfPhaseTP->pressure();
        double temp = surfPhaseTP->temperature();
        temp += 95;
        surfPhaseTP->setState_TP(temp, pres);

        surfaceProb->solvePseudoSteadyStateProblem();
        iKin_ptr->getNetProductionRates(src);
        iKin2_ptr->getNetProductionRates(src2);

        printGas(cout, gasTP, iKin_ptr, src);
        printBulk(cout, bulkPhaseTP, iKin_ptr, src);
        printSurf(cout, surfPhaseTP, iKin_ptr, src) ;
        printSurf(cout, surfPhaseTP2, iKin2_ptr, src2) ;

        /*****************************************************************************/
        /*  Now Don't Tweak the inputs at all */
        /****************************************************************************/
        gasTP->setState_TP(temp, pres);

        surfaceProb->solvePseudoSteadyStateProblem();
        iKin_ptr->getNetProductionRates(src);
        iKin2_ptr->getNetProductionRates(src2);

        printGas(cout, gasTP, iKin_ptr, src);
        printBulk(cout, bulkPhaseTP, iKin_ptr, src);
        printSurf(cout, surfPhaseTP, iKin_ptr, src) ;
        printSurf(cout, surfPhaseTP2, iKin2_ptr, src2) ;

        delete surfaceProb;
        surfaceProb = 0;
        delete iKin_ptr;
        iKin_ptr = 0;
        delete iKin2_ptr;
        iKin2_ptr = 0;
        delete gasTP;
        gasTP = 0;
        delete bulkPhaseTP;
        bulkPhaseTP = 0;
        delete surfPhaseTP;
        surfPhaseTP = 0;
        delete surfPhaseTP2;
        surfPhaseTP2 = 0;
        appdelete();
    } catch (CanteraError& err) {
        std::cout << err.what() << std::endl;
    }

    return 0;
}
/***********************************************************/
