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

#pragma once

#include "StepData.h"

#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"

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

#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;
		int 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;
	};


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;

	void SetFromOlder(CaplQLQuery *older);
	bool SaveToDataBuf(CaplDataBuf &dbuf, bool load_extent = true);
	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;

	bool 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);
};
