/*******************************************************************************
 * Copyright (c) 2023, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include <iostream>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <vector>
#include <set>
#include <regex>

#include <htmlpp/html.h>
#include <htmlpp/exception.h>

#include <dbpp/database.h>

#include <conf.h>

#include <conf.h>
#include <plugin.h>
#include <i18n.h>


namespace blogi {
    class Template : public PluginApi {
    public:
        Template(){

        };

        ~Template(){

        };

        void initPlugin(){
            registerGPO("54809944-d07f-405f-9b5a-7f3644adac26",
                        "Blogi: Template Settings",
                        "Controls access to template management",
                        true);

            dbpp::SQL sql;
            dbpp::DBResult res;
            sql << "CREATE TABLE IF NOT EXISTS templates ("
                << "id integer PRIMARY KEY " << Args->database[0]->autoincrement(sql) << ","
                << "layer varchar(255),"
                << "html text,"
                << "type integer"
                << ");";
            Args->database[0]->exec(sql,res);

            // Auto-create empty templates for each layer found in theme HTML files
            autoCreateLayers();
        };

        bool haveSettings() override { return true; }

        json_object* Settings(const int tid, libhttppp::HttpRequest &req, const std::string &sessionid) override {
            std::string _lang = getLang(*Args->database[tid]);
            std::string url = req.getRequestURL();
            std::string base = Args->config->buildurl("settings/template/");

            // Sub-routing (like survey plugin pattern)
            if (url.length() > base.length()) {
                std::string sub = url.substr(base.length());
                if (sub.find("delete") == 0) {
                    return settingsDelete(tid, req, _lang);
                } else if (sub.find("edit") == 0) {
                    return settingsEdit(tid, req, _lang);
                }
            }

            return settingsIndex(tid, req, _lang);
        }

    private:
        // Extract all id="" values from an HTML file
        std::set<std::string> extractIdsFromFile(const std::string &filepath) {
            std::set<std::string> ids;
            std::ifstream f(filepath);
            if (!f.is_open()) return ids;

            std::string content((std::istreambuf_iterator<char>(f)),
                                 std::istreambuf_iterator<char>());
            f.close();

            std::regex idRegex("\\bid=[\"']([^\"']+)[\"']");
            std::sregex_iterator it(content.begin(), content.end(), idRegex);
            std::sregex_iterator end;
            for (; it != end; ++it) {
                ids.insert((*it)[1].str());
            }
            return ids;
        }

        void autoCreateLayers() {
            std::string tplDir = Args->config->gettemplate();

            // Scan index.html and mobile.html for IDs
            std::set<std::string> allIds;
            for (const char *file : {"index.html", "mobile.html"}) {
                std::string path = tplDir + "/" + file;
                auto ids = extractIdsFromFile(path);
                allIds.insert(ids.begin(), ids.end());
            }

            if (allIds.empty()) return;

            // Get existing layers from DB
            std::set<std::string> existingLayers;
            {
                dbpp::SQL sql;
                dbpp::DBResult res;
                sql << "SELECT DISTINCT layer FROM templates;";
                try {
                    int n = Args->database[0]->exec(sql, res);
                    for (int i = 0; i < n; ++i) {
                        if (res[i][0]) existingLayers.insert(res[i][0]);
                    }
                } catch (...) {}
            }

            // Skip IDs that are internal/editor-only (not useful as content layers)
            static const std::set<std::string> skipIds = {
                "banner", "copyrigt", "rendering", "toolbar", "plugin-list",
                "loading-plugins", "tpl-current", "tpl-current-name", "tpl-current-rev",
                "tpl-list", "tpl-status", "xml-import-input", "publish-panel",
                "publish-targets", "publish-form", "publish-status", "document-tree",
                "tree-root", "properties-panel", "prop-widget-type", "selected-element-id",
                "prop-status", "element-settings", "theme-palette", "theme-swatches",
                "preview", "addElementModal", "modal-title", "modalPluginName",
                "modalPluginTypeId", "modalParentUuid", "modalPosition", "modal-status",
                "modalAddButton", "view-controls", "desktopViewBtn", "mobileViewBtn",
                "maincontent", "layer1", "cookiebanner", "cookieoverlay",
                "cookiecontent", "cookiesettings", "cookiesubmit", "theme-toggle"
            };

            // Insert missing layers
            for (const auto &id : allIds) {
                if (skipIds.count(id)) continue;
                if (existingLayers.count(id)) continue;

                std::vector<char> sbuf;
                dbpp::SQL sql;
                dbpp::DBResult res;
                sql << "INSERT INTO templates (layer, html, type) VALUES ('"
                    << dbpp::SQL::escaped(sbuf, id.c_str()) << "', '', 0);";
                try {
                    Args->database[0]->exec(sql, res);
                } catch (...) {}
            }
        }

        json_object* settingsIndex(const int tid, libhttppp::HttpRequest &req, const std::string &_lang) {
            json_object *jroot = json_object_new_object();
            json_object_object_add(jroot, "title", json_object_new_string(tr(_lang, "Templates").c_str()));

            // Query all templates
            dbpp::SQL sql;
            dbpp::DBResult res;
            sql << "SELECT id, layer, type, length(html) FROM templates ORDER BY layer, type;";
            int n = 0;
            try { n = Args->database[tid]->exec(sql, res); } catch (...) {}

            // Table
            json_object *jtable = json_object_new_object();
            json_object *jheaders = json_object_new_array();
            json_object_array_add(jheaders, json_object_new_string("ID"));
            json_object_array_add(jheaders, json_object_new_string("Layer"));
            json_object_array_add(jheaders, json_object_new_string(tr(_lang, "Device Type").c_str()));
            json_object_array_add(jheaders, json_object_new_string(tr(_lang, "Content").c_str()));
            json_object_object_add(jtable, "headers", jheaders);

            json_object *jrows = json_object_new_array();
            for (int i = 0; i < n; ++i) {
                json_object *jrow = json_object_new_object();
                json_object *jcells = json_object_new_array();
                json_object_array_add(jcells, json_object_new_string(res[i][0] ? res[i][0] : ""));
                json_object_array_add(jcells, json_object_new_string(res[i][1] ? res[i][1] : ""));

                int devType = res[i][2] ? atoi(res[i][2]) : 0;
                const char *devLabel = devType == 1 ? "Desktop" : devType == 2 ? "Mobile" : "All";
                json_object_array_add(jcells, json_object_new_string(tr(_lang, devLabel).c_str()));

                int htmlLen = res[i][3] ? atoi(res[i][3]) : 0;
                std::string sizeStr = htmlLen > 0 ? (std::to_string(htmlLen) + " Bytes") : tr(_lang, "Empty");
                json_object_array_add(jcells, json_object_new_string(sizeStr.c_str()));

                json_object_object_add(jrow, "cells", jcells);

                // Actions
                json_object *jactions = json_object_new_array();

                // Edit link → opens editor with this template's layer
                json_object *jedit = json_object_new_object();
                json_object_object_add(jedit, "label", json_object_new_string(tr(_lang, "Edit").c_str()));
                json_object_object_add(jedit, "url", json_object_new_string(
                    (Args->config->buildurl("editor?template_id=") + std::string(res[i][0]) + "&template_name=" + std::string(res[i][1] ? res[i][1] : "")).c_str()));
                json_object_array_add(jactions, jedit);

                // Delete
                json_object *jdel = json_object_new_object();
                json_object_object_add(jdel, "label", json_object_new_string(tr(_lang, "Delete").c_str()));
                json_object_object_add(jdel, "url", json_object_new_string(
                    (Args->config->buildurl("settings/template/delete?id=") + std::string(res[i][0])).c_str()));
                json_object_array_add(jactions, jdel);

                json_object_object_add(jrow, "actions", jactions);
                json_object_array_add(jrows, jrow);
            }
            json_object_object_add(jtable, "rows", jrows);
            json_object_object_add(jroot, "table", jtable);

            // Links: Open Editor
            json_object *jlinks = json_object_new_array();
            json_object *jleditor = json_object_new_object();
            json_object_object_add(jleditor, "label", json_object_new_string(tr(_lang, "Open Editor").c_str()));
            json_object_object_add(jleditor, "url", json_object_new_string(
                Args->config->buildurl("editor").c_str()));
            json_object_array_add(jlinks, jleditor);
            json_object_object_add(jroot, "links", jlinks);

            return jroot;
        }

        json_object* settingsDelete(const int tid, libhttppp::HttpRequest &req, const std::string &_lang) {
            // Parse id from request parameters (GET/POST)
            int id = -1;
            libhttppp::HttpForm form;
            form.parse(req);
            for (const auto &cdat : form.urlData()) {
                if (cdat.key == "id")
                    id = atoi(cdat.value.c_str());
            }

            json_object *jroot = json_object_new_object();

            if (id > 0) {
                try {
                    dbpp::SQL sql;
                    dbpp::DBResult res;
                    sql << "DELETE FROM templates WHERE id = " << id << ";";
                    Args->database[tid]->exec(sql, res);
                    json_object_object_add(jroot, "message", json_object_new_string(tr(_lang, "Deleted!").c_str()));
                    json_object_object_add(jroot, "message_type", json_object_new_string("success"));
                } catch (const std::exception &e) {
                    json_object_object_add(jroot, "message", json_object_new_string(e.what()));
                    json_object_object_add(jroot, "message_type", json_object_new_string("error"));
                }
            }

            // Show list after delete
            json_object *idx = settingsIndex(tid, req, _lang);
            // Merge message into index result
            json_object *msg = nullptr, *mtype = nullptr;
            if (json_object_object_get_ex(jroot, "message", &msg)) {
                json_object_object_add(idx, "message", json_object_get(msg));
            }
            if (json_object_object_get_ex(jroot, "message_type", &mtype)) {
                json_object_object_add(idx, "message_type", json_object_get(mtype));
            }
            json_object_put(jroot);
            return idx;
        }

        json_object* settingsEdit(const int tid, libhttppp::HttpRequest &req, const std::string &_lang) {
            // Parse id from request parameters (GET/POST)
            int id = -1;
            libhttppp::HttpForm form;
            form.parse(req);
            for (const auto &cdat : form.urlData()) {
                if (cdat.key == "id")
                    id = atoi(cdat.value.c_str());
            }

            // Check for form submission (POST)
            bool saved = false;
            for (const auto &cur : form.urlData()) {
                if (cur.key == "tpl_layer") {
                    std::string layer = cur.value;
                    std::string html;
                    int type = 0;
                    for (const auto &f : form.urlData()) {
                        if (f.key == "tpl_html") html = f.value;
                        if (f.key == "tpl_type") type = atoi(f.value.c_str());
                    }
                    std::vector<char> sbuf;
                    dbpp::SQL sql;
                    dbpp::DBResult res;
                    sql << "UPDATE templates SET layer = '"
                        << dbpp::SQL::escaped(sbuf, layer.c_str()) << "', html = '"
                        << dbpp::SQL::escaped(sbuf, html.c_str()) << "', type = "
                        << type << " WHERE id = " << id << ";";
                    try {
                        Args->database[tid]->exec(sql, res);
                        saved = true;
                    } catch (...) {}
                    break;
                }
            }

            json_object *jroot = json_object_new_object();
            json_object_object_add(jroot, "title", json_object_new_string(
                (tr(_lang, "Edit Template") + " #" + std::to_string(id)).c_str()));

            if (saved) {
                json_object_object_add(jroot, "message", json_object_new_string(tr(_lang, "Saved!").c_str()));
                json_object_object_add(jroot, "message_type", json_object_new_string("success"));
            }

            // Load current values
            std::string layer, html;
            int type = 0;
            {
                dbpp::SQL sql;
                dbpp::DBResult res;
                sql << "SELECT layer, html, type FROM templates WHERE id = " << id << ";";
                try {
                    int n = Args->database[tid]->exec(sql, res);
                    if (n > 0) {
                        if (res[0][0]) layer = res[0][0];
                        if (res[0][1]) html = res[0][1];
                        type = res[0][2] ? atoi(res[0][2]) : 0;
                    }
                } catch (...) {}
            }

            // Nav
            json_object *jnav = json_object_new_array();
            json_object *jback = json_object_new_object();
            json_object_object_add(jback, "label", json_object_new_string(tr(_lang, "Back to list").c_str()));
            json_object_object_add(jback, "url", json_object_new_string(
                Args->config->buildurl("settings/template").c_str()));
            json_object_array_add(jnav, jback);

            json_object *jedlink = json_object_new_object();
            json_object_object_add(jedlink, "label", json_object_new_string(tr(_lang, "Open Editor").c_str()));
            json_object_object_add(jedlink, "url", json_object_new_string(
                Args->config->buildurl("editor").c_str()));
            json_object_array_add(jnav, jedlink);
            json_object_object_add(jroot, "nav", jnav);

            // Form
            json_object *jform = json_object_new_object();
            json_object_object_add(jform, "method", json_object_new_string("POST"));
            json_object_object_add(jform, "action", json_object_new_string(
                (Args->config->buildurl("settings/template/edit?id=") + std::to_string(id)).c_str()));

            json_object *jfields = json_object_new_array();

            // Layer field
            json_object *jf1 = json_object_new_object();
            json_object_object_add(jf1, "name", json_object_new_string("tpl_layer"));
            json_object_object_add(jf1, "label", json_object_new_string("Layer (Element ID)"));
            json_object_object_add(jf1, "type", json_object_new_string("text"));
            json_object_object_add(jf1, "value", json_object_new_string(layer.c_str()));
            json_object_array_add(jfields, jf1);

            // Type field
            json_object *jf2 = json_object_new_object();
            json_object_object_add(jf2, "name", json_object_new_string("tpl_type"));
            json_object_object_add(jf2, "label", json_object_new_string(tr(_lang, "Device Type").c_str()));
            json_object_object_add(jf2, "type", json_object_new_string("select"));
            json_object *topts = json_object_new_array();
            const char *typeLabels[] = {"All", "Desktop", "Mobile"};
            for (int t = 0; t <= 2; ++t) {
                json_object *to = json_object_new_object();
                json_object_object_add(to, "value", json_object_new_string(std::to_string(t).c_str()));
                json_object_object_add(to, "label", json_object_new_string(tr(_lang, typeLabels[t]).c_str()));
                if (t == type) json_object_object_add(to, "selected", json_object_new_boolean(true));
                json_object_array_add(topts, to);
            }
            json_object_object_add(jf2, "options", topts);
            json_object_array_add(jfields, jf2);

            // HTML field
            json_object *jf3 = json_object_new_object();
            json_object_object_add(jf3, "name", json_object_new_string("tpl_html"));
            json_object_object_add(jf3, "label", json_object_new_string("HTML"));
            json_object_object_add(jf3, "type", json_object_new_string("textarea"));
            json_object_object_add(jf3, "value", json_object_new_string(html.c_str()));
            json_object_array_add(jfields, jf3);

            json_object_object_add(jform, "fields", jfields);
            json_object_object_add(jform, "submit", json_object_new_string(tr(_lang, "save").c_str()));
            json_object_object_add(jroot, "form", jform);

            return jroot;
        }

    public:

        void Rendering(const int tid,libhttppp::HttpRequest &req,libhtmlpp::HtmlElement* curpage,int type,const std::string &sessionid=""){
            try {
                dbpp::SQL sql;
                dbpp::DBResult res;
                sql << "select id,layer,html,type from templates";

                std::string turl=req.getRequest();
                if(turl.rfind('?')>0){
                    turl=turl.substr(0,turl.rfind('?'));
                }

                int n = Args->database[tid]->exec(sql,res);
                if(n<1){
                    return;
                }

                for (int i = 0; i < n; i++) {
                    if(atoi(res[i][3])==RenderingType::All || type==atoi(res[i][3])){
                        if(!res[i][2] || res[i][2][0] == '\0')
                            continue;
                        json_object *params = json_object_new_object();
                        json_object_object_add(params, "layer", json_object_new_string(res[i][1]));
                        RenderPublished(tid, res[i][2], params, *curpage);
                        json_object_put(params);
                    }
                }
            } catch(const std::exception &e) {
                std::cerr << "template: DB error: " << e.what() << std::endl;
            } catch(...) {
                std::cerr << "template: DB unknown error" << std::endl;
            }
        }

        const std::string getAuthor(){
            return "Jan Koester";
        }

        const std::string getName(){
            return "template";
        }

        const std::string getVersion(){
            return "20241020";
        }

        void GetSiteMap(std::map<libhttppp::HttpUrl,std::chrono::year_month_day> &sitemap){
            return;
        }

        bool canPublish() override {
            return true;
        }

        void RenderPublished(const int /*tid*/, const std::string &html,
                             json_object *params, libhtmlpp::HtmlElement &page) override {
            std::string layer;
            json_object *val_obj = nullptr;
            if (params && json_object_object_get_ex(params, "layer", &val_obj)) {
                const char *v = json_object_get_string(val_obj);
                if (v) layer = v;
            }

            libhtmlpp::HtmlElement *target = nullptr;
            if (!layer.empty())
                target = page.getElementbyID(layer);
            if (!target)
                target = page.getElementbyID("main");

            if (target) {
                try {
                    libhtmlpp::HtmlString buf;
                    buf << html;
                    target->appendChild(buf.parse());
                } catch (libhtmlpp::HTMLException &e) {
                    std::cerr << "template: RenderPublished layer '" << layer
                              << "' HTML error: " << e.what() << std::endl;
                } catch (const std::exception &e) {
                    std::cerr << "template: RenderPublished layer '" << layer
                              << "' error: " << e.what() << std::endl;
                } catch (...) {
                    std::cerr << "template: RenderPublished layer '" << layer
                              << "' unknown error" << std::endl;
                }
            }
        }

        json_object* getPublishForm(const int tid) override {
            std::string _lang = getLang(*Args->database[tid]);
            json_object *fields = json_object_new_array();

            json_object *f_layer = json_object_new_object();
            json_object_object_add(f_layer, "key", json_object_new_string("layer"));
            json_object_object_add(f_layer, "label", json_object_new_string(tr(_lang,"Layer (Element ID)").c_str()));
            json_object_object_add(f_layer, "type", json_object_new_string("select"));

            json_object *layer_opts = json_object_new_array();
            json_object *custom_opt = json_object_new_object();
            json_object_object_add(custom_opt, "value", json_object_new_string("__custom__"));
            json_object_object_add(custom_opt, "label", json_object_new_string(tr(_lang,"Custom layer...").c_str()));
            json_object_array_add(layer_opts, custom_opt);

            try {
                dbpp::SQL sql;
                dbpp::DBResult res;
                sql << "SELECT DISTINCT layer FROM templates WHERE layer IS NOT NULL AND layer<>'' ORDER BY layer";
                int count = Args->database[tid]->exec(sql, res);
                for (int i = 0; i < count; ++i) {
                    if (!res[i][0]) continue;
                    json_object *opt = json_object_new_object();
                    json_object_object_add(opt, "value", json_object_new_string(res[i][0]));
                    json_object_object_add(opt, "label", json_object_new_string(res[i][0]));
                    json_object_array_add(layer_opts, opt);
                }
            } catch (...) {}

            json_object_object_add(f_layer, "options", layer_opts);
            json_object_array_add(fields, f_layer);

            json_object *f_layer_custom = json_object_new_object();
            json_object_object_add(f_layer_custom, "key", json_object_new_string("layer_custom"));
            json_object_object_add(f_layer_custom, "label", json_object_new_string(tr(_lang,"Custom Layer (Element ID)").c_str()));
            json_object_object_add(f_layer_custom, "type", json_object_new_string("text"));
            json_object_object_add(f_layer_custom, "placeholder", json_object_new_string("content"));
            json_object_array_add(fields, f_layer_custom);

            json_object *f_type = json_object_new_object();
            json_object_object_add(f_type, "key", json_object_new_string("type"));
            json_object_object_add(f_type, "label", json_object_new_string(tr(_lang,"Device Type").c_str()));
            json_object_object_add(f_type, "type", json_object_new_string("select"));
            json_object *opts = json_object_new_array();
            json_object *opt0 = json_object_new_object();
            json_object_object_add(opt0, "value", json_object_new_string("0"));
            json_object_object_add(opt0, "label", json_object_new_string(tr(_lang,"All").c_str()));
            json_object_array_add(opts, opt0);
            json_object *opt1 = json_object_new_object();
            json_object_object_add(opt1, "value", json_object_new_string("1"));
            json_object_object_add(opt1, "label", json_object_new_string("Desktop"));
            json_object_array_add(opts, opt1);
            json_object *opt2 = json_object_new_object();
            json_object_object_add(opt2, "value", json_object_new_string("2"));
            json_object_object_add(opt2, "label", json_object_new_string("Mobile"));
            json_object_array_add(opts, opt2);
            json_object_object_add(f_type, "options", opts);
            json_object_array_add(fields, f_type);

            return fields;
        }

        void Publish(const int tid, const std::string &html, json_object *params, json_object *response) override {
            std::string _lang = getLang(*Args->database[tid]);

            std::string layer;
            std::string layerCustom;
            int type = 0;

            if (params) {
                json_object *val_obj = nullptr;
                if (json_object_object_get_ex(params, "layer", &val_obj)) {
                    const char *v = json_object_get_string(val_obj);
                    if (v) layer = v;
                }
                if (json_object_object_get_ex(params, "layer_custom", &val_obj)) {
                    const char *v = json_object_get_string(val_obj);
                    if (v) layerCustom = v;
                }
                if (json_object_object_get_ex(params, "type", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        type = atoi(json_object_get_string(val_obj));
                    } else {
                        type = json_object_get_int(val_obj);
                    }
                }
            }

            if (layer == "__custom__") {
                layer = layerCustom;
            }

            if (layer.empty()) {
                json_object_object_add(response, "status", json_object_new_string("error"));
                json_object_object_add(response, "message", json_object_new_string(tr(_lang,"Layer is required.").c_str()));
                return;
            }

            try {
                std::vector<char> sbuf;
                dbpp::SQL sql;
                dbpp::DBResult res;

                // Check for existing template with same layer + type
                bool overwrite = false;
                if (params) {
                    json_object *ow_obj = nullptr;
                    if (json_object_object_get_ex(params, "overwrite", &ow_obj)) {
                        overwrite = json_object_get_boolean(ow_obj);
                    }
                }

                sql << "SELECT id FROM templates WHERE layer='"
                    << dbpp::SQL::escaped(sbuf, layer.c_str()) << "' AND type=" << type << " LIMIT 1;";
                int dupCount = Args->database[tid]->exec(sql, res);

                if (dupCount > 0 && !overwrite) {
                    json_object_object_add(response, "status", json_object_new_string("duplicate"));
                    json_object_object_add(response, "message",
                        json_object_new_string(tr(_lang,"A template with this layer and device type already exists. Overwrite?").c_str()));
                    json_object_object_add(response, "existing_id", json_object_new_int(atoi(res[0][0])));
                    return;
                } else if (dupCount > 0 && overwrite) {
                    int eid = atoi(res[0][0]);
                    sql.clear();
                    sql << "UPDATE templates SET html='"
                        << dbpp::SQL::escaped(sbuf, html.c_str())
                        << "' WHERE id=" << eid << ";";
                    Args->database[tid]->exec(sql, res);
                } else {
                    sql.clear();
                    sql << "INSERT INTO templates (layer, html, type) VALUES ('"
                        << dbpp::SQL::escaped(sbuf, layer.c_str()) << "', '"
                        << dbpp::SQL::escaped(sbuf, html.c_str()) << "', "
                        << type << ");";
                    Args->database[tid]->exec(sql, res);
                }

                json_object_object_add(response, "status", json_object_new_string("success"));
                json_object_object_add(response, "message", json_object_new_string(tr(_lang,"Published to template layer!").c_str()));
            } catch (const std::exception &e) {
                json_object_object_add(response, "status", json_object_new_string("error"));
                json_object_object_add(response, "message", json_object_new_string(e.what()));
            }
        }

    };

    EXPORT PluginApi* create() {
        return new Template();
    }

    EXPORT void destroy(PluginApi* p) {
        delete p;
    }
};
