
//OpenSCADA module DAQ.JavaLikeCalc file: freelib.cpp
/***************************************************************************
 *   Copyright (C) 2005-2023 by Roman Savochenko, <roman@oscada.org>       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; version 2 of the License.               *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include <tsys.h>
#include <tmess.h>

#include "freefunc.h"
#include "virtual.h"
#include "freelib.h"

using namespace JavaLikeCalc;

//*************************************************
//* Lib: Functions library                        *
//*************************************************
Lib::Lib( const string &id, const string &name, const string &lib_db ) :
    TConfig(&mod->elLib()), runSt(false), mDB(lib_db), mId(cfg("ID"))
{
    mId = id;
    cfg("NAME").setS(name);
    cfg("DB").setS("flb_"+id);	//????[v1.0] Remove, saved for the new libraries work on old OpenSCADA versions
    mFnc = grpAdd("fnc_");
    if(DB().empty()) modifClr();
}

Lib::~Lib( )
{

}

TCntrNode &Lib::operator=( const TCntrNode &node )
{
    const Lib *src_n = dynamic_cast<const Lib*>(&node);
    if(!src_n) return *this;

    //Configuration copy
    exclCopy(*src_n, "ID;DB;");
    setDB(src_n->DB());

    //Functions copy
    vector<string> ls;
    src_n->list(ls);
    for(unsigned iP = 0; iP < ls.size(); iP++) {
	if(!present(ls[iP])) add(ls[iP].c_str());
	(TCntrNode&)at(ls[iP]).at() = (TCntrNode&)src_n->at(ls[iP]).at();
    }
    if(src_n->startStat() && !startStat()) setStart(true);

    return *this;
}

void Lib::preDisable( int flag )
{
    setStart(false);
}

void Lib::postDisable( int flag )
{
    if(flag&(NodeRemove|NodeRemoveOnlyStor) && DB().size()) {
	//Delete libraries record
	TBDS::dataDel(DB(flag&NodeRemoveOnlyStor)+"."+mod->libTable(), mod->nodePath()+"lib/", *this, TBDS::UseAllKeys);

	//Delete function's files
	TBDS::dataDelTbl(fullDB(flag&NodeRemoveOnlyStor), mod->nodePath()+tbl());
	TBDS::dataDelTbl(fullDB(flag&NodeRemoveOnlyStor)+"_io", mod->nodePath()+tbl()+"_io");

	if(flag&NodeRemoveOnlyStor) { setStorage(mDB, "", true); return; }
    }
}

string Lib::name( )
{
    string tNm = cfg("NAME").getS();
    return tNm.size() ? tNm : mId;
}

void Lib::setFullDB( const string &vl )
{
    int off = vl.size();
    cfg("DB").setS(TSYS::strParseEnd(vl,0,".",&off));
    setDB(vl.substr(0,off+1));
}

void Lib::load_( TConfig *icfg )
{
    if(DB().empty() || (!SYS->chkSelDB(DB())))	throw TError();

    if(icfg) *(TConfig*)this = *icfg;
    else TBDS::dataGet(DB()+"."+mod->libTable(), mod->nodePath()+"lib/", *this);

    //Load functions
    map<string, bool>   itReg;
    TConfig cEl(&mod->elFnc());
    cEl.cfg("FORMULA").setExtVal(true);
    //cEl.cfgViewAll(false);
    for(int fldCnt = 0; TBDS::dataSeek(fullDB(),mod->nodePath()+tbl(),fldCnt++,cEl,TBDS::UseCache); ) {
	string fId = cEl.cfg("ID").getS();
	if(!present(fId)) add(fId);
	at(fId).at().load(&cEl);
	itReg[fId] = true;
    }

    // Check for remove items removed from DB
    if(SYS->chkSelDB(SYS->selDB(),true)) {
	vector<string> itLs;
	list(itLs);
	for(unsigned iIt = 0; iIt < itLs.size(); iIt++)
	    if(itReg.find(itLs[iIt]) == itReg.end())
		del(itLs[iIt]);
    }
}

void Lib::save_( )
{
    if(DB().empty())	return;

    TBDS::dataSet(DB()+"."+mod->libTable(), mod->nodePath()+"lib/", *this);
    setDB(DB(), true);
}

void Lib::setStart( bool val )
{
    runSt = val;

    vector<string> lst;
    list(lst);
    for(unsigned iF = 0; iF < lst.size(); iF++)
	try { if(!val || at(lst[iF]).at().toStart()) at(lst[iF]).at().setStart(val); }
	catch(TError &err) { mess_err(err.cat.c_str(), "%s", err.mess.c_str()); }
}

void Lib::add( const string &id, const string &name )	{ chldAdd(mFnc, new Func(id,name)); }

void Lib::del( const string &id )			{ chldDel(mFnc, id); }

TVariant Lib::objFuncCall( const string &iid, vector<TVariant> &prms, const string &user_lang )
{
    if(present(iid))	return at(iid).at().objFuncCall("call", prms, user_lang);

    return TCntrNode::objFuncCall(iid, prms, user_lang);
}

void Lib::cntrCmdProc( XMLNode *opt )
{
    //Get page info
    if(opt->name() == "info") {
	TCntrNode::cntrCmdProc(opt);
	XMLNode *nd = ctrMkNode3("oscada_cntr",opt,-1,"/",_("Library of the functions: ")+id(),RWRWR_);
	if(nd)	nd->setAttr("doc", TUIS::docKeyGet(descr()));
	if(ctrMkNode3("branches",opt,-1,"/br","",R_R_R_))
	    ctrMkNode3("grp",opt,-1,"/br/fnc_",_("Function"),RWRWR_, "idm",i2s(limObjNm_SZ).c_str(), "idSz",i2s(limObjID_SZ).c_str());
	if(ctrMkNode3("area",opt,-1,"/lib",_("Library"),R_R_R_)) {
	    if(ctrMkNode3("area",opt,-1,"/lib/st",_("State"),R_R_R_)) {
		ctrMkNode3("fld",opt,-1,"/lib/st/st",_("Accessible"),RWRWR_, "tp","bool");
		if(DB().size()) {
		    if(isStdStorAddr())
			ctrMkNode3("fld",opt,-1,"/lib/st/db",_("Storage"),SEC_RD|SEC_WR,
			    "tp","str", "dest","select", "select","/db/list", "help",TMess::labStor().c_str());
		    else ctrMkNode3("fld",opt,-1,"/lib/st/db",_("Library DB"),SEC_RD|SEC_WR,
			    "tp","str", "dest","sel_ed", "select",("/db/tblList:flb_"+id()).c_str(),
			    "help",TSYS::strMess(_("Storage address in the format \"{DB module}.{DB name}.{Table name}\".\nTo use %s, set '%s.{Table name}'."),
					TMess::labStorFromCode(DB_GEN).c_str(),DB_GEN).c_str());
		    if(DB(true).size())
			ctrMkNode3("comm",opt,-1,"/lib/st/removeFromDB",TSYS::strMess(_("Remove from '%s'"),
			    TMess::labStorFromCode(DB(true)).c_str()).c_str(),RWRW__, "help",TMess::labStorRem(mDB).c_str());
		    ctrMkNode3("fld",opt,-1,"/lib/st/timestamp",_("Date of modification"),R_R_R_, "tp","time");
		}
	    }
	    if(ctrMkNode3("area",opt,-1,"/lib/cfg",_("Configuration"),SEC_RD)) {
		ctrMkNode3("fld",opt,-1,"/lib/cfg/ID",_("Identifier"),SEC_RD, "tp","str");
		ctrMkNode3("fld",opt,-1,"/lib/cfg/NAME",_("Name"),SEC_RD|(DB().empty()?0:SEC_WR), "tp","str", "len",i2s(limObjNm_SZ).c_str());
		ctrMkNode3("fld",opt,-1,"/lib/cfg/DESCR",_("Description"),SEC_RD|(DB().empty()?0:SEC_WR),
		    "tp","str", "cols","100", "rows","5", "SnthHgl","1");
	    }
	}
	if(ctrMkNode3("area",opt,-1,"/func",_("Functions"),R_R_R_))
	    ctrMkNode3("list",opt,-1,"/func/func",_("Functions"),RWRWR_,
		"tp","br", "idm",i2s(limObjNm_SZ).c_str(), "s_com","add,del", "br_pref","fnc_", "idSz",i2s(limObjID_SZ).c_str());
	return;
    }

    //Process command to page
    string a_path = opt->attr("path");
    if(a_path == "/lib/st/st") {
	if(ctrChkNode3(opt,"get",RWRWR_,SEC_RD)) opt->setText(startStat() ? "1" : "0");
	if(ctrChkNode3(opt,"set",RWRWR_,SEC_WR)) setStart(s2i(opt->text()));
    }
    else if(a_path == "/lib/st/db" && DB().size()) {
	if(ctrChkNode2(opt,"get",SEC_RD)) opt->setText(isStdStorAddr()?DB():fullDB());
	if(ctrChkNode2(opt,"set",SEC_WR)) isStdStorAddr() ? setDB(opt->text()) : setFullDB(opt->text());
    }
    else if(a_path == "/lib/st/removeFromDB" && ctrChkNode3(opt,"set",RWRW__,SEC_WR))
	postDisable(NodeRemoveOnlyStor);
    else if(a_path == "/lib/st/timestamp" && ctrChkNode3(opt,"get",R_R_R_,SEC_RD)) {
	vector<string> tls;
	list(tls);
	time_t maxTm = 0;
	for(size_t iT = 0; iT < tls.size(); iT++) maxTm = vmax(maxTm, at(tls[iT]).at().timeStamp());
	opt->setText(i2s(maxTm));
    }
    else if(a_path == "/lib/cfg/ID" && ctrChkNode2(opt,"get",SEC_RD))	opt->setText(id());
    else if(a_path == "/lib/cfg/NAME") {
	if(ctrChkNode2(opt,"get",SEC_RD)) opt->setText(trD(name()));
	if(ctrChkNode2(opt,"set",SEC_WR)) setName(trDSet(name(),opt->text()));
    }
    else if(a_path == "/lib/cfg/DESCR") {
	if(ctrChkNode2(opt,"get",SEC_RD)) opt->setText(trD(descr()));
	if(ctrChkNode2(opt,"set",SEC_WR)) setDescr(trDSet(descr(),opt->text()));
	if(ctrChkNode2(opt,"SnthHgl",SEC_RD)) { nodeLoadACLSnthHgl(*opt); nodeDocSnthHgl(*opt); }
    }
    else if(a_path == "/br/fnc_" || a_path == "/func/func") {
	if(ctrChkNode3(opt,"get",RWRWR_,SEC_RD)) {
	    vector<string> lst;
	    list(lst);
	    for(unsigned iF = 0; iF < lst.size(); iF++)
		opt->childAdd("el")->setAttr("id",lst[iF])->setText(trD(at(lst[iF]).at().name()));
	}
	if(ctrChkNode3(opt,"add",RWRWR_,SEC_WR)) add(TSYS::strEncode(opt->attr("id"),TSYS::oscdID).c_str(),opt->text().c_str());
	if(ctrChkNode3(opt,"del",RWRWR_,SEC_WR)) chldDel(mFnc,opt->attr("id"), -1, NodeRemove);
    }
    else if(a_path == "/func/ls_lib" && ctrChkNode3(opt,"get",R_R_R_,SEC_RD)) {
	vector<string> lst;
	opt->childAdd("el")->setAttr("id","")->setText("");
	mod->lbList(lst);
	for(unsigned iA = 0; iA < lst.size(); iA++)
	    opt->childAdd("el")->setAttr("id",lst[iA])->setText(mod->lbAt(lst[iA]).at().name());
    }
    else TCntrNode::cntrCmdProc(opt);
}
