// aplQLQuery.h: interface for the CaplQLQuery class.
//
//////////////////////////////////////////////////////////////////////

#pragma once

#include "StepData.h"

// Класс CaplPagin описывает чтение порциями результатов запросов StepQl и LoadEx
// Переменные start и size_p задают начало и размер порции. Нумерация элементов начинается с нуля.
// Переменные sort_attrs и asc задают атрибуты, по которым будет выполнена сортировка перед выделением
//    порции и направление (true возрастание false убывание)
// Если start равно нулю и size_p равно -1, то разбиение на порции не производится, возвращается полный список инстансов.
// Если start не больше нуля нулю и size_p равно -1, то возвращается одна порция инстансов начиная с позиции start до конца массива инстансов.
// В переменной size_all возвращается полноре количество инстансов в результатах запроса
class AFX_EXT_CLASS CaplQLQuery;
class AFX_EXT_CLASS CaplPagin
{
public:
    CaplPagin(){setEmpty();}
    CaplPagin(int _start, int _size);
#if __cplusplus >= 201103L
    CaplPagin(int _start, int _size, std::list<CaplAttr*> _sort_attrs, bool _asc = true);
#else
    CaplPagin(int _start, int _size, const CaplTAggr<CaplAttr*, CaplAttr*, APLAGGR_LIST> &_sort_attrs, bool _asc = true);
#endif
    CaplPagin(const CaplPagin *prev);

    void Set(const CaplPagin *prev);
#if __cplusplus >= 201103L
	void Set(int _start, int _size, std::list<CaplAttr*> _sort_attrs, bool _asc = true);
#endif
	void Set(int _start, int _size, const CaplTAggr<CaplAttr*, CaplAttr*, APLAGGR_LIST> &_sort_attrs, bool _asc = true);

    bool isEqual(const CaplPagin *prev, bool withPortion) const;

    void setEmpty(){m_start=0; m_size_p=-1; m_size_all=-1; m_sort_attrs.Clear();}
    bool isEmpty() const {return (m_start==0 && (m_size_p==-1 || m_size_p==0x7fffffff));}

    void Add2Buf(CaplDataBuf &dbuf);
    bool ReadFromBuf(CaplDataBuf &dbuf, CaplStepData* data);
    CString CheckQuery(CaplQLQuery* m_query);

    void Print(CString &info);
    void GetPortion(aplExtent &ext_in, aplExtent &ext_out, CaplStepData *data);

    int m_start;	// in: индекс первого возвращаемого элемиента
    int m_size_p;	// in: размер возвращаемой порции
    __int64 m_size_all;	// out: возвращается общее количество найденных инстансов
    CaplTAggr<CaplAttr*, CaplAttr*, APLAGGR_LIST> m_sort_attrs; // список атрибутов для сортировки
    bool m_asc; // сортировка: true возрастание false убывание

};




#define APL_STEP_QL_FORMAT_ID_BASE	"apl STEP QL v "
#define APL_STEP_QL_FORMAT_ID_1_3	"apl STEP QL v 1.3"
#define APL_STEP_QL_FORMAT_ID_1_4	"apl STEP QL v 1.4"
#define APL_STEP_QL_FORMAT_ID_1_5	"apl STEP QL v 1.5"
#define APL_STEP_QL_FORMAT_ID_1_6	"apl STEP QL v 1.6"

// изменения: в CaplQLLogOp добавлена возможность ссылаться атрибутом value_attr на объект CaplQLPathElement
#define APL_STEP_QL_FORMAT_ID_1_7	"apl STEP QL v 1.7"

// изменения: в CaplQLExtent добавлены атрибуты "загрузить в кеш (in_result) и "загрузить все атрибюты" (with_attr)
#define APL_STEP_QL_FORMAT_ID_1_8	"apl STEP QL v 1.8"

// изменения: в CaplQLPathElement добавлен атрибут attr_convert (надо ли и если надо - как преобразовывать значение перед сравнением)
#define APL_STEP_QL_FORMAT_ID_1_9		"apl STEP QL v 1.9"

// изменения: в CaplQLPathElement добавлены атрибуты для арифметических операций между двумя атрибутами или доп числом
#define APL_STEP_QL_FORMAT_ID_2_0		"apl STEP QL v 2.0"

// изменения: в CaplQLPathElement добавлены псевдоатрибуты для запроса даты/юзера создания/изменения
#define APL_STEP_QL_FORMAT_ID_2_1		"apl STEP QL v 2.1"

// изменения: серверу передается флаг "возвращать только количество найденых инcтансов"
#define APL_STEP_QL_FORMAT_ID_2_2		"apl STEP QL v 2.2"

// изменения: серверу структура с указанием начала и размера возвращаемой порции
#define APL_STEP_QL_FORMAT_ID		"apl STEP QL v 2.3"

#define InserString2Control(control,value) 	pos_added=control.AddString(CaplQLQuery::LogOp2String(value));control.SetItemData(pos_added,value);
#define InserString2ControlDef(control,value,value_def) pos_added=control.AddString(CaplQLQuery::LogOp2String(value));control.SetItemData(pos_added,value);if(value==value_def){m_Combo.SetCurSel(pos_added);};

#define MAX_OBJECTS_IN_QUERY 1000
#define MAX_REQURSIVE_IN_QUERY 600


class AFX_EXT_CLASS CaplQLQuery
{
public:
    // тип объекта
    typedef enum{
            aplTOObject=0,
            aplTOBranchSelect=1,
            aplTOQuery=2,
            aplTOExtent=3,
            aplTOGroupOp=4,
            aplTOLogOp=5,
            aplTOPathElement=6,
            aplTOField=7,
            aplTOArithmeticOpPath=8
    }aplQLTypeObject;

    // тип псевдоатрибута
    typedef enum{
            aplPAInstId=0,
            aplPAUserCreate=1,
            aplPADateCreate=2,
            aplPAUserUpdate=3,
            aplPADateUpdate=4
    }aplQLTypePseudoAttr;

    class AFX_EXT_CLASS CaplQLObject{
    public:
        int m_def_pos;int m_def_str;
        CaplQLQuery *m_parent;
        aplQLTypeObject type;
        CaplQLObject(CaplQLQuery* parent);
        void SetParentErrPosition();
        virtual ~CaplQLObject();
    };


    class AFX_EXT_CLASS CaplQLBranchSelect:public CaplQLObject{
    public:
        CaplQLBranchSelect(CaplQLQuery* parent);
        virtual ~CaplQLBranchSelect();
    };


    class AFX_EXT_CLASS CaplQLExtent:public CaplQLObject{
    public:
        CaplQLExtent(CaplQLQuery* parent);
        virtual ~CaplQLExtent();
        CaplEntity* base_entity;
        CaplEntity* res_entity;
        CaplQLBranchSelect* conditions;
        CString name;
        aplExtent res_ext;
        CaplQLBranchSelect* QLExtents;
        CString SQL;
        CString typeSQL;

        CaplAttr *attr_sharp;
        bool is_res_field;
        bool is_defined;
        bool in_result;
        bool with_attr;
    };

    // тип группового отношения
    typedef enum{
            aplNOGRPREL=0,
            aplAND=1,
            aplOR=2,
            aplGNOTIN=3
    }aplQLGroupRelation;

    class AFX_EXT_CLASS CaplQLGroupOp:public CaplQLBranchSelect{
    public:
        CaplQLGroupOp(CaplQLQuery* parent);
        virtual ~CaplQLGroupOp();
        aplQLGroupRelation operation_type;
        CaplQLBranchSelect* first_op;
        CaplQLBranchSelect* second_op;
    };

    // тип логического отношения
    typedef enum{
            aplNOLOGREL=0,
            aplSMALL=1, // "<"
            aplBIG=2,//">"
            aplSMALLEQUAL=3,// "<="
            aplBIGEQUAL=4, //">="
            aplEQUAL=5,//"равно"
            aplNOTEQUAL=6,//"не равно"
            aplIN=7,// "входит"
            aplNOTIN=8,// "не входит"
            aplLIKE=9,// "содержит"
            aplNOTLIKE=10,// "не содержит"
            aplEXIST=11,// характеристика задана
            aplNOTEXIST=12,//характеристика не задана
            aplLIKELEFT=13,// "содержит с левой стороны"
            aplLIKERIGHT=14,// "содержит с правой стороны"
            aplNOTLIKELEFT=15,// "не содержит с левой стороны"
            aplNOTLIKERIGHT=16,// "не содержит с правой стороны"
            aplINDIAPAZON=17,//  "в диапазоне"
            aplEMPTY=18, // "массив пустой"
            aplNOTEMPTY=19, // "массив не пустой"
            aplLOGICALRELATION=20, // "логическое выражение"
            aplISNULL=21, // ""
            aplISNOTNULL=22, // ""
    }aplQLLogRelation;

    // Функция выводит или описание команды по русски (если is_rus=true), или
    // оператор SQL, используемый при поиске в ORACLE. Во втором случае выходные коды
    // могут совпадать (например для aplLIKE и aplLIKELEFT)
    static CString LogOp2String(CaplQLQuery::aplQLLogRelation log_relation,bool is_rus=true);

    typedef enum{
            aplValNoValue=0,
            aplValValue=1,
            aplValExtent=2,
            aplValInBlobString=3,
            aplValAttr=4,
    }aplQLLogOpSelector;


    class AFX_EXT_CLASS CaplQLLogOp:public CaplQLObject{
    public:
        CaplQLLogOp(CaplQLQuery* parent);
        virtual ~CaplQLLogOp();
        aplQLLogRelation operation_type;
        aplQLLogOpSelector selector;
        union
        {
            CaplValue* value_val;
            CaplQLExtent* value_ext;
            CaplAttr* value_attr;
        };
    };

    typedef enum{
        aplValNoConvert=1,
            aplValToInt=2,
            aplValToReal=3,
            aplValToString=4,
    }aplQLLogAttrConvert;

    typedef enum{
        aplAONoOp=0,
        aplAOAdd=1,
        aplAOSub=2,
        aplAOMul=3,
        aplAODiv=4,
    }aplQLArithmeticOp;

    class AFX_EXT_CLASS CaplQLArithmeticOpPath:public CaplQLObject{
    public:
        CaplQLArithmeticOpPath(CaplQLQuery* parent);
        int GetAOOrder();

        CaplQLArithmeticOpPath* next_element;
        aplQLArithmeticOp arithm_op;
        CaplAttr* attr2;
        aplQLLogAttrConvert attr_convert;
        double num2;
    };

    class AFX_EXT_CLASS CaplQLPathElement:public CaplQLBranchSelect{
    public:
        CaplQLPathElement(CaplQLQuery* parent);
        virtual ~CaplQLPathElement();
        CaplQLBranchSelect* next_element;
        CaplAttr* attr;
        aplQLTypePseudoAttr pseudo_attr;
        CaplEntity* res_entity;
        CaplQLLogOp* operation;
        CString attr_name;
        aplQLLogAttrConvert attr_convert;
        // 2_0
        CaplQLArithmeticOpPath* arithmetic;
    };

    class AFX_EXT_CLASS CaplQLField:public CaplQLBranchSelect{
    public:
        CaplQLField(CaplQLQuery* parent);
        virtual ~CaplQLField();
        CaplQLExtent* base_extent;
        CString extent_name;
        CaplQLPathElement* path;
        bool in_result;
    };


public:
    friend class CaplQLObject;
    CaplQLQuery(CaplStepData *hStepData);
    virtual ~CaplQLQuery();

    CaplTAggr<CaplQLExtent*,CaplQLExtent*,APLAGGR_LIST> m_extents;
    CaplQLBranchSelect* m_res_bush;

    // Не учитывать регистр символов при сравнениях '=' и '!='
    // если false (по умолчанию) то при сравнениях нужно строгое равенство
    // если true (запрос SELECT NO_CASE или SELECT_LOAD_ALL NO_CASE) то регистр символов не важен, строка TeSt равна tesT
    bool m_no_case;
    bool m_load_attr;
    CaplPagin m_pagin;
	// если установить в true, то при несоответствии запроса критериям порционирования запрос будет 
	//	выполнен без учета сортировки и порционирования. Режим нужен для вызова из PSS REST API
	bool m_ignore_pagin_error; 

    void SetFromOlder(CaplQLQuery *older);
    bool SaveToDataBuf(CaplDataBuf &dbuf, bool load_extent = true, CaplPagin *pagin = 0);
    bool LoadFromDataBuf(CaplDataBuf &dbuf, UINT32 *p_load_extent = NULL);
    virtual void ProcessHeaderError(const char* version);
    virtual CaplInstance* GetInst4Query(apl_id inst_id, apl_id ent_id);
    virtual void ProcessCatchError(SaplErrorDescription &error);

    bool Compare(CaplValue &val, CaplInstance* base_inst, CaplQLLogOp* log_op=0, CaplValue *val_new=0, aplQLLogRelation def_op_type=aplLIKE);

    CaplStepData *m_data;
    // числа для индикации выполнения - количество процентов на элемент и текущий выполненный процент
    double m_percent_on_el,m_counter_percent;

    CString m_LastErrDescr;
    CString m_class_info;

    void PrintQuery(CString &query);
    CString PrintMakeStep(CaplQLBranchSelect *bsel);
    CString PrintMakeLogOp(CaplQLBranchSelect *bsel);
    CString PrintGroupQLExt(CaplQLBranchSelect* bsel);

    bool m_ignore_nonextsts_inst;

protected:
    int m_error_string_num;// текущий номер строки
    int m_error_pos;// текущая позиция в строке

    int m_stack_level; // печать запроса сделана рекурсией - чтобы не превышать стек

    bool m_need_lastest_buffer; // это костыль на время, пока все внутренние сервера не сумеют читать свежий буфер

    CaplTAggr<CaplQLObject*,CaplQLObject*,APLAGGR_LIST_OR_AUTOKILLREF> m_all_objects;
};

class CaplStepData;

class AFX_EXT_CLASS CaplQLQueryParseBase : public CaplQLQuery
{
public:
    CaplQLQueryParseBase(CaplStepData *hStepData);
    virtual ~CaplQLQueryParseBase();
    CString GetLastError(int &string_num, int &pos_in_string, int *global_pos = 0, int *length = 0);
    bool parse(CString &query);
    bool m_is_parse;

    inline virtual void LoadCurrUser(){};
    inline virtual void LoadTypeSrv(){};
    inline virtual CaplInstance* GetExistedInstById(apl_id id);
    virtual bool LogMessage(LPCTSTR /*ErrDescr*/, bool /*is_warning=true*/, bool /*is_time=true*/, bool /*is_write=true*/, bool /*is_line=false*/,
        LPCTSTR /*funct=0*/, LPCTSTR /*class_info=0*/, LPCTSTR /*ExtDescr=0*/,
        LPCTSTR /*srs_file=0*/, LPCTSTR /*srs_date=0*/, UINT /*srs_line=0*/){return false;};

protected:
    CString m_TypeSrv;
    CString m_error_definition;
    int m_pos_read;// глобальная позиция чтения в разбираемом выражении
    CString m_in;
    int m_brackets;// количество открытых скобок
    CString m_cur_word;
    bool m_is_header;// при разборе результирующего условия = true; при разборе тел подзапросов = false
    int m_recurse_level;
    aplQLGroupRelation m_last_operation_type;

    CaplInstance* m_CurrUser;
    CaplInstance* m_CurrUserGroupe;
    CaplInstance* m_CurrUserPerson;

    CaplQLExtent* FindQLExtent(CString &name, bool auto_create);
    void SetErrDef(int err_def);
    bool IsRezSym(TCHAR c);
    bool IsServSym(CString &str);
    bool GetWord(CString &word);

    CaplQLBranchSelect* GetFields(CString &query, bool is_need_read_first = true);
    CaplQLPathElement* GetAllPath(CString& query);
    CaplQLBranchSelect *GetConditions(CaplEntity *ent_in);
    CaplQLArithmeticOpPath* GetArithmEl(CaplEntity *ent_in, CaplAttr* prev_attr, aplQLLogAttrConvert attr_convert);

    bool ReadTypeConvert(aplQLLogAttrConvert &attr_convert);
    bool IsBoolOp(CString &str);
    bool IsLogOp(CString &str);
    bool IsServiceSymbol(CString &str);
    bool TestEndRecursiv(bool is_ext);
    bool CheckFields(CaplQLBranchSelect *bsel, CaplEntity* upper_ent = 0);
};

