/**
 * @file fctxml.cpp
 *
 */
// Copyright 2001  California Institute of Technology


#include "clib/clib_defs.h"
#include "cantera/base/ctml.h"

#include <cstring>
#include <fstream>

using namespace ctml;
using namespace std;
using Cantera::XML_Node;
using Cantera::CanteraError;
using Cantera::handleAllExceptions;

#include "clib/Cabinet.h"

typedef Cabinet<XML_Node, false> XmlCabinet;
template<> XmlCabinet* XmlCabinet::__storage = 0;

typedef integer status_t;

inline XML_Node* _xml(const integer* i)
{
    return &XmlCabinet::item(*i);
}

std::string f2string(const char* s, ftnlen n);

extern "C" {

    integer fxml_new_(const char* name, ftnlen namelen)
    {
        try {
            XML_Node* x;
            if (!name) {
                x = new XML_Node;
            } else {
                x = new XML_Node(f2string(name, namelen), 0);
            }
            return XmlCabinet::add(x);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_get_xml_file_(const char* file, ftnlen filelen)
    {
        try {
            XML_Node* x = Cantera::get_XML_File(f2string(file, filelen));
            int ix = XmlCabinet::add(x);
            return ix;
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_clear_()
    {
        try {
            XmlCabinet::clear();
            Cantera::close_XML_File("all");
            return 0;
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_del_(const integer* i)
    {
        try {
            XmlCabinet::del(*i);
            return 0;
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_removechild_(const integer* i, const integer* j)
    {
        try {
            _xml(i)->removeChild(_xml(j));
            return 0;
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_copy_(const integer* i)
    {
        try {
            return XmlCabinet::newCopy(*i);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_assign_(const integer* i, const integer* j)
    {
        try {
            return XmlCabinet::assign(*i,*j);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_attrib_(const integer* i, const char* key, char* value,
                          ftnlen keylen, ftnlen valuelen)
    {
        try {
            std::string ky = f2string(key, keylen);
            XML_Node& node = *_xml(i);
            if (node.hasAttrib(ky)) {
                std::string v = node[ky];
                strncpy(value, v.c_str(), valuelen);
            } else
                throw CanteraError("fxml_attrib","node "
                                   " has no attribute '"+ky+"'");
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_addattrib_(const integer* i, const char* key,
                             const char* value, ftnlen keylen, ftnlen valuelen)
    {
        try {
            std::string ky = f2string(key, keylen);
            std::string val = f2string(value, valuelen);
            XML_Node& node = *_xml(i);
            node.addAttribute(ky, val);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_addcomment_(const integer* i, const char* comment,
                              ftnlen commentlen)
    {
        try {
            std::string c = f2string(comment, commentlen);
            XML_Node& node = *_xml(i);
            node.addComment(c);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_tag_(const integer* i, char* tag, ftnlen taglen)
    {
        try {
            XML_Node& node = *_xml(i);
            const std::string v = node.name();
            strncpy(tag, v.c_str(), taglen);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_value_(const integer* i, char* value, ftnlen valuelen)
    {
        try {
            XML_Node& node = *_xml(i);
            const std::string v = node.value();
            strncpy(value, v.c_str(), valuelen);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_child_(const integer* i, const char* loc, ftnlen loclen)
    {
        try {
            XML_Node& node = *_xml(i);
            XML_Node& c = node.child(f2string(loc, loclen));
            return XmlCabinet::add(&c);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }

    status_t fxml_child_bynumber_(const integer* i, const integer* m)
    {
        try {
            XML_Node& node = *_xml(i);
            XML_Node& c = node.child(*m);
            return XmlCabinet::add(&c);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_findid_(const integer* i, const char* id, ftnlen idlen)
    {
        try {
            XML_Node& node = *_xml(i);
            XML_Node* c = node.findID(f2string(id, idlen));
            if (c) {
                return XmlCabinet::add(c);
            } else {
                throw CanteraError("fxml_find_id","id not found: "+f2string(id, idlen));
            }
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_findbyname_(const integer* i, const char* nm, ftnlen nmlen)
    {
        try {
            XML_Node& node = *_xml(i);
            XML_Node* c = node.findByName(f2string(nm, nmlen));
            if (c) {
                return XmlCabinet::add(c);
            } else
                throw CanteraError("fxml_findByName","name "+f2string(nm, nmlen)
                                   +" not found");
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    integer fxml_nchildren_(const integer* i)
    {
        try {
            XML_Node& node = *_xml(i);
            return node.nChildren();
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_addchild_(const integer* i, const char* name,
                            const char* value, ftnlen namelen, ftnlen valuelen)
    {
        try {
            XML_Node& node = *_xml(i);
            XML_Node& c = node.addChild(f2string(name, namelen),
                                        f2string(value,valuelen));
            return XmlCabinet::add(&c);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_addchildnode_(const integer* i, const integer* j)
    {
        try {
            XML_Node& node = *_xml(i);
            XML_Node& chld = *_xml(j);
            XML_Node& c = node.addChild(chld);
            return XmlCabinet::add(&c);
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t fxml_write_(const integer* i, const char* file, ftnlen filelen)
    {
        try {
            std::string ff(file, filelen);
            ofstream f(ff.c_str());
            if (f) {
                XML_Node& node = *_xml(i);
                node.write(f);
            } else {
                throw CanteraError("fxml_write",
                                   "file "+f2string(file, filelen)+" not found.");
            }
            return 0;
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

    status_t ctml_getfloatarray_(const integer* i, const integer* n,
                                 doublereal* data, const integer* iconvert)
    {
        try {
            XML_Node& node = *_xml(i);
            Cantera::vector_fp v;
            bool conv = false;
            if (*iconvert > 0) {
                conv = true;
            }
            getFloatArray(node, v, conv);
            int nv = v.size();

            // array not big enough
            if (*n < nv) {
                throw CanteraError("ctml_getfloatarray",
                                   "array must be dimensioned at least "+Cantera::int2str(nv));
            }

            for (int i = 0; i < nv; i++) {
                data[i] = v[i];
            }
            //n = nv;
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }

}
