//
// Created by Ht on 2021/5/10.
//

#ifndef INC_0_LIBDEVCPP_JAINDEXEDDOM_H
#define INC_0_LIBDEVCPP_JAINDEXEDDOM_H

#include <utility>

#include "JABaseDom.h"

namespace ja {

    static const char* sval_cls_name_stored = "stored";
    static const char* sval_cls_name_stored_owner = "storedOwner";
    static const char* sval_cls_name_task = "task";
    static const char* sval_cls_name_task_owner = "taskOwner";

#define IDX_CONTEXT_OF(jao) \
    (*dynamic_cast<ja::JAIndexNodeContext*>(&((jao).getContext())))

#define IDX_INSTANCE_OF(jao) \
    (dynamic_cast<ja::JAStoredObjectBase*>(&jao)->getInstance())

#define IDX_INSTANCE(i) \
    (dynamic_cast<ja::JAIndexedInstance&>(i))

#define PIDX_INSTANCE(i) \
    (dynamic_cast<ja::JAIndexedInstance*>(i))

#define IS_IDX_INSTANCE(i) \
    (dynamic_cast<ja::JAIndexedInstance*>(&i)!=nullptr)

#define IS_STORED_OBJ(jao) \
    (dynamic_cast<ja::JAStoredObjectBase*>(&jao)!=nullptr)

#define STORED_OBJ(jao) \
    (dynamic_cast<ja::JAStoredObject&>(jao))

#define IS_OWNER_OBJ(obj) \
    (dynamic_cast<ja::JAObjectOwnerBase*>(&obj)!=nullptr)

#define IS_STORED_OWNER_OBJ(obj) \
    (dynamic_cast<ja::JAStoredOwner*>(&obj)!=nullptr)

#define STORED_OWNER_OBJ(obj) \
    (dynamic_cast<ja::JAStoredOwner&>(obj))

    class JAIndexedInstance;
    using JARIndexedInstance = std::shared_ptr<JAIndexedInstance>;

    class JE_API JAStoredObjectBase {
    protected:
        QStringList* index_key_list_template_ = nullptr;        //键值索引模板
        QStringList* index_key_list_val_ = nullptr;             //运行时索引键值
        bool key_updated_ = false;
        std::mutex                             update_;
        ja::JAObject&                          object_;
        ja::JARIndexedInstance                 instance_;
        std::string                            path_;
        bool val_updated_ = false;

    public:
        explicit JAStoredObjectBase(ja::JAObject& object, ja::JAIndexedInstance& inst, std::string path);

        explicit JAStoredObjectBase(ja::JAObject& object, std::string path);

        virtual ~JAStoredObjectBase() {
            delete index_key_list_val_;
            delete index_key_list_template_;
        }

        inline ja::JARIndexedInstance getInstance(){ return instance_; }

        QStringList & getIndexList();

        virtual void saveObjectToFile(const std::string& path = "");

        virtual void reloadObjectFromFile();

        virtual void removeObjectFile();

        virtual void updateProperties(const qlibc::QData& source, const std::string& keylist);
        virtual void updateProperties(const qlibc::QData& source, const QStringList& keylist);

        //给出模板键值、返回运行值
        const std::string& addIndexKey(const std::string& k);
        bool removeIndexKey(const std::string& k);

        const std::string& PropertyKey(const char* key_type) const;

        void setStoredName(const std::string &name);

        string getStoredName() const;

        void checkName();

        string getType() const ;

        void setType(const std::string &type);

        std::string getObjectId();

        void setObjectId(const std::string& id);

        static void randomObjectName(string &nametarget, const std::string& type);

        static std::string randomObjectId(){
            std::string id;
            StringUtils::formatString(id,"JAO_ID_%06X", NumberUtils::randomIntger(0X100000,0XFFFFFF));
            return id;
        }


        bool isUpdated() {
            return val_updated_;
        }
    };

    class JE_API JAStoredObject : public JAObject, public JAStoredObjectBase {
    public:
        explicit JAStoredObject(JAContext& ctx, JAIndexedInstance& inst, const qlibc::QData &data, const std::string& path) :
                JAObject(ctx, data), JAStoredObjectBase(*this, inst, path) {
        }

        qlibc::QData& put(const std::string &key, const Json::Value &val) override;

        qlibc::QData &setBool(const std::string &key, bool val) override;

        qlibc::QData &setInt(const std::string &key, int val) override;

        qlibc::QData &setString(const std::string &key, const std::string &val) override;

        qlibc::QData &putString(const std::string &key, const std::string &val) override;

        qlibc::QData &setValue(const std::string &key, const Json::Value &val) override;

        qlibc::QData &setName(const std::string &name) override;

        void setType(const std::string &type) override;

        void initWithParaList(str_vector vl) override;

        void initWithJsonStr(const std::string &str) override;

        QData &setInitData(const QData &data) override;

        QData &setInitValue(const Json::Value &value) override;

        QData &putObjFmtString(const std::string &key, const std::string &value) override;

        QData &clearObjFmtKey(const std::string &key) override;

        QData &removeObjFmtKey(const std::string &key) override;

        QData &putObjFmtValue(const std::string &key, const Json::Value &value) override;

        void copyObjFmtValue(const QData &src, const std::string &key) override;

        QData &setObjFmtInt(const std::string &key, int value) override;

        QData &copyData(const QData &data) override;

        QData &copyData(const QData &data, const std::string &key) override;

        QData &copyDataIfNotExists(const QData &data) override;

        QData &copyData(const QData &data, const char *key, ...) override;

        QData &copyDataNotStartWith(const QData &data, char start) override;

        QData &copyDataNotStartWith(const QData &data, const std::string &start) override;

        QData &copyDataStartWith(const QData &data, char start) override;

        QData &copyDataStartWith(const QData &data, const std::string &start) override;

        QData &clearDataStartWith(const std::string &start) override;

        QData &clearDataNotStartWith(const std::string &start) override;

        QData &expandCompactedField() override;

        QData &compactField(const std::string &field) override;

        static ja::JAObject* creator(ja::JAContext& ctx,  ja::JAInstance& instance, const std::string &key, const qlibc::QData& data);

        bool hasNoIndex();
    };

    using JARStoredObject = std::shared_ptr<JAStoredObject>;

    class JE_API JATask : public ja::JAObject {
    protected:
        JAHandler* handler = nullptr;      //任务入口
        //任务达成条件
        //任务校验频率
        //任务所属容器
    public:
        explicit JATask(ja::JAContext& ctx, const std::string& clsName = sval_cls_name_task);

        static ja::JAObject* creator(ja::JAContext &ctx, JAInstance &instance, const std::string &key, const qlibc::QData &data);
    };


    template <typename T>
    class JE_API JAObjectNameIndexer {
    protected:
        //Name->ID
        qlibc::QPObjectHolder<T>                         mIndexMap_;  //
        qlibc::QPObjectHolder<JAObjectNameIndexer<T>>*   mSubIndex_ = nullptr;

    public:
        explicit JAObjectNameIndexer() : mIndexMap_(false) {
        }

        T* findTarget(const std::string& key) {
            std::string mainKey = StringUtils::getSubStrBefore(key, '.', 1);
            std::string subKey = StringUtils::getSubStrAfter(key, '.', 1);
            if(subKey.empty())
                return mIndexMap_.findObject(mainKey);
            if(mSubIndex_==nullptr) return nullptr;
            auto sub = mSubIndex_->findObject(mainKey);
            return sub== nullptr?nullptr:sub->findTarget(subKey);
        }

        JAObjectNameIndexer<T>* findSubIndexer(const std::string& key) {
            if(key.empty()) return this;
            if(mSubIndex_==nullptr) return nullptr;
            std::string mainKey = StringUtils::getSubStrBefore(key, '.', 1);
            std::string subKey = StringUtils::getSubStrAfter(key, '.', 1);
            if(subKey.empty()) {
                return mSubIndex_->findObject(mainKey);
            }
            auto sub = mSubIndex_->findObject(mainKey);
            return sub== nullptr?nullptr:sub->findSubIndexer(subKey);
        }

        //递归处理，TO DO::设置层数限制
        void addSubIndexer(const std::string& key, T* dev) {
            std::string mainKey = StringUtils::getSubStrBefore(key, '.', 1);
            std::string subKey = StringUtils::getSubStrAfter(key, '.', 1);
            if(subKey.empty()){
                mIndexMap_.appendNew(mainKey, dev);
                return;
            }
            if(mSubIndex_==nullptr) mSubIndex_ = new qlibc::QPObjectHolder<JAObjectNameIndexer<T>>;
            auto subIndex = mSubIndex_->findObject(mainKey);
            if( subIndex == nullptr ) {
                subIndex = new JAObjectNameIndexer<T>();
                mSubIndex_->appendNew(mainKey, subIndex);
            }
            subIndex->addSubIndexer(subKey, dev);
        }

        void removeSubIndexer(const std::string& key) {
            std::string mainKey = StringUtils::getSubStrBefore(key, '.', 1);
            std::string subKey = StringUtils::getSubStrAfter(key, '.', 1);
            if(subKey.empty()){
                mIndexMap_.removeObject(mainKey);
                return;
            }
            if(mSubIndex_==nullptr) return;
            auto subIndex = mSubIndex_->findObject(mainKey);
            if(subIndex!=nullptr)
                subIndex->removeSubIndexer(subKey);
        }

        void printAllIndexes(QStringList& keyList, const std::string& prefix = "") {
            //打印本级的所有对象及索引分支
            for(auto& k  : mIndexMap_.getObjectMap()){
                keyList.emplace_back((prefix.empty()?prefix:prefix+".") + k.first);
            }
            if(mSubIndex_!=nullptr) {
                for(auto &s : mSubIndex_->getObjectMap()) {
                    s.second->printAllIndexes(keyList, (prefix.empty()?prefix:prefix+".") + s.first);
                }
            }
        }
    };

    /**
     * 设备分组等，主要用于多路径索引
     */
    class JE_API JAObjectGroup : public JAObjectNameIndexer<JAStoredObject> {
    public:
        virtual JAStoredObject* findObjectInGroup(const std::string& fmtKey) {
            return findTarget(fmtKey);
        }
    };

    class JE_API JAIndexHolder {
    protected:
        JAObjectGroup           mIndexes_;          //设备复杂索引，如同一种设备大类、子类、型号、功能组等进行分组，统一控制

    public:
        /**
         * 将对象加入到一个分组
         * @param obj
         * @param groupKey
         * @return
         */
        bool addJAObjectToGroup(JAStoredObject &obj , const std::string& groupKey);
        void removeJAObjectFromGroup(JAStoredObject &obj , const std::string& groupKey);
        void clearObjectIndexes(JAStoredObject *obj);

        void addJAObjectIndexes(JAStoredObject *obj);

        void beforeObjectErase(JAObject* obj);

        /**
         *  显示索引分组树
         */
        void showIndexerTree(JAObject& obj, QStringList &keyList, const string &groupKey = "");

        static JAObject *runObjectFromDataCreator(JAIndexedInstance &instance, JAObjectOwnerBase &owner, const std::string &name, qlibc::QData &data);
    };

    class JE_API JAStoredOwner :
            public ja::JAStoredObject,
            public ja::JAObjectOwnerBase,
            public ja::JAIndexHolder {
    public:
        explicit JAStoredOwner(JAContext &ctx, JAIndexedInstance &inst, const qlibc::QData &data,
                               const std::string &path);

        JAObject *createRunObjectFromData(JAInstance &inst, const std::string &key, qlibc::QData &data) override;

        static ja::JAObject* creator(ja::JAContext &ctx, JAInstance &instance, const std::string &key, const qlibc::QData &data);

    protected:
        void beforeObjectErase(JAObject *object) override;
    };

    class JE_API JATaskOwner : public JAStoredOwner {
    public:
        explicit JATaskOwner(JAContext &ctx, JAIndexedInstance &inst, const qlibc::QData &data,
                             const std::string &path);

        static ja::JAObject* creator(ja::JAContext &ctx, JAInstance &instance, const std::string &key, const qlibc::QData &data);
    };

    class JE_API JAIndexNodeStack : public JAStack, public JAStoredObjectBase {
    public:
        explicit JAIndexNodeStack(JAContext& ctx, JAIndexedInstance& inst, const std::string& path, const std::string& cls="istack", bool pack_ctx = true);

        JAStoredObject* dynamicStoredObject(const std::string &key, const qlibc::QData &obj);

        inline JAIndexedInstance& getIndexedInstance();

        void copyStackResult(QData *resp) override;
    };

    using PJAStoredObjectList = std::vector<JAStoredObject*>;

    using SystemDefaultObjectsInitor = std::function<void(JAContext& ctx, JAIndexedInstance& inst, const std::string& path)>;

    const static char* vkey_prop_name_ = "name";
    const static char* vkey_prop_obj_id_ = "id";
    const static char* vkey_prop_class_ = "class";
    const static char* vkey_prop_type_ = "type";

    //具有树形对象结构的Instance，能从本地文件夹或JA代码中创建树形对象结构
    class JE_API JAIndexedInstance :
            public JAInstance,
            public JAIndexHolder {
    protected:
        std::string             objs_path_;         //对象树路径
        std::string             cls_path_;          //类路径
        std::string             file_;              //文件路径
        qlibc::QData            jaconfig_;
        const std::string       mEmptyKey;          //空Key
        QStringList             mDefaultIndexKeyList_, mSaveObjectKeysOrderList_;
        QStringPMap             mObjectsKeysSet_;

    public:
        explicit JAIndexedInstance(JAContext& ctx, JAObject& baseObj, const std::string& name, std::string initPath =DEFAULT_CURRENT_PATH,
                 std::string  file = "instance.json", const std::string& cls = "sinstance", bool pack_ctx = false);

        ~JAIndexedInstance() override;

    protected:
        void buildInstanceConsts(const std::string& filename);

        void firstRunPrepare(const SystemDefaultObjectsInitor& initor);

        virtual void loadInstanceClasses();
        void buildKeysSet();

        void getJAObjectList(PJAStoredObjectList &dls);

        //扫描目录读取文件，重建设备树
        int loadObjectsFromFile();

        JAStack *createJAServiceStack() override;

    public:
        bool containsHandler(const std::string &channel, const std::string &handlerKey);

        virtual void reloadServiceJsonAction(const std::string& keylist = vkey_default_ja_script_channel_keys);

        virtual JAStoredObject* createStoredObjectFromData(const std::string& key, qlibc::QData& deviceInfo);

        JAObject *createRunObjectFromData(JAInstance &inst, const string &key, qlibc::QData &data) override;

        void prepareInstanceWithInitFunction(const SystemDefaultObjectsInitor& initor = [](JAContext& ctx, JAIndexedInstance&, const std::string&){});

        bool prepareInstance() override;

        bool queryServiceHandlers(qlibc::JCArgNode &node, qlibc::QData *rsp);
        const std::string &getObjectBasePath();
        virtual void createDefaultObjects();

        inline QStringList& getDefaultIndexKeyList(){ return mDefaultIndexKeyList_; }
        inline QStringList& getSaveObjectKeysOrderList(){ return mSaveObjectKeysOrderList_; }

        void saveObjectsToFile();
        void listObjects(PJAStoredObjectList& dls);

        /**
         *  从消息到象的操作
         */
        JAStoredObject* createStoredJAObject(qlibc::QData& deviceInfo);
        void removeStoredJAObject(qlibc::QData& deviceInfo);
        JARStoredObject findStoredObject(const std::string &id);

        void StoredObjectRef(const std::string &key, JAStoredObject &obj);
        void StoredObject(const std::string &key, JAStoredObject *obj);

        const std::string &emptyKey();
        const std::string &PropertyKey(const char *keyType);

        std::string getObjectProperty(const qlibc::QData &data, const char *valType, const std::string& defVal = "");
        void setObjectProperty(qlibc::QData &data, const char *valType, const std::string& value);

        static bool createStored(ja::JAStack& s, ja::JAObject& o, const qlibc::QData &qm, const std::string& sp);

        virtual void createDefaultJAFile(const std::string &scriptFn);

        JAStoredObject* loadObjectFromFile(const std::string &fn, qlibc::QData &d);

        bool addObjectToOwnerTree(JAStoredObject *sto);

    protected:
        bool addObjectToIndexedOwner(JAObjectOwnerBase *owner, JAStoredObject *obj);

        void beforeObjectErase(JAObject *object) override;
    };

    class JE_API JAIndexNodeContext : public JAContext, public JAStoredObjectBase  {
    protected:
        JARIndexedInstance   defaultInst;

        void loadInnerClasses() override;

    public:
        explicit JAIndexNodeContext(const std::string& name,
                const std::string& initPath,
                const std::string& file="instance.json",
                const std::string& cls="icontext");

        ~JAIndexNodeContext() override;

        bool prepareContext(const qlibc::QData& data) override ;

    protected:
        void buildClassFromDefData(JAClassObject *cls, const qlibc::QData &data) override;

        JARHandler parseClassJAMethod(JAClassObject* cls, Json::Value &vl, std::string &key,
                                      const char *channel, const std::string& fn, const std::string& chn);

    public:
        void prepareContextWithInitFunction(const qlibc::QData& config, const SystemDefaultObjectsInitor& initor = [](JAContext& ctx, JAIndexedInstance&, const std::string&){});;

        bool loadJAScript(const std::string &jafile, const std::string& keylist = "msg",const std::string& inst="");

        bool runJAScript(const std::string &chn, qlibc::QData *rsp, qlibc::QData &msg,const std::string& inst="");

        bool evalJAScript(const std::string &script, qlibc::QData *rsp, const qlibc::QData &msg, const std::string &src = vkey_filename_inline_,const std::string& inst="");

        JARIndexedInstance getDefaulInstance();

    };
}

#endif //INC_0_LIBDEVCPP_JAINDEXEDDOM_H
