/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

// include the needed files
#include <cstring>
#include <fstream>
#include <lml/Force.h>
#include <lml/Loads.h>
#include <lml/Translation.h>
#include <ostream>

void testActivation(std::string imlFile, double dt, std::string outcsv = "") {
    // read the loads
    Loads list(imlFile.c_str());

    // search for the first event
    double tmin = list.getFirstEventDate();

    // search for the last event
    double tmax = list.getLastEventDate();

    std::cout << "Checking load activation, with time = [" << tmin << ".." << tmax << "], dt="
              << dt << std::endl;

    // open the output file if needed
    std::ofstream outStream;

    if (outcsv != "") {
        outStream.open(outcsv.c_str());
    }

    if (outcsv != "") {
        outStream << "t ";

        for (unsigned int i = 0; i < list.numberOfLoads(); i++) {
            outStream << list.getLoad(i)->getType() << " ";
        }

        outStream << std::endl;
    }

    // loop from tmin to tmax and display each list of loads at each time t
    double t = tmin - dt;
    while (t <= tmax + dt) {
        std::cout << "---------------------------" << std::endl;
        std::cout << "time t=" << t << std::endl;

        if (outcsv != "") {
            outStream << t << " ";
        }

        // check all the loads
        for (unsigned int i = 0; i < list.numberOfLoads(); i++) {
            Load* l = list.getLoad(i);

            // print the value
            if (l->isActive(t)) {
                std::cout << "\t- active ";
                std::cout << l->getType() << ": ";

                if (l->getDirection().isToward()) {
                    std::cout << "dir toward Atom#" << l->getDirection().getToward() << ", ";
                }
                else {
                    std::cout << "dir=(";

                    // x
                    if (l->getDirection().isXSpecified()) {
                        std::cout << l->getDirection().getX();
                    }
                    else if (l->getDirection().isXNull()) {
                        std::cout << "NULL";
                    }
                    else {
                        std::cout << "free";
                    }

                    std::cout << ",";

                    // y
                    if (l->getDirection().isYSpecified()) {
                        std::cout << l->getDirection().getY();
                    }
                    else if (l->getDirection().isYNull()) {
                        std::cout << "NULL";
                    }
                    else {
                        std::cout << "free";
                    }

                    std::cout << ",";

                    // z
                    if (l->getDirection().isZSpecified()) {
                        std::cout << l->getDirection().getZ();
                    }
                    else if (l->getDirection().isZNull()) {
                        std::cout << "NULL";
                    }
                    else {
                        std::cout << "free";
                    }

                    std::cout << "), ";
                }

                std::cout << "val=" << l->getValue(t);
                std::cout << std::endl;

                if (outcsv != "") {
                    outStream << l->getValue(t) << " ";
                }
            }
            else {
                if (outcsv != "") {
                    outStream << 0.0 << " ";
                }
            }
        }

        if (outcsv != "") {
            outStream << std::endl;
        }

        // next step
        t += dt;
    }

    if (outcsv != "") {
        outStream.close();
    }
}


int main(int argc, char* argv[]) {
    // test param
    std::string ifile;
    bool inputTest;
    std::string ofile;
    bool outputTest;
    double dt;
    bool activationTest;
    std::string afile;
    bool ansysOutput;
    std::string csvfile = "";

    // test the arguments
    inputTest = outputTest = activationTest = ansysOutput = false;

    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-i")) {
            ifile = argv[i + 1];
            inputTest = true;
        }

        if (!strcmp(argv[i], "-o")) {
            ofile = argv[i + 1];
            outputTest = true;
        }

        if (!strcmp(argv[i], "-dt")) {
            dt = atof(argv[i + 1]);
            activationTest = true;
        }

        if (!strcmp(argv[i], "-a")) {
            afile = argv[i + 1];
            ansysOutput = true;
        }

        if (!strcmp(argv[i], "-plot")) {
            csvfile = argv[i + 1];
        }
    }

    if ((!activationTest && !inputTest && !outputTest) ||
            (!inputTest && ansysOutput) // can't do an ansys output if there are no input file
       ) {
        if (argc > 1) {
            std::cout << "Argument errors: ";
            for (int i = 1; i < argc; i++) {
                std::cout << "[" << argv[i] << "] ";
            }
        }
        std::cout << std::endl;
        std::cout << "usage: lmltest [-i file.xml] [-o out.xml] [-dt dt] [-a ansys.mac] [-plot out.csv]" << std::endl;
        std::cout << "\t-i in.xml\treading test: read the file, create object and then print them on stdout" << std::endl;
        std::cout << "\t-o out.xml\twriting test: a file 'out.xml' will be created with synthesis data and then re-read for testing" << std::endl;
        std::cout << "\t-dt dt\tactivation test: the delta t used to check load activations (if not given, no activation test)" << std::endl;
        std::cout << "\t-plot out.csv\toptional for the activation test: output a simple csv file that can be use to plot the loads values along time (column1=time)" << std::endl;
        std::cout << "\t-a ansys.mac\tprint out the input file in ansys format" << std::endl;
        std::cout << std::endl;
        std::cout << "LML " << Loads::VERSION << std::endl;
        if (argc > 1) {
            return EXIT_FAILURE;
        }
        else {
            return EXIT_SUCCESS;
        }
    }

    if (inputTest) {
        Loads allLoads(ifile);
        allLoads.xmlPrint(std::cout);

        if (ansysOutput) {
            std::cout << "ANSYS translation to: " << afile << std::endl;
            std::ofstream outputFile;
            outputFile.open(afile.c_str());
            allLoads.ansysPrint(outputFile);
            outputFile.close();
        }
    }

    if (outputTest) {
        // 1. create a new synthetic load list
        Loads l;

        // 2. add a translation
        Translation* t = new Translation();

        t->addTarget("1-20");
        t->setDirection(0.21, 12.0, 5.39);
        t->addEvent(new ValueEvent(10.0, 0.5));
        t->addEvent(new ValueEvent(100.0, 1.5));
        t->setUnit(TranslationUnit::MM());

        // insert the translation t in l
        l.addLoad(t);

        // 3 . add a force
        Force* f = new Force();

        f->addTarget("1-5,7,10-12");
        f->setDirection(0.21, 12.0, 5.39);
        f->addEvent(new ValueEvent(0.0, 1.0));
        f->addEvent(new ValueEvent(15.0, 2.0));
        f->addEvent(new ValueEvent(0.0, 3.0));
        f->setUnit(ForceUnit::KN());

        // insert the translation t in l
        l.addLoad(f);

        // 4. print to file
        std::cout << "Writing test loads to:" << argv[1] << std::endl;
        std::ofstream outputFile;
        outputFile.open(ofile.c_str());
        l.xmlPrint(outputFile);
        outputFile.close();

        // 5. Try to read it again and print to stdout
        std::cout << "Printing of loads from: " << ifile << std::endl;
        Loads l2(ofile);
        l2.xmlPrint(std::cout);
    }

    // test the load activations, using time step=0.1
    if (activationTest) {
        if (inputTest) {
            testActivation(ifile, dt, csvfile);
        }

        if (outputTest) {
            testActivation(ofile, dt, csvfile);
        }
    }

    return EXIT_SUCCESS;
}
