#ifndef __HCM4A_CalcFS4A_dbMask_h_
#define __HCM4A_CalcFS4A_dbMask_h_

#ifdef C_UMK
	#define MODEL <sqMask.sch>
#else
	#define MODEL <APP_PATH\sqMask.sch>
#endif
#include "sqMask.h"


enum class RowType {NONE, MG, BND, CSP, MSK, MP};
String RowTypeToStr(RowType type){
	switch(type){
		case RowType::NONE:
			return "NONE";
		case RowType::MG:
			return "MG";
		case RowType::BND:
			return "BND";
		case RowType::CSP:
			return "CSP";
		case RowType::MSK:
			return "MSK";
		case RowType::MP:
			return "MP";
	}
}

struct NodeMask :Moveable<NodeMask>{
	int db_id;					// DB_ID
	RowType type;
	void Clear();
	String ToString() const;
	NodeMask(){Clear();};
	NodeMask(const int db_id, const RowType type):db_id(db_id), type(type){};
	NodeMask(const NodeMask& node):db_id(node.db_id), type(node.type){};
};
void NodeMask::Clear(){
	db_id = 0;
	type = RowType::NONE;
}
String NodeMask::ToString() const{
	return Format("node: [%d, %s]", db_id, RowTypeToStr(type));
}
//!---------------------------------------------------------------------------------------------

struct DbMask{
	static const String GetFullPath(){return GetExeFolder() + "/mask.db";}
	VectorMap<int, NodeMask> rows; // key tree npp
	void LoadFromCSV(const String& dir);
	void FillTree(TreeCtrl& tree);
	void FillMpList(int msk_id, Vector<MpointRec>& list);
	DbMask();
protected:
	int btrMaxLen;
	int claMaxLen;
	Sqlite3Session sqMask;
	MaskGroupData mgData;
	BandData bndData;
	ChSpacingData cspData;
	BitRateData btrData;
	ClassData claData;
	Mg_BndData mgbData;
	Mg_Bnd_CspData mbcData;
	MaskData mskData;
	MpointData mpData;
	void FillTree(int p_id, int db_id, RowType type, TreeCtrl& tree);
};
DbMask::DbMask(){
	btrMaxLen = 0;
	claMaxLen = 0;
	if(sqMask.IsOpen()) sqMask.Close();
    if(!FileExists(GetFullPath())) {
		CheckSqLiteDB(sqMask, GetFullPath());
		LoadFromCSV(GetExeFolder() << "/CSV" );
	}
	else {
		// read database
		if(!sqMask.Open(GetFullPath())){
			Exclamation("Can't open database file: " << GetFullPath());
			return;
		}
	}
	sqMask.ClearError();
}
void DbMask::LoadFromCSV(const String& dir){
	if(!DirectoryExists(dir)) return;
	if(mgData.Get(sqMask).GetCount() == 0) mgData.FromCSV(sqMask, String()   << dir << "/" << "maskgroup.txt", true);
	if(bndData.Get(sqMask).GetCount() == 0) bndData.FromCSV(sqMask, String() << dir << "/" << "bands.txt", true);
	if(cspData.Get(sqMask).GetCount() == 0) cspData.FromCSV(sqMask, String() << dir << "/" << "channel_spacing.txt", true);
	if(btrData.Get(sqMask).GetCount() == 0) btrData.FromCSV(sqMask, String() << dir << "/" << "bitrate.txt", true);
	if(claData.Get(sqMask).GetCount() == 0) claData.FromCSV(sqMask, String() << dir << "/" << "class.txt", true);

	if(mgbData.Get(sqMask).GetCount() == 0) mgbData.FromCSV(sqMask, String() << dir << "/" << "cband.txt", true);
	if(mbcData.Get(sqMask).GetCount() == 0) mbcData.FromCSV(sqMask, String() << dir << "/" << "cchannel.txt", true);
	if(mskData.Get(sqMask).GetCount() == 0) mskData.FromCSV(sqMask, String() << dir << "/" << "cmask.txt", true);
	if(mpData.Get(sqMask).GetCount() == 0) mpData.FromCSV(sqMask, String()   << dir << "/" << "mpoints.txt", true);
}
void DbMask::FillTree(int p_nid, int db_id, RowType type, TreeCtrl& tree){
	switch(type){
		case RowType::NONE:
			return;
		case RowType::MG:
			{
				Vector<MaskGroupRec> vec; mgData.Fill(sqMask, vec);
				for(int i=0; i<vec.GetCount(); i++){
					int nid = tree.Add(p_nid, Image(), vec[i].mg_id, vec[i].mg_name, true);
					rows.Add(nid, NodeMask(vec[i].mg_id, RowType::MG));
					FillTree(nid, vec[i].mg_id, RowType::BND, tree);
				}
			}
			break;
		case RowType::BND:
			{
				Vector<Mg_BndRec> vec; mgbData.Fill(sqMask, db_id, vec);
				for(int i=0; i<vec.GetCount(); i++){
					int nid = tree.Add(p_nid, Image(), vec[i].mgb_id, vec[i].bnd_name, true);
					rows.Add(nid, NodeMask(vec[i].mgb_id, RowType::BND));
					FillTree(nid, vec[i].mgb_id, RowType::CSP, tree);
				}
			}
			break;
		case RowType::CSP:
			{
				Vector<Mg_Bnd_CspRec> vec; mbcData.Fill(sqMask, db_id, vec);
				for(int i=0; i<vec.GetCount(); i++){
					int nid = tree.Add(p_nid, Image(), vec[i].csp_id, String() << vec[i].csp_mhz << " MHz", true);
					rows.Add(nid, NodeMask(vec[i].csp_id, RowType::CSP));
					FillTree(nid, vec[i].mbc_id, RowType::MSK, tree);
				}
			}
			break;
		case RowType::MSK:
			{
				Vector<MaskRec> vec; mskData.Fill(sqMask, db_id, vec);
				for(int i=0; i<vec.GetCount(); i++){
					String btrStr = vec[i].btr_bitrare + SetW(btrMaxLen - vec[i].btr_bitrare.GetLength()+4);
					String claStr = vec[i].cla_name + SetW(claMaxLen - vec[i].cla_name.GetLength());
					int nid = tree.Add(p_nid, Image(), vec[i].msk_id, btrStr + "\t|\t" + claStr + "\t|\t" + vec[i].msk_remark, true);
					rows.Add(nid, NodeMask(vec[i].msk_id, RowType::MSK));
					FillTree(nid, vec[i].msk_id, RowType::MP, tree);
				}
			}
			break;
		case RowType::MP:
			{
				Vector<MpointRec> vec; mpData.Fill(sqMask, db_id, vec);
				for(int i=0; i<vec.GetCount(); i++){
					String str = Format("%s\t%s%s", DblStr(vec[i].mp_freq), SetW(3), DblStr(vec[i].mp_atten));
					int nid = tree.Add(p_nid, Image(), vec[i].mp_id, str, false);
					rows.Add(nid, NodeMask(vec[i].mp_id, RowType::MP));
				}
			}
			break;
	}
}

void DbMask::FillTree(TreeCtrl& tree){
	rows.Clear();
	{
		// calc btrMaxLen, claMaxLen
		btrMaxLen = 0;
		Vector<BitRateRec> btr_list; btrData.Fill(sqMask, btr_list);
		for(auto v: btr_list)
			if(TrimBoth(v.btr_bitrare).GetLength() > btrMaxLen) btrMaxLen = v.btr_bitrare.GetLength();
		btrMaxLen++;
		claMaxLen = 0;
		Vector<ClassRec> cla_list; claData.Fill(sqMask, cla_list);
		for(auto v: cla_list)
			if(TrimBoth(v.cla_name).GetLength() > claMaxLen) claMaxLen = v.cla_name.GetLength();
		claMaxLen++;
		
	}
	FillTree(0, 0, RowType::MG, tree);
}
void DbMask::FillMpList(int msk_id, Vector<MpointRec>& list){
	mpData.Fill(sqMask, msk_id, list);
}
//!---------------------------------------------------------------------------------------------
//!---------------------------------------------------------------------------------------------

/// Dlg Show(Select) Mask from Tree
struct MaskDlg: public WithMaskDlg<TopWindow>{
public:
	typedef MaskDlg CLASSNAME;
	const TwoCveList& Get()const{return tcveList;}
	int GetSaveNodeNum()const{return save_node_num;}
	void ToCtrl();
	void FromCtrl();
	void Clear();
	void Serialize(Stream& s);
	void BeforeFrmShow(const String& nameCtrl, VectorMap<String, String>& sMap);
	void BeforeFrmClose(const String& nameCtrl, VectorMap<String, String>& sMap);
	void SelMaskFromTree();
	void FillTwoCveList();
	void PrintMaskFromTree();
	MaskDlg(const TwoCveList& tcveList);
	MaskDlg(const TwoCveList& tcveList, const int mask_id);
protected:
	TwoCveList tcveList;
	DbMask dbMask;
	int sel_mask_id;
	int save_node_num;
	void Setup();
};
MaskDlg::MaskDlg(const TwoCveList& tcveList):tcveList(clone(tcveList)){
	CtrlLayoutOKCancel(*this, "...");
	sel_mask_id = 0;
	save_node_num = 0;
	Sizeable();
	Setup();
}
MaskDlg::MaskDlg(const TwoCveList& tcveList, const int mask_id):tcveList(clone(tcveList)), save_node_num(mask_id){
	CtrlLayoutOKCancel(*this, "...");
	Sizeable();
	Setup();
}
void MaskDlg::Serialize(Stream& s){
    int version = 1;
    s / version;
	SerializePlacement(s);
	tree.Serialize(s);
}
void MaskDlg::BeforeFrmShow(const String& nameCtrl, VectorMap<String, String>& sMap){
	int idx = sMap.Find(nameCtrl);
	if(idx != -1) LoadFromString(*this, sMap[idx]);
}
void MaskDlg::BeforeFrmClose(const String& nameCtrl, VectorMap<String, String>& sMap){
	int idx = sMap.Find(nameCtrl);
	if(idx == -1)
		sMap.Add(nameCtrl, StoreAsString(*this));
	else
		sMap[idx] = StoreAsString(*this);
}
void MaskDlg::Setup(){
	tree.NoRoot();
	dbMask.FillTree(tree);
	tree.WhenLeftClick = THISBACK(SelMaskFromTree);
	ok << [=]{ FillTwoCveList(); Close();};
	ok.Enable(false);
	if(tree.IsValid(save_node_num) && save_node_num > 0){
		tree.MakeVisible(save_node_num);
		tree.OpenDeep(save_node_num, true);
	}
}
void MaskDlg::SelMaskFromTree(){
	if(!dbMask.rows.GetCount() || tree.GetCursor() == -1) return;
	int node_num = tree.GetCursor();
	int node_pnum = tree.GetParent(node_num);
	int idx = dbMask.rows.Find(node_num);
	if(idx > -1){
		NodeMask& node = dbMask.rows[idx];
		switch(node.type){
			case RowType::NONE:
				ok.Enable(false);
				break;
			case RowType::MG:
				ok.Enable(false);
				break;
			case RowType::BND:
				ok.Enable(false);
				break;
			case RowType::CSP:
				ok.Enable(false);
				break;
			case RowType::MSK:
				ok.Enable(true);
				sel_mask_id = node.db_id;
				save_node_num = node_num;
				break;
			case RowType::MP:
				ok.Enable(true);
				int pidx = dbMask.rows.Find(node_pnum);
				if(pidx > -1){
					NodeMask& pnode = dbMask.rows[pidx];
					sel_mask_id = pnode.db_id;
					save_node_num = node_pnum;
				}
				break;
		}
	}
}
void MaskDlg::ToCtrl(){
}
void MaskDlg::FromCtrl(){
}
void MaskDlg::Clear(){
	tree.Clear();
}
void MaskDlg::PrintMaskFromTree(){
	Vector<MpointRec> list;
	dbMask.FillMpList(sel_mask_id, list);
	String str = "\n";
	for(auto v: list){
		str << Format("f,a: [%s, %s]\n", DblStr(v.mp_freq), DblStr(v.mp_atten));
	}
	Close();
}
void MaskDlg::FillTwoCveList(){
	Vector<MpointRec> list;
	dbMask.FillMpList(sel_mask_id, list);
	tcveList.ClearValue();
	for(int i=0; i<list.GetCount(); i++){
		String sGf = Format("7Gf%d", i+1);
		String sGa = Format("7Ga%d", i+1);
		tcveList.GetFld(sGf).SetValue(list[i].mp_freq);
		tcveList.GetFld(sGa).SetValue(list[i].mp_atten);
	}
}

//!---------------------------------------------------------------------------------------





#endif
