bool GetRamReadAdr(shil_opcode* op,u32* addr)
{
	if ((op->flags & (FLAG_R0|FLAG_GBR|FLAG_REG2))==0)
	{//[imm] form
		verify((op->flags & (~(3|FLAG_SX))) == (FLAG_IMM1|FLAG_REG1));
		if (IsOnRam(op->imm1))
		{
			*addr= op->imm1;
			return true;
		}
	}
	return false;
}

#include "shil_ce.h"
#include "dc/mem/sh4_mem.h"
#include "log/logging_interface.h"

u64 total_ops_removed=0;
void CompileBasicBlock_slow_c(BasicBlock* block,u32 pass);
u32 shil_optimise_pass_btp_main(BasicBlock* bb);

#define shilh(name) bool __fastcall shil_ce_##name(shil_opcode* op,BasicBlock* bb,shil_stream* il)

typedef shilh(FP);

shilh(nimp)
{
	die("Fatal error : Dynarec CE NIMP!");//lol ?
	return false;
}

shil_ce_FP* shil_ce_lut[shilop_count]=
{
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,shil_ce_nimp,
	shil_ce_nimp
};
void SetShilHanlder(shil_opcodes op,shil_ce_FP* ha)
{
	if (op>(shilop_count-1))
	{
		log("SHIL COMPILER ERROR\n");
	}
	if (shil_ce_lut[op]!=shil_ce_nimp)
	{
		log("SHIL COMPILER ERROR [hash table overwrite]\n");
	}

	shil_ce_lut[op]=ha;
}
shilh(pref);
shilh(adc);
shilh(add);
shilh(and);
shilh(cmp);
shilh(fabs);
shilh(fadd);
shilh(fdiv);
shilh(fmac);

shilh(fmul);
shilh(fneg);
shilh(fsub);
shilh(LoadT);
shilh(mov);
shilh(movex);
shilh(neg);
shilh(not);

shilh(or);
shilh(rcl);
shilh(rcr);
shilh(readm);
shilh(rol);
shilh(ror);
shilh(sar);
shilh(SaveT);

shilh(shil_ifb);
shilh(shl);
shilh(shr);
shilh(sub);
shilh(swap);
shilh(test);
shilh(writem);
shilh(xor);
shilh(jcond);
shilh(jmp);
shilh(mul);

shilh(ftrv);
shilh(fsqrt);
shilh(fipr);
shilh(floatfpul);
shilh(ftrc);
shilh(fsca);
shilh(fsrra);
shilh(div32);
shilh(fcmp);

bool Inited_ce_pass=false;
struct RegData
{
	u32 RegValue;//Current reg value (if IsConstant is true)
	u32 RB_value;//last value writen back onto the register (exists olny of IsRB is true)

	bool IsConstant;
	bool IsRB;
};

RegData shil_ce_gpr[sh4_reg_count];
vector<u32> static_reads;
bool shil_ce_is_locked=true;
void Init_ce()
{
	memset(shil_ce_gpr,0,sizeof(shil_ce_gpr));

	if (Inited_ce_pass)
		return;

	Inited_ce_pass=true;
	SetShilHanlder(shilop_pref,shil_ce_pref);
	SetShilHanlder(shilop_adc,shil_ce_adc);
	SetShilHanlder(shilop_add,shil_ce_add);
	SetShilHanlder(shilop_and,shil_ce_and);
	SetShilHanlder(shilop_cmp,shil_ce_cmp);
	SetShilHanlder(shilop_fabs,shil_ce_fabs);
	SetShilHanlder(shilop_fadd,shil_ce_fadd);
	SetShilHanlder(shilop_fdiv,shil_ce_fdiv);
	SetShilHanlder(shilop_fmac,shil_ce_fmac);

	SetShilHanlder(shilop_fmul,shil_ce_fmul);
	SetShilHanlder(shilop_fneg,shil_ce_fneg);
	SetShilHanlder(shilop_fsub,shil_ce_fsub);
	SetShilHanlder(shilop_LoadT,shil_ce_LoadT);
	SetShilHanlder(shilop_mov,shil_ce_mov);
	SetShilHanlder(shilop_movex,shil_ce_movex);
	SetShilHanlder(shilop_neg,shil_ce_neg);
	SetShilHanlder(shilop_not,shil_ce_not);

	SetShilHanlder(shilop_or,shil_ce_or);
	SetShilHanlder(shilop_rcl,shil_ce_rcl);
	SetShilHanlder(shilop_rcr,shil_ce_rcr);
	SetShilHanlder(shilop_readm,shil_ce_readm);
	SetShilHanlder(shilop_rol,shil_ce_rol);
	SetShilHanlder(shilop_ror,shil_ce_ror);
	SetShilHanlder(shilop_sar,shil_ce_sar);
	SetShilHanlder(shilop_SaveT,shil_ce_SaveT);

	SetShilHanlder(shilop_ifb,shil_ce_shil_ifb);
	SetShilHanlder(shilop_shl,shil_ce_shl);
	SetShilHanlder(shilop_shr,shil_ce_shr);
	SetShilHanlder(shilop_sub,shil_ce_sub);
	SetShilHanlder(shilop_swap,shil_ce_swap);
	SetShilHanlder(shilop_test,shil_ce_test);
	SetShilHanlder(shilop_writem,shil_ce_writem);
	SetShilHanlder(shilop_xor,shil_ce_xor);
	SetShilHanlder(shilop_jcond,shil_ce_jcond);
	SetShilHanlder(shilop_jmp,shil_ce_jmp);
	SetShilHanlder(shilop_mul,shil_ce_mul);

	SetShilHanlder(shilop_ftrv,shil_ce_ftrv);
	SetShilHanlder(shilop_fsqrt,shil_ce_fsqrt);
	SetShilHanlder(shilop_fipr,shil_ce_fipr);
	SetShilHanlder(shilop_floatfpul,shil_ce_floatfpul);
	SetShilHanlder(shilop_ftrc,shil_ce_ftrc);
	SetShilHanlder(shilop_fsca,shil_ce_fsca);
	SetShilHanlder(shilop_fsrra,shil_ce_fsrra);
	SetShilHanlder(shilop_div32,shil_ce_div32);
	SetShilHanlder(shilop_fcmp,shil_ce_fcmp);
}

void ce_die(char* reason)
{
	if (reason)
		log("C.E. pass : die [%s]\n",reason);
	else
		log("C.E. pass : die\n");

	__debugbreak();
}
bool ce_CanBeConst(u32 reg)
{
	return (reg<16) || (reg==reg_pc_temp)|| (reg==reg_macl) || (reg==reg_mach);
}

bool ce_IsConst(u32 reg)
{
	if (ce_CanBeConst(reg))
		return shil_ce_gpr[reg].IsConstant;
	else
		return false;
}

u32 ce_GetConst(u32 reg)
{
	if (ce_IsConst(reg))
		return shil_ce_gpr[reg].RegValue;
	else
		ce_die("ce_GetConst : can't get const when reg is not const");
	return 0;
}
void ce_SetConst(u32 reg,u32 value)
{
	if (ce_IsConst(reg))
	{
		if (shil_ce_gpr[reg].RegValue!=value)
		{
			shil_ce_gpr[reg].IsRB=false;
			shil_ce_gpr[reg].RegValue=value;
		}
	}
	else
		ce_die("ce_SetConst : can't set const when reg is not const");
}
void ce_MakeConst(u32 reg,u32 value)
{
	if (ce_CanBeConst(reg))
	{
		if (shil_ce_gpr[reg].IsConstant==false)
		{
			shil_ce_gpr[reg].IsConstant=true;
			shil_ce_gpr[reg].RegValue=value;
			shil_ce_gpr[reg].IsRB=false;
		}
		else
		{
			ce_die("ce_MakeConst : reg is allready const");
			/*
			if (shil_ce_gpr[reg].RegValue!=value)
			{
				shil_ce_gpr[reg].IsRB=false;
				shil_ce_gpr[reg].RegValue=value;
			}
			*/
		}
	}
	else
		ce_die("ce_MakeConst : can't create const when reg can't be const.tracked");
}
void ce_KillConst(u32 reg)
{
	if (ce_IsConst(reg))
	{
		shil_ce_gpr[reg].IsConstant=false;
		shil_ce_gpr[reg].IsRB=false;
	}
}
bool ce_FindExistingConst(u32 value,u8* reg_num)
{
	for (u8 i=0;i<sh4_reg_count;i++)
	{
		if (ce_IsConst(i))
		{
			if ((shil_ce_gpr[i].IsRB==true) && shil_ce_gpr[i].RB_value==value)
			{
				*reg_num=i;
				return true;
			}
		}
	}
	return false;
}

void ce_WriteBack(u32 reg,shil_stream* il)
{
	if (ce_IsConst(reg))
	{
		if ((shil_ce_gpr[reg].IsRB==false) || (shil_ce_gpr[reg].RegValue!=shil_ce_gpr[reg].RB_value))
		{
			/*u8 aliased_reg;
			u32 rv=ce_GetConst(reg);
			 // not realy usable til temporal reg alloc is implemented or smth :)
			if (ce_FindExistingConst(rv,&aliased_reg))
			{
				il->mov((Sh4RegType)reg,(Sh4RegType)aliased_reg);
			}
			else*/
			{
				il->mov((Sh4RegType)reg,ce_GetConst(reg));
			}
			shil_ce_gpr[reg].RB_value=ce_GetConst(reg);
			shil_ce_gpr[reg].IsRB=true;
		}
	}
}

void ce_WriteBack_all(shil_stream* il)
{
	for (u8 i=0;i<sh4_reg_count;i++)
		ce_WriteBack(i,il);
}

void ce_WriteBack_aks(u32 reg,shil_stream* il)
{
	ce_WriteBack(reg,il);
	ce_KillConst(reg);
}
void ce_WriteBack_aks_all(shil_stream* il)
{
	for (u8 i=0;i<sh4_reg_count;i++)
		ce_WriteBack_aks(i,il);
}
bool ce_re_run;

void shil_ce_add_static(u32 addr,u32 sz)
{
	static_reads.push_back(addr);
}
bool shil_ce_check_static(u32 addr,u32 sz)
{
	for (size_t i=0;i<static_reads.size();i++)
	{
		if (static_reads[i]==addr)
			return true;
	}
	return false;
}
bool is_writem_safe(BasicBlock* bb, shil_opcode* op);

//Optimisation pass mainloop
u32 shil_optimise_pass_ce_main(BasicBlock* bb)
{
	bool rv=false;
	ce_re_run=false;
	u32 opt=0;

	Init_ce();

	shil_stream il;

	size_t old_Size=bb->ilst.opcodes.size();

	u32 unsafe_pos=0xFFFFFFFF;
	if (settings.dynarec.Safe)
	{
		shil_ce_is_locked=true;		//block is safe til a write mem opcode that can modify it
		for (size_t i=0;i<bb->ilst.opcodes.size();i++)
		{
			shil_opcode* op=&bb->ilst.opcodes[i];

			if (op->opcode==shilop_writem && !is_writem_safe(bb,op))
			{
				log("Block %08X : disabling read-const @ %d/%d\n",bb->start,i,bb->ilst.opcodes.size());
				unsafe_pos=i;
				break;
			}
		}
	}

	bool old_re_run=ce_re_run;

	for (size_t i=0;i<bb->ilst.opcodes.size();i++)
	{
		shil_opcode* op=&bb->ilst.opcodes[i];

		if (i==unsafe_pos)
			shil_ce_is_locked=false;
		old_re_run=ce_re_run;
		if (shil_ce_lut[op->opcode](op,bb,&il)==false)
			il.opcodes.push_back(*op);//emit the old opcode
		else
		{
			opt++;
			rv=true;
		}
	}

	if (old_re_run!=ce_re_run)
		ce_re_run=false;

	if (rv)
	{
		bb->ilst.opcodes.clear();
		bb->ilst.opcodes.reserve(il.opcodes.size());

		for (size_t i=0;i<il.opcodes.size();i++)
			bb->ilst.opcodes.push_back(il.opcodes[i]);

		//no need to write back reg_pc_temp , it not used after block [its olny a temp reg]:)
		for (u8 i=0;i<16;i++)
		{
			ce_WriteBack_aks(i,&bb->ilst);
		}
		ce_WriteBack_aks(reg_mach,&bb->ilst);
		ce_WriteBack_aks(reg_macl,&bb->ilst);

		bb->ilst.op_count=(u32)bb->ilst.opcodes.size();
	}

	if (old_Size!=bb->ilst.opcodes.size())
		ce_re_run = true;

	return opt;
}

void shil_optimise_pass_ce_driver(BasicBlock* bb)
{
	if (settings.dynarec.CPpass==0)
		return;

	shil_ce_is_locked=true;
	static_reads.clear();

	ce_re_run=true;
	u32 rv=0;
	u32 pass=0;
	size_t old_Size=bb->ilst.opcodes.size();

	while(ce_re_run)
	{
		rv+=shil_optimise_pass_ce_main(bb);
		pass++;
		if (pass>10)
			break;
	}

	total_ops_removed+=old_Size-bb->ilst.opcodes.size();
	shil_optimise_pass_btp_main(bb);

	static_reads.clear();

}
void DefHanlder(shil_opcode* op,BasicBlock* bb,shil_stream* il)
{
	for (u8 i=0;i<16;i++)
	{
		if (ce_IsConst(i))
		{
			if (op->ReadsReg((Sh4RegType)i))
			{
				ce_WriteBack(i,il);
			}
			if (op->WritesReg((Sh4RegType)i))
			{
				ce_KillConst(i);
			}
		}
	}
}
#define NormBinaryOp(oppstrrr)\
if ((op->flags & FLAG_REG2) && (ce_IsConst(op->reg2)))\
	{\
		op->imm1=ce_GetConst(op->reg2);\
		op->flags|=FLAG_IMM1;\
		op->flags&=~FLAG_REG2;\
		ce_re_run=true;\
	}\
	\
	if (ce_IsConst(op->reg1))\
	{\
		if ((op->flags & FLAG_IMM1))\
		{\
			ce_SetConst(op->reg1,ce_GetConst(op->reg1) oppstrrr op->imm1);\
			ce_re_run=true;\
			return true;\
		}\
		else\
		{\
			ce_WriteBack_aks(op->reg1,il);\
		}\
	}\
	else\
	{\
	}\

		//reg1 has to be writen back , and its no more const
		//-> its not const to start with :p not realy needed here
		//reg2 has to be writen back , but it will remain constant ;)
		//-> not needed , if const , the reg1,imm1 form is used
shilh(pref)
{
	if ((op->flags & FLAG_REG1) && (ce_IsConst(op->reg1)))
	{
		op->imm1=ce_GetConst(op->reg1);
		op->flags|=FLAG_IMM1;
		op->flags&=~FLAG_REG1;
	}
	DefHanlder(op,bb,il);
	return false;
}
shilh(adc)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(add)
{
	NormBinaryOp(+);
	return false;
}
shilh(and)
{
	NormBinaryOp(&);
	return false;
}
shilh(cmp)
{
	DefHanlder(op,bb,il);
	return false;
}

shilh(LoadT)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(mov)
{
	bool rv=false;

	if ((op->flags & FLAG_REG2) &&  op->reg1==op->reg2)
	{
		ce_re_run=true;
		return true;
	}

	if ((op->flags & FLAG_REG2) &&  ce_IsConst(op->reg2))
	{
		op->flags&=~FLAG_REG2;
		op->flags|=FLAG_IMM1;
		op->imm1 = ce_GetConst(op->reg2);
		ce_re_run=true;
	}

	if (op->flags & FLAG_IMM1)
	{	//reg1=imm1
		//reg1 gets a known value
		if (ce_CanBeConst(op->reg1))
		{
			if (ce_IsConst(op->reg1))
				ce_SetConst(op->reg1,op->imm1);
			else
				ce_MakeConst(op->reg1,op->imm1);
			rv=true;
		}
		else
		{
			ce_KillConst(op->reg1);
		}
	}
	else
	{
		//reg1 gets an unknown value
		ce_WriteBack(op->reg2,il);
		ce_KillConst(op->reg1);
	}

	return rv;
}
shilh(movex)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(neg)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(not)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(or)
{
	NormBinaryOp(|);
	return false;
}
shilh(rcl)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(rcr)
{
	DefHanlder(op,bb,il);
	return false;
}
//ReadMem

bool ce_ReadWriteParams(shil_opcode* op)
{
	bool rv=false;

	if (op->flags & FLAG_REG2)
	{
		if (ce_IsConst(op->reg2))
		{
			u32 vl=ce_GetConst(op->reg2);
			if (op->flags & FLAG_IMM1)
				op->imm1+=vl;
			else
				op->imm1=vl;

			rv=true;
			op->flags |= FLAG_IMM1;
			op->flags&=~FLAG_REG2;
		}
	}
	if (op->flags & FLAG_R0)
	{
		if (ce_IsConst(r0))
		{
			u32 vl=ce_GetConst(r0);
			if (op->flags & FLAG_IMM1)
				op->imm1+=vl;
			else
				op->imm1=vl;

			rv=true;
			op->flags |= FLAG_IMM1;
			op->flags&=~FLAG_R0;
		}
	}
	if (op->flags & FLAG_GBR)
	{
		if (ce_IsConst(reg_gbr))
		{
			u32 vl=ce_GetConst(reg_gbr);
			if (op->flags & FLAG_IMM1)
				op->imm1+=vl;
			else
				op->imm1=vl;

			rv=true;
			op->flags |= FLAG_IMM1;
			op->flags&=~FLAG_GBR;
		}
	}

	return rv;
}
shilh(readm)
{
	bool rv=ce_ReadWriteParams(op);

	u32 addr;

	if (GetRamReadAdr(op,&addr))
	{
		u32 size=op->flags&3;
		if (shil_ce_is_locked && bb->IsMemLocked(addr) && (size!=FLAG_64))
		{
			u32 data=0;
			shil_ce_add_static(addr,size);
			if (size==FLAG_64)
			{
				Sh4RegType base=(Sh4RegType)GetSingleFromDouble((u8)op->reg1);
				il->mov(base,ReadMem32(addr));
				il->mov((Sh4RegType)(base+1),ReadMem32(addr+4));
			}
			else
			{
				if (size==0)
				{
					verify(op->flags & FLAG_SX);
					data=(u32)(s32)(s8)ReadMem8(addr);
				}
				else if (size==1)
				{
					verify(op->flags & FLAG_SX);
					data=(u32)(s32)(s16)ReadMem16(addr);
				}
				else
				{
					verify((op->flags & FLAG_SX)==0);
					data=ReadMem32(addr);
				}

				bb->locked.push_back(addr);
				bb->locked.push_back(data);

				if (ce_CanBeConst(op->reg1))
				{
					if (ce_IsConst(op->reg1))
					{
						ce_SetConst(op->reg1,data);
					}
					else
						ce_MakeConst(op->reg1,data);
				}
				else
				{
					il->mov((Sh4RegType)op->reg1,data);
				}
			}

			ce_re_run=true;
			return true;
		}
	}

	//even if we did optimise smth , a readback may be needed
	//since it uses opcode flags to decode what to write back , it should't cause any non needed write backs ;)
	DefHanlder(op,bb,il);

	//we did chainge smth :0
	if (rv)//we optimised something , re run the ce pass once more
		ce_re_run=true;

	return false;
}

bool is_writem_safe(BasicBlock* bb, shil_opcode* op)
{

	u32 addr;
	if (GetRamReadAdr(op,&addr))
	{
		return !bb->IsMemLocked(addr);
		/*
		if (bb->IsMemLocked(addr))
		{
			return false;	//block modifies its page. not safe.
		}
		else
		{
			return true;	//block writes somewhere outside of its page. safe.
		}*/
	}

	//any non-static write may write in the block page list, so its not safe by default
	return false;
}
shilh(writem)
{
	bool rv=ce_ReadWriteParams(op);
	u32 addr;
	//bool kill_all=false;
	if (GetRamReadAdr(op,&addr))
	{
		u32 size=op->flags&3;
		if (shil_ce_is_locked && bb->IsMemLocked(addr) && (size!=FLAG_64))
		{
			verify(!settings.dynarec.Safe);	//can't happen in safe mode
			shil_ce_is_locked=false;
			log("CE: Block will be demoted to manual for the CE pass\n");
		}
	}
	//even if we did optimise smth , a readback may be needed
	//since it uses opcode flags to decode what to write back , it should't cause any non needed write backs ;)
	DefHanlder(op,bb,il);

	//we did chainge smth :0
	if (rv)//we optimised something , re run the ce pass once more
		ce_re_run=true;
/*
	if (kill_all)
		ce_WriteBack_aks_all(il);
*/
	return false;
}
shilh(rol)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(ror)
{
	DefHanlder(op,bb,il);
	return false;
}
//uses flags from previous opcode
shilh(SaveT)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(shil_ifb)
{
	ce_WriteBack_aks_all(il);
	return false;
}
shilh(sar)
{
	if (ce_IsConst(op->reg1) && (op->imm1>1))
	{
		ce_SetConst(op->reg1,(u32)(((s32)ce_GetConst(op->reg1))>>(op->imm1)));
		return true;
	}
	else
		DefHanlder(op,bb,il);
	return false;
}
shilh(shl)
{
	if (ce_IsConst(op->reg1) && (op->imm1>1))
	{
		ce_SetConst(op->reg1,ce_GetConst(op->reg1)<<(op->imm1));
		return true;
	}
	else
		DefHanlder(op,bb,il);
	return false;
}
shilh(shr)
{
	if (ce_IsConst(op->reg1) && (op->imm1>1))
	{
		ce_SetConst(op->reg1,ce_GetConst(op->reg1)>>(op->imm1));
		return true;
	}
	else
		DefHanlder(op,bb,il);
	return false;
}
//sets T if used as DT
shilh(sub)
{
	//that's all we can do for now
	//DT uses sub , and it needs flags to be ok after it
	if ((op->flags & FLAG_REG2) && (ce_IsConst(op->reg2)))
	{
		op->imm1=ce_GetConst(op->reg2);
		op->flags|=FLAG_IMM1;
		op->flags&=~FLAG_REG2;
	}

	DefHanlder(op,bb,il);
	return false;
}
shilh(swap)
{
	if (ce_IsConst(op->reg1))
	{
		u32 size=op->flags&3;
		if (size==FLAG_8)
		{
			u32 ov=ce_GetConst(op->reg1);
			ov=(ov & 0xFFFF0000) | ((ov&0xFF)<<8) | ((ov>>8)&0xFF);
			ce_SetConst(op->reg1,ov);
		}
		else
		{
			u32 ov=ce_GetConst(op->reg1);
			ov=(ov>>16)|(ov<<16);
			ce_SetConst(op->reg1,ov);
		}

		return true;
	}
	else
		DefHanlder(op,bb,il);
	return false;
}
//sets T
shilh(test)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(xor)
{
	NormBinaryOp(^);
	return false;
}
shilh(jcond)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(jmp)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(mul)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(div32)
{
	ce_WriteBack_aks(op->reg1,il);
	ce_WriteBack_aks(op->reg2,il);
	ce_WriteBack_aks((u8)op->imm1,il);
	DefHanlder(op,bb,il);
	return false;
}
shilh(fcmp)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fabs)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fadd)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fdiv)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fmac)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fmul)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fneg)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fsub)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(ftrv)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fsqrt)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fipr)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(floatfpul)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(ftrc)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fsca)
{
	DefHanlder(op,bb,il);
	return false;
}
shilh(fsrra)
{
	DefHanlder(op,bb,il);
	return false;
}

//works olny for moves
//a very limited form of dce pre scan ;)
bool backscan_const(BasicBlock* bb,u8 reg,u32* rv)
{
	u32 lop=bb->ilst.op_count;
	shil_stream* ilst=&bb->ilst;

	//look if reg takes a const value
	while (lop)
	{
		lop--;
		shil_opcode* op=&ilst->opcodes[lop];
		//if move (we can take const value olny from here for now)
		if (op->opcode==shilop_mov && op->reg1==reg)
		{
			//if the reg we want became a const , were finished :D
			if (op->flags & FLAG_IMM1)
			{
				*rv=op->imm1;
				return true;
			}
			else
			{
				//if its a reg 2 reg move , we alias the old reg w/ the one that replaced it ;)
				verify(op->flags & FLAG_REG2);
				reg=(u8)op->reg2;
			}
		}
		else
		{
			//we get writen an unknown value , unable to fix it up :p
			if (op->WritesReg((Sh4RegType)reg))
				return false;
		}
	}

	//we failed to find a const on the entire block
	return false;
}

u32 shil_optimise_pass_btp_main(BasicBlock* bb)
{
	if ((bb->flags.ExitType==BLOCK_EXITTYPE_DYNAMIC) ||
		(bb->flags.ExitType==BLOCK_EXITTYPE_DYNAMIC_CALL))
	{
		u32 new_cv=0;
		if (backscan_const(bb,reg_pc,&new_cv))
		{
			//log("Block promote 0x%X , from DYNAMIC to FIXED exit 0x%X : %d\n",bb->start,new_cv,bb->flags.ExitType);
			bb->TF_next_addr=new_cv;
			if (bb->flags.ExitType==BLOCK_EXITTYPE_DYNAMIC)
				bb->flags.ExitType=BLOCK_EXITTYPE_FIXED;
			else
				bb->flags.ExitType=BLOCK_EXITTYPE_FIXED_CALL;
		}
	}

	return 1;
}


logger::global_data::
    ~global_data (
    )
    {
        unregister_thread_end_handler(*this,&global_data::thread_end_handler);
    }

#ifndef DLIB_LOGGER_KERNEL_1_CPp_
#define DLIB_LOGGER_KERNEL_1_CPp_

#include "logger_kernel_1.h"
#include <iostream>
#include <sstream>

namespace dlib
{

    void set_all_logging_output_streams (
        std::ostream& out_
    )
    {
        logger::global_data& gd = logger::get_global_data();
        auto_mutex M(gd.m);
        gd.loggers.reset();
        while (gd.loggers.move_next())
        {
            gd.loggers.element()->out.rdbuf(out_.rdbuf());
            gd.loggers.element()->hook.clear();
        }

        gd.set_output_stream("",out_);

        // set the default hook to be an empty member function pointer
        logger::hook_mfp hook;
        gd.set_output_hook("",hook);
    }

    void set_all_logging_levels (
        const log_level& new_level
    )
    {
        logger::global_data& gd = logger::get_global_data();
        auto_mutex M(gd.m);
        gd.loggers.reset();
        while (gd.loggers.move_next())
        {
            gd.loggers.element()->cur_level = new_level;
        }

        gd.set_level("",new_level);
    }

    void set_all_logging_headers (
        const print_header_type& new_header
    )
    {
        logger::global_data& gd = logger::get_global_data();
        auto_mutex M(gd.m);
        gd.loggers.reset();
        while (gd.loggers.move_next())
        {
            gd.loggers.element()->print_header = new_header;
        }

        gd.set_logger_header("",new_header);
    }

    namespace logger_helper_stuff
    {
        class helper
        {
        public:
            helper()
            {
                std::ostringstream sout;
                print_default_logger_header(sout,"some_name",LDEBUG,0);
            }
        };
        // do this to make sure all the static members of print_default_logger_header get
        // initialized when the program turns on.
        static helper a;
        // make a logger to make extra sure the static global_data object gets
        // initialized before any threads start up.  Also do this so that there is always
        // at least one logger so that the global data won't be deleted until the
        // program is terminating.
        static logger log("dlib");
    }

    void print_default_logger_header (
        std::ostream& out,
        const std::string& logger_name,
        const log_level& l,
        const uint64 thread_id
    )
    {
        using namespace std;
        static timestamper ts;
        static const uint64 first_time = ts.get_timestamp();

        const uint64 cur_time = (ts.get_timestamp() - first_time)/1000;
        streamsize old_width = out.width(); out.width(5);
        out << cur_time << " " << l.name;
        out.width(old_width);

        out << " [" << thread_id << "] " << logger_name << ": ";
    }

//                 global_data stuff

    logger::global_data::
    global_data(
    ) :
        next_thread_name(1)
    {
        // make sure the main program thread always has id 0.  Since there is
        // a global logger object declared in this file we should expect that
        // the global_data object will be initialized in the main program thread
        // so if we call get_thread_id() now we should get the main thread id.
        thread_id_type main_id = get_thread_id();
        uint64 id_zero = 0;
        thread_names.add(main_id,id_zero);

        // set up the defaults
        auto_flush_table.val = true;
        streambuf_table.val = std::cout.rdbuf();
        header_table.val = print_default_logger_header;

        // also allocate an initial buffer for hook based logging
        hookbuf.buffer.reserve(1000);
    }

    logger::global_data::level_container::
    level_container (
    ) : val(300,"ERROR") {}

    template <typename T>
    const T& search_tables (
        const T& c,
        const std::string& name
    )
    {
        if (c.table.size() == 0 || name.size() == 0)
            return c;

        const std::string::size_type pos = name.find_first_of(".");
        const std::string first = name.substr(0,pos);
        std::string last;
        if (pos != std::string::npos)
            last = name.substr(pos+1);

        if (c.table.is_in_domain(first))
        {
            return search_tables(*c.table[first], last);
        }
        else
        {
            return c;
        }
    }

    template <typename T, typename U>
    void assign_tables (
        T& c,
        const std::string& name,
        const U& val
    )
    {
        if (name.size() == 0)
        {
            c.val = val;
            c.table.clear();
            return;
        }

        const std::string::size_type pos = name.find_first_of(".");
        std::string first = name.substr(0,pos);
        std::string last;
        if (pos != std::string::npos)
            last = name.substr(pos+1);

        if (c.table.is_in_domain(first))
        {
            assign_tables(*c.table[first], last, val);
        }
        else
        {
            std::unique_ptr<T> temp (new T);
            temp->val = c.val;
            assign_tables(*temp, last, val);
            c.table.add(first,temp);
        }
    }

    const log_level logger::global_data::
    level (
        const std::string& name
    ) const
    {
        auto_mutex M(m);
        return search_tables(level_table, name).val;
    }

    void logger::global_data::
    set_level (
        const std::string& name,
        const log_level& new_level
    )
    {
        auto_mutex M(m);
        assign_tables(level_table, name, new_level);
    }

    bool logger::global_data::
    auto_flush (
        const std::string& name
    ) const
    {
        auto_mutex M(m);
        return search_tables(auto_flush_table, name).val;
    }

    void logger::global_data::
    set_auto_flush (
        const std::string& name,
        bool enabled
    )
    {
        auto_mutex M(m);
        assign_tables(auto_flush_table, name, enabled);
    }

    std::streambuf* logger::global_data::
    output_streambuf (
        const std::string& name
    )
    {
        auto_mutex M(m);
        return search_tables(streambuf_table, name).val;
    }

    void logger::global_data::
    set_output_stream (
        const std::string& name,
        std::ostream& out_
    )
    {
        auto_mutex M(m);
        assign_tables( streambuf_table, name, out_.rdbuf());
    }

    void logger::global_data::
    set_output_stream (
        const std::string& name,
        std::streambuf& buf
    )
    {
        auto_mutex M(m);
        assign_tables( streambuf_table, name, &buf);
    }

    logger::hook_mfp logger::global_data::
    output_hook (
        const std::string& name
    )
    {
        auto_mutex M(m);
        return search_tables(hook_table, name).val;
    }

    void logger::global_data::
    set_output_hook (
        const std::string& name,
        const hook_mfp& hook
    )
    {
        auto_mutex M(m);
        assign_tables( hook_table, name, hook);
    }

    print_header_type logger::global_data::
    logger_header (
        const std::string& name
    )
    {
        auto_mutex M(m);
        return search_tables(header_table, name).val;
    }

    void logger::global_data::
    set_logger_header (
        const std::string& name,
        print_header_type ph
    )
    {
        auto_mutex M(m);
        assign_tables(header_table, name, ph);
    }

    logger::global_data& logger::get_global_data()
    {
        // Allocate the global_data on the heap rather than on the stack because
        // we want to guard against the case where this static object would be destroyed
        // during program termination BEFORE all logger objects are destroyed.
        static global_data* gd = new global_data;
        return *gd;
    }

    void logger::global_data::
    thread_end_handler (
    )
    {
        auto_mutex M(m);
        thread_id_type id = get_thread_id();
        thread_id_type junkd;
        uint64 junkr;
        thread_names.remove(id,junkd,junkr);
    }

    uint64 logger::global_data::
    get_thread_name (
    )
    {
        thread_id_type id = get_thread_id();
        uint64 thread_name;
        if (thread_names.is_in_domain(id))
        {
            thread_name = thread_names[id];
        }
        else
        {
            if (is_dlib_thread(id))
                register_thread_end_handler(*this,&global_data::thread_end_handler);
            thread_name = next_thread_name;
            thread_names.add(id,thread_name);
            thread_name = next_thread_name;
            ++next_thread_name;
        }
        return thread_name;
    }

//               logger_stream stuff

    void logger::logger_stream::
    print_header_and_stuff (
    )
    {
        if (!been_used)
        {
            log.gd.m.lock();

            // Check if the output hook is setup.  If it isn't then we print the logger
            // header like normal.  Otherwise we need to remember to clear out the output
            // stringstream we always write to.
            if (log.hook.is_set() == false)
            {
                log.logger_header()(log.out,log.name(),l,log.gd.get_thread_name());
            }
            else
            {
                // Make sure the hook buffer doesn't have any old data in it before we start
                // logging a new message into it.
                log.gd.hookbuf.buffer.resize(0);
            }
            been_used = true;
        }
    }

    void logger::logger_stream::
    print_end_of_line (
    )
    {
        auto_unlock M(log.gd.m);

        if (log.hook.is_set() == false)
        {
            if (log.auto_flush_enabled)
                log.out << std::endl;
            else
                log.out << "\n";
        }
        else
        {
            // Make sure the buffer is a proper C-string
            log.gd.hookbuf.buffer.push_back('\0');
            // call the output hook with all the info regarding this log message.
            log.hook(log.name(), l, log.gd.get_thread_name(), &log.gd.hookbuf.buffer[0]);
        }
    }

//         logger stuff

    logger::
    logger (
        const std::string& name_
    ) :
        gd(get_global_data()),
        logger_name(name_),
        out(gd.output_streambuf(logger_name)),
        cur_level(gd.level(logger_name))
    {
        DLIB_ASSERT(name_[0] != '\0',
                    "\tlogger::logger()"
                    << "\n\tYou can't make a logger with an empty name"
                    << "\n\tthis: " << this
        );

        auto_mutex M(gd.m);
        logger* temp = this;
        gd.loggers.add(temp);

        // load the appropriate settings
        print_header        = gd.logger_header(logger_name);
        auto_flush_enabled  = gd.auto_flush(logger_name);
        hook                = gd.output_hook(logger_name);
    }

    logger::
    ~logger (
    )
    {
        gd.m.lock();
        gd.loggers.destroy(this);
        // if this was the last logger then delete the global data
        if (gd.loggers.size() == 0)
        {
            gd.m.unlock();
            delete &gd;
        }
        else
        {
            gd.m.unlock();
        }
    }

}

#endif // DLIB_LOGGER_KERNEL_1_CPp_


//                     The LLVM Compiler Infrastructure
/// \file
/// This file defines a stage.
/// A chain of stages compose an instruction pipeline.

#include "Stage.h"

namespace mca {

// Pin the vtable here in the implementation file.

void Stage::addListener(HWEventListener *Listener) {
  Listeners.insert(Listener);
}

} // namespace mca


bool check(ll n, ll k, ll mid){
    // this function checks whether the number mid is a possible maximum element of series
    // i.e. if a series can be made with max element mid whose sum is divisible by k
    if (mid==1) return n%k==0;
    return mid*n >= k;
}

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

/*
Consider two separated case:-
1) When the maximum element is 1
2) When the maximum elenent is not 1

Case 1 is possible only when 1+1+1..n times is divisible by k
i.e. n is divisible by k

In case 2, let the max element be x.
It is only possible to contruct a series which is divisible by k (with max element x)
if x*n is greater than k
This is because x can be replaced by any number smaller number to adjust the sum such that
it becomes either equal or divisible by k

e.g n=5 k=3
Clearly, 3 is a possible candidate for maximum element
Let us check for 2, 2*5 = 20 > 3. Then 2 can also serve as the maximum element - {1,1,1,1,2}
Let us check for 1, 1*5 = 5 (not divisible by 3). So, 1 cannot be the max element

Our aim is to find a number x using binary search from 1 to k
*/

int main(){

    ll t, n, k;
    cin >> t; // test cases
    while (t--){
        cin >> n >> k;
        ll low=1, high=k, mid, ans=k;
        // k is always a possible maximum element of series
        // because sum of a series {k,k,k,...} is always divisible by k
        while (low<=high){
            mid = (low+high)/2; // calculating middle number
            if (check(n, k, mid)){
                ans = mid;
                high = mid-1;
                // if mid is a possible candidate
                // then perform the check on smaller numbers only
            } else {
                low = mid+1;
                // if mid is too small to be the max element
                // then perform the check on bigger numbers only
            }
        }
        cout << ans << endl;
    }

    return 0;
}


std::wstring
Utility::SystemTimeToString(
    SYSTEMTIME SystemTime
    )
{
    constexpr size_t STR_LEN = 64;
    wchar_t dateStr[STR_LEN] = { 0 };

#pragma warning(suppress : 38042)
    GetDateFormatEx(0, 0, &SystemTime, L"yyyy-MM-dd", dateStr, STR_LEN, 0);

    wchar_t timeStr[STR_LEN] = { 0 };
    GetTimeFormatEx(0, 0, &SystemTime, L"HH:mm:ss", timeStr, STR_LEN);

    return FormatString(L"%sT%s.000Z", dateStr, timeStr);
}

#include "pch.h"

using namespace std;

/// Contains utility functions to format strings, convert time to string, etc.

/// Returns the string representation of a SYSTEMTIME structure that can be used
/// in an XML query for Windows Event collection.
/// \param SystemTime         SYSTEMTIME with the time to format.
/// \return The string with the human-readable time.

/// Returns a string representation of a FILETIME.
/// \param FileTime         FILETIME with the time to format.
/// \return The string with the human-readable time.
std::wstring
Utility::FileTimeToString(
    FILETIME FileTime
    )
{
    SYSTEMTIME systemTime;
    FileTimeToSystemTime(&FileTime, &systemTime);
    return SystemTimeToString(systemTime);
}

/// Returns a formatted wide string.
/// \param FormatString         Format string that follows the same specifications as format in printf.
/// \param ...                  Variable arguments list
/// \return The composed string.
std::wstring
Utility::FormatString(
    _In_ _Printf_format_string_ LPCWSTR FormatString,
    ...
    )
{
    std::wstring result = L"";
    va_list vaList;
    va_start(vaList, FormatString);

    size_t length = ::_vscwprintf(FormatString, vaList);

    try
    {
        result.resize(length);

        if (-1 == ::vswprintf_s(&result[0], length + 1, FormatString, vaList))
        {
            // Ignore failure and continue
        }
    }
    catch (...) {}

    va_end(vaList);

    return result;
}

/// Verify if the input stream is in UTF-8 format.
/// UTF-8 is the encoding of Unicode based on Internet Society RFC2279
/// \param lpstrInputStream     Pointer to a buffer with the wide text to evaluate.
/// \param iLen                 Size of the buffer.
/// \return If the text is UTF-8, return true. Otherwise, false
bool
Utility::IsTextUTF8(
    LPCSTR InputStream,
    int Length
    )
{
    int nChars = MultiByteToWideChar(CP_UTF8,
                                      MB_ERR_INVALID_CHARS,
                                      InputStream,
                                      Length,
                                      NULL,
                                      0);

    return nChars > 0 || GetLastError() != ERROR_NO_UNICODE_TRANSLATION;
}

/// Verify if the input stream is in Unicode format.
/// \param lpstrInputStream     Pointer to a buffer with the wide text to evaluate.
/// \param iLen                 Size of the buffer.
/// \return If the text is unicode, return true. Otherwise, false
bool
Utility::IsInputTextUnicode(
    LPCSTR InputStream,
    int Length
    )
{
    int  iResult = ~0; // turn on IS_TEXT_UNICODE_DBCS_LEADBYTE
    bool bUnicode;

    bUnicode = IsTextUnicode(InputStream, Length, &iResult);

    // If the only hint is based on statistics, assume ANSI for short strings
    // This protects against short ansi strings like "this program can break" from being
    // detected as unicode
    if (bUnicode && iResult == IS_TEXT_UNICODE_STATISTICS && Length < 100)
    {
        bUnicode = false;
    }

    return bUnicode;
}

/// Get the short path name of the file. If the function
/// failed to get the short path, then it returns original path.
/// \param Path Full path of a file or directory
/// \return Short path of a file or directory
std::wstring
Utility::GetShortPath(
    _In_ const std::wstring& Path
    )
{
    DWORD bufSz = 1024;
    std::vector<wchar_t> buf(bufSz);

    DWORD pathSz = GetShortPathNameW(Path.c_str(), &(buf[0]), bufSz);
    if (pathSz >= bufSz)
    {
        buf.resize(pathSz + 1);
        if (GetShortPathNameW(Path.c_str(), &(buf[0]), pathSz + 1))
        {
            wstring shortPath = &buf[0];
            return shortPath.c_str();
        }
    }
    else if (pathSz)
    {
        wstring shortPath = &buf[0];
        return shortPath.c_str();
    }

    return Path;
}

/// Get the long path name of the file. If the function
/// failed to get the long path, then it returns original path.
/// \param Path Full path of a file or directory
/// \return Long path of a file or directory
std::wstring
Utility::GetLongPath(
    _In_ const std::wstring& Path
    )
{
    DWORD bufSz = 1024;
    std::vector<wchar_t> buf(bufSz);

    DWORD pathSz = GetLongPathNameW(Path.c_str(), &(buf[0]), bufSz);
    if(pathSz >= bufSz)
    {
        buf.resize(pathSz + 1);
        if (GetLongPathNameW(Path.c_str(), &(buf[0]), pathSz + 1))
        {
            wstring longPath = &buf[0];
            return longPath.c_str();
        }
    }
    else if (pathSz)
    {
        wstring longPath = &buf[0];
        return longPath.c_str();
    }

    return Path;
}


try
    {
        result.resize(length);

        if (-1 == ::vswprintf_s(&result[0], length + 1, FormatString, vaList))
        {
            // Ignore failure and continue
        }
    }
    catch (...) {}

#include "pch.h"

using namespace std;

/// Contains utility functions to format strings, convert time to string, etc.

/// Returns the string representation of a SYSTEMTIME structure that can be used
/// in an XML query for Windows Event collection.
/// \param SystemTime         SYSTEMTIME with the time to format.
/// \return The string with the human-readable time.
std::wstring
Utility::SystemTimeToString(
    SYSTEMTIME SystemTime
    )
{
    constexpr size_t STR_LEN = 64;
    wchar_t dateStr[STR_LEN] = { 0 };

#pragma warning(suppress : 38042)
    GetDateFormatEx(0, 0, &SystemTime, L"yyyy-MM-dd", dateStr, STR_LEN, 0);

    wchar_t timeStr[STR_LEN] = { 0 };
    GetTimeFormatEx(0, 0, &SystemTime, L"HH:mm:ss", timeStr, STR_LEN);

    return FormatString(L"%sT%s.000Z", dateStr, timeStr);
}

/// Returns a string representation of a FILETIME.
/// \param FileTime         FILETIME with the time to format.
/// \return The string with the human-readable time.
std::wstring
Utility::FileTimeToString(
    FILETIME FileTime
    )
{
    SYSTEMTIME systemTime;
    FileTimeToSystemTime(&FileTime, &systemTime);
    return SystemTimeToString(systemTime);
}

/// Returns a formatted wide string.
/// \param FormatString         Format string that follows the same specifications as format in printf.
/// \param ...                  Variable arguments list
/// \return The composed string.
std::wstring
Utility::FormatString(
    _In_ _Printf_format_string_ LPCWSTR FormatString,
    ...
    )
{
    std::wstring result = L"";
    va_list vaList;
    va_start(vaList, FormatString);

    size_t length = ::_vscwprintf(FormatString, vaList);

    va_end(vaList);

    return result;
}

/// Verify if the input stream is in UTF-8 format.
/// UTF-8 is the encoding of Unicode based on Internet Society RFC2279
/// \param lpstrInputStream     Pointer to a buffer with the wide text to evaluate.
/// \param iLen                 Size of the buffer.
/// \return If the text is UTF-8, return true. Otherwise, false
bool
Utility::IsTextUTF8(
    LPCSTR InputStream,
    int Length
    )
{
    int nChars = MultiByteToWideChar(CP_UTF8,
                                      MB_ERR_INVALID_CHARS,
                                      InputStream,
                                      Length,
                                      NULL,
                                      0);

    return nChars > 0 || GetLastError() != ERROR_NO_UNICODE_TRANSLATION;
}

/// Verify if the input stream is in Unicode format.
/// \param lpstrInputStream     Pointer to a buffer with the wide text to evaluate.
/// \param iLen                 Size of the buffer.
/// \return If the text is unicode, return true. Otherwise, false
bool
Utility::IsInputTextUnicode(
    LPCSTR InputStream,
    int Length
    )
{
    int  iResult = ~0; // turn on IS_TEXT_UNICODE_DBCS_LEADBYTE
    bool bUnicode;

    bUnicode = IsTextUnicode(InputStream, Length, &iResult);

    // If the only hint is based on statistics, assume ANSI for short strings
    // This protects against short ansi strings like "this program can break" from being
    // detected as unicode
    if (bUnicode && iResult == IS_TEXT_UNICODE_STATISTICS && Length < 100)
    {
        bUnicode = false;
    }

    return bUnicode;
}

/// Get the short path name of the file. If the function
/// failed to get the short path, then it returns original path.
/// \param Path Full path of a file or directory
/// \return Short path of a file or directory
std::wstring
Utility::GetShortPath(
    _In_ const std::wstring& Path
    )
{
    DWORD bufSz = 1024;
    std::vector<wchar_t> buf(bufSz);

    DWORD pathSz = GetShortPathNameW(Path.c_str(), &(buf[0]), bufSz);
    if (pathSz >= bufSz)
    {
        buf.resize(pathSz + 1);
        if (GetShortPathNameW(Path.c_str(), &(buf[0]), pathSz + 1))
        {
            wstring shortPath = &buf[0];
            return shortPath.c_str();
        }
    }
    else if (pathSz)
    {
        wstring shortPath = &buf[0];
        return shortPath.c_str();
    }

    return Path;
}

/// Get the long path name of the file. If the function
/// failed to get the long path, then it returns original path.
/// \param Path Full path of a file or directory
/// \return Long path of a file or directory
std::wstring
Utility::GetLongPath(
    _In_ const std::wstring& Path
    )
{
    DWORD bufSz = 1024;
    std::vector<wchar_t> buf(bufSz);

    DWORD pathSz = GetLongPathNameW(Path.c_str(), &(buf[0]), bufSz);
    if(pathSz >= bufSz)
    {
        buf.resize(pathSz + 1);
        if (GetLongPathNameW(Path.c_str(), &(buf[0]), pathSz + 1))
        {
            wstring longPath = &buf[0];
            return longPath.c_str();
        }
    }
    else if (pathSz)
    {
        wstring longPath = &buf[0];
        return longPath.c_str();
    }

    return Path;
}


void TrigThetas::vdSinCos(const int n, const double a[], double r1[],
                          double r2[]) {
  Eigen::Map<const Eigen::ArrayXd> a_map(a, n);
  Eigen::Map<Eigen::ArrayXd> r1_map(r1, n);
  Eigen::Map<Eigen::ArrayXd> r2_map(r2, n);

  r1_map = a_map.sin();
  r2_map = a_map.cos();
}

// This file is part of the libWetHair open source project

#include "DegreesOfFreedom.h"

#include "ElasticStrandUtils.h"

void Edges::compute() {
  m_value.resize(m_size);
  const VecX& dofs = m_dofs.get();
  for (IndexType vtx = 0; vtx < m_firstValidIndex; ++vtx) {
    m_value[vtx].setZero();
  }
  for (IndexType vtx = m_firstValidIndex; vtx < size(); ++vtx) {
    m_value[vtx] = dofs.segment<3>(4 * (vtx + 1)) - dofs.segment<3>(4 * vtx);
  }

  setDependentsDirty();
}

void Lengths::compute() {
  m_value.resize(m_size);
  const Vec3Array& edges = m_edges.get();
  for (IndexType vtx = 0; vtx < m_firstValidIndex; ++vtx) {
    m_value[vtx] = 0.0;
  }
  for (IndexType vtx = m_firstValidIndex; vtx < size(); ++vtx) {
    m_value[vtx] = edges[vtx].norm();
    // assert( !isSmall(m_value[vtx]) ); // Commented-out assert, as it be may
    // thrown while we're checking stuff
  }

  setDependentsDirty();
}

void Tangents::compute() {
  m_value.resize(m_size);
  const Vec3Array& edges = m_edges.get();
  const std::vector<scalar>& lengths = m_lengths.get();
  for (IndexType vtx = 0; vtx < m_firstValidIndex; ++vtx) {
    m_value[vtx].setZero();
  }
  for (IndexType vtx = m_firstValidIndex; vtx < size(); ++vtx) {
    m_value[vtx] = edges[vtx] / lengths[vtx];
  }

  setDependentsDirty();
}

void CurvatureBinormals::compute() {
  m_value.resize(m_size);
  for (IndexType vtx = 0; vtx < m_firstValidIndex; ++vtx) {
    m_value[vtx].setZero();
  }

  const Vec3Array& tangents = m_tangents.get();

  for (IndexType vtx = m_firstValidIndex; vtx < size(); ++vtx) {
    const Vec3& t1 = tangents[vtx - 1];
    const Vec3& t2 = tangents[vtx];

    scalar denominator = 1. + t1.dot(t2);

    if (denominator <= 0. || isSmall(denominator)) {
      if (denominator <= 0.) {
        denominator = 1. + t1.normalized().dot(t2.normalized());
      }

      if (denominator <= 0.) {
        std::cerr << "CurvatureBinormals::compute() denominator == "
                  << denominator << " at vertex " << vtx << " t1 = " << t1
                  << " t2 = " << t2 << std::endl;

        m_value[vtx] = Vec3::Constant(
            std::numeric_limits<scalar>::infinity());  // Should not be
      } else {
        m_value[vtx] = 4. * std::tan(.5 * std::acos(denominator - 1.)) *
                       findNormal(t1).segment<3>(0);
      }
    } else {
      m_value[vtx] = 2.0 * t1.cross(t2) / denominator;
    }
  }

  setDependentsDirty();
}

// Probably not as fast as MKL, but portable.

void TrigThetas::compute() {
  const VecX& dofs = m_dofs.get();
  const IndexType numThetas = m_dofs.getNumEdges();
  m_value.first.resize(numThetas);
  m_value.second.resize(numThetas);

  // Extract thetas in their own vector for mkl_vlm
  const Eigen::Map<const VecX, Eigen::Unaligned, Eigen::InnerStride<4> >
      thetasMap(dofs.data() + 3, numThetas);
  const VecX thetaVec(thetasMap);
  // Compute their sine and cosine
  vdSinCos(
      numThetas, thetaVec.data(), m_value.first.data(),
      m_value.second.data());  // FIXME this won't compile if scalar != double

  setDependentsDirty();
}


PPALDD_CONSTRUCTOR(LotOps)
{
	if(Valid) {
		AssignHeadData(&H, sizeof(H));
		AssignDefIterData(&I, sizeof(I));
	}
}

// V_LOTOP.CPP
#include <pp.h>
#pragma hdrstop

IMPLEMENT_PPFILT_FACTORY(LotOp); LotOpFilt::LotOpFilt() : PPBaseFilt(PPFILT_LOTOP, 0, 0)
{
	SetFlatChunk(offsetof(LotOpFilt, ReserveStart),
		offsetof(LotOpFilt, Reserve)-offsetof(LotOpFilt, ReserveStart)+sizeof(Reserve));
	Init(1, 0);
}

PPViewLotOp::PPViewLotOp() : PPView(0, &Filt, PPVIEW_LOTOP, 0, REPORT_LOTOPS), P_BObj(BillObj), State(0)
{
	SETFLAG(State, stAccsCost, P_BObj->CheckRights(BILLRT_ACCSCOST));
}

int PPViewLotOp::Init_(const PPBaseFilt * pFilt)
{
	State &= ~stCtrlX;
	return Helper_InitBaseFilt(pFilt);
}

int PPViewLotOp::InitIteration()
{
	int    ok = 1;
	DBQ  * dbq = 0;
	DateRange period;
	TransferTbl::Key2 k;
	Transfer * p_tt = P_BObj->trfr;
	BExtQuery::ZDelete(&P_IterQuery);
	THROW_MEM(P_IterQuery = new BExtQuery(p_tt, 2));
	P_IterQuery->selectAll();
	period.Z();
	AdjustPeriodToRights(period, 0);
	if(Filt.Flags & LotOpFilt::fZeroLotOps)
		dbq = & (p_tt->LotID == 0L && daterange(p_tt->Dt, &period) && (p_tt->Flags & PPTFR_RECEIPT) == PPTFR_RECEIPT);
	else {
		ReceiptTbl::Rec lot_rec;
		THROW(GetLotRec(&lot_rec) > 0);
		if(lot_rec.GoodsID < 0)
			dbq = & (p_tt->LotID == Filt.LotID && daterange(p_tt->Dt, &period));
		else
			dbq = & (p_tt->LotID == Filt.LotID && daterange(p_tt->Dt, &period) && p_tt->LocID == lot_rec.LocID);
	}
	P_IterQuery->where(*dbq);
	MEMSZERO(k);
	k.LotID = Filt.LotID;
	k.Dt = period.low;
	P_IterQuery->initIteration(0, &k, spGe);
	CATCHZOK
	return ok;
}

int FASTCALL PPViewLotOp::NextIteration(LotOpViewItem * pItem)
{
	if(P_IterQuery && P_IterQuery->nextIteration() > 0) {
		if(pItem) {
			P_BObj->trfr->copyBufTo(pItem);
			pItem->OldQtty = 0.0;
			pItem->OldCost = 0.0;
			pItem->OldPrice = 0.0;
			{
				PPTransferItem ti;
				ti.SetupByRec(pItem);
				if(ti.LotID) {
					ReceiptTbl::Rec lot_rec;
					if(P_BObj->trfr->Rcpt.Search(ti.LotID, &lot_rec) > 0)
						P_BObj->trfr->SetupItemByLot(&ti, &lot_rec, 1, pItem->OprNo);
				}
				double cost = 0.0;
				double old_cost = 0.0;
				double price = 0.0;
				double old_price = 0.0;
				double qtty  = 0.0;
				double old_qtty = 0.0;
				if(ti.Flags & PPTFR_REVAL) {
					cost = (State & stAccsCost) ? ti.Cost : 0.0;
					price = ti.Price;
					old_cost = (State & stAccsCost) ? ti.RevalCost : 0.0;
					old_price = ti.Discount;
				}
				else {
					cost = old_cost = (State & stAccsCost) ? ti.Cost : 0.0;
					price = old_price = ti.NetPrice();
				}
				if(ti.Flags & PPTFR_CORRECTION) {
					qtty = fabs(ti.Quantity_);
					old_qtty  = fabs(ti.QuotPrice);
				}
				else {
					qtty = old_qtty = fabs(ti.Qtty());
				}
				pItem->Quantity = qtty;
				pItem->Cost = cost;
				pItem->Price = price;
				pItem->OldQtty = old_qtty;
				pItem->OldCost = old_cost;
				pItem->OldPrice = old_price;
			}
		}
		return 1;
	}
	return -1;
}

int PPViewLotOp::GetLotRec(ReceiptTbl::Rec * pRec)
{
	int    ok = -1;
	if(Filt.LotID && P_BObj->trfr->Rcpt.Search(Filt.LotID, pRec) > 0)
		ok = 1;
	else
		memzero(pRec, sizeof(ReceiptTbl::Rec));
	return ok;
}

static IMPL_DBE_PROC(dbqf_lotopladingbill_i)
{
	PPID   bill_id = params[0].lval;
	if(bill_id) {
		BillTbl::Rec rec;
		if(BillObj->Search(bill_id, &rec) > 0) {
			if(rec.OpID == 0 && rec.LinkBillID)
				bill_id = rec.LinkBillID;
		}
	}
	result->init(bill_id);
}

int PPViewLotOp::DynFuncLadingBill = 0;

DBQuery * PPViewLotOp::CreateBrowserQuery(uint * pBrwId, SString * pSubTitle)
{
	DbqFuncTab::RegisterDyn(&DynFuncLadingBill, BTS_INT, dbqf_lotopladingbill_i, 1, BTS_INT);

	PPID   loc_id = 0;
	uint   brw_id = 0;
	DateRange period;
	TransferTbl * trf = 0;
	BillTbl * bll = 0;
	BillTbl * lnk_bll = 0;
	DBQuery * q   = 0;
	DBE    dbe_oprkind;
	DBE    dbe_ar;
	DBE    dbe_price;
	DBE    dbe_goods;
	DBE    dbe_bill;
	DBConst zero_cost;
	zero_cost.init(0.0);
	period.Z();
	AdjustPeriodToRights(period, 0);
	THROW(CheckTblPtr(trf = new TransferTbl));
	THROW(CheckTblPtr(bll = new BillTbl));
	PPDbqFuncPool::InitObjNameFunc(dbe_ar, PPDbqFuncPool::IdObjNameAr, bll->Object);
	{
		dbe_price.init();
		dbe_price.push(bll->OpID);
		dbe_price.push(trf->LotID);
		dbe_price.push(trf->Dt);
		dbe_price.push(trf->OprNo);
		dbe_price.push(trf->Cost);
		dbe_price.push(trf->Price);
		dbe_price.push(trf->Discount);
		dbe_price.push(static_cast<DBFunc>(PPDbqFuncPool::IdTrfrPrice));
	}
	if(Filt.Flags & LotOpFilt::fZeroLotOps) {
		brw_id = BROWSER_ZEROLOTOPS;
		PPDbqFuncPool::InitObjNameFunc(dbe_goods, PPDbqFuncPool::IdObjNameGoods, trf->GoodsID);
		if(State & stAccsCost)
			q = &select(trf->Dt, trf->OprNo, bll->Code, dbe_ar, trf->Quantity, trf->Rest, trf->Cost, dbe_price, dbe_goods, 0L);
		else
			q = &select(trf->Dt, trf->OprNo, bll->Code, dbe_ar, trf->Quantity, trf->Rest, zero_cost, dbe_price, dbe_goods, 0L);
		q->from(trf, bll, 0L).where(trf->LotID == 0L && daterange(trf->Dt, &period) &&
			(trf->Flags & PPTFR_RECEIPT) == PPTFR_RECEIPT && bll->ID == trf->BillID).
			orderBy(trf->LotID, trf->Dt, trf->OprNo, 0L);
	}
	else {
		ReceiptTbl::Rec lot_rec;
		PPObjGoods goods_obj;
		THROW(P_BObj->trfr->Rcpt.Search(Filt.LotID, &lot_rec) > 0);
		if(goods_obj.CheckFlag(lot_rec.GoodsID, GF_USEINDEPWT))
			brw_id = BROWSER_OPSBYLOT_WT;
		else
			brw_id = BROWSER_OPSBYLOT;
		PPDbqFuncPool::InitObjNameFunc(dbe_oprkind, PPDbqFuncPool::IdObjNameOprKind, bll->OpID);
		q = &select(trf->Dt, 0L);    // #0
		q->addField(trf->OprNo);     // #1
		q->addField(bll->Code);      // #2
		q->addField(dbe_ar);         // #3
		q->addField(trf->Quantity);  // #4
		q->addField(trf->Rest);      // #5
		if(State & stAccsCost)
			q->addField(trf->Cost);  // #6
		else
			q->addField(zero_cost);  // #6
		q->addField(dbe_price);      // #7
		q->addField(dbe_oprkind);    // #8
		q->addField(trf->WtQtty);    // #9
		q->addField(trf->WtRest);    // #10
		q->from(trf, bll, 0L);
		if(lot_rec.GoodsID < 0) {
			PPDbqFuncPool::InitLongFunc(dbe_bill, DynFuncLadingBill, trf->BillID);
			q->where(trf->LotID == Filt.LotID && daterange(trf->Dt, &period) && (bll->ID += dbe_bill));
		}
		else {
			loc_id = lot_rec.LocID;
			q->where(trf->LotID == Filt.LotID && daterange(trf->Dt, &period) &&
				trf->LocID == loc_id && (bll->ID += trf->BillID));
		}
		q->orderBy(trf->LotID, trf->Dt, trf->OprNo, 0L);
	}
	THROW(CheckQueryPtr(q));
	if(pSubTitle) {
		*pSubTitle = 0;
		if(Filt.LotID) {
			ReceiptTbl::Rec lot_rec;
			if(P_BObj->trfr->Rcpt.Search(Filt.LotID, &lot_rec) > 0) {
				SString temp_buf;
				pSubTitle->Cat(GetGoodsName(lot_rec.GoodsID, temp_buf)).CatDiv('-', 1);
				GetLocationName(lot_rec.LocID, temp_buf);
				pSubTitle->Cat(temp_buf);
			}
		}
	}
	CATCH
		if(q)
			ZDELETE(q);
		else {
			delete bll;
			delete trf;
		}
	ENDCATCH
	ASSIGN_PTR(pBrwId, brw_id);
	return q;
}

int PPViewLotOp::SearchOp(const BrwHdr * pHdr, TransferTbl::Rec * pRec)
{
	if(pHdr) {
		TransferTbl::Key1 k;
		k.Dt = pHdr->Dt;
		k.OprNo = pHdr->OprNo;
		return SearchByKey(P_BObj->trfr, 1, &k, pRec);
	}
	else
		return 0;
}

int PPViewLotOp::ProcessCommand(uint ppvCmd, const void * pHdr, PPViewBrowser * pBrw)
{
	int    ok = PPView::ProcessCommand(ppvCmd, pHdr, pBrw);
	if(ok == -2) {
		const BrwHdr * p_hdr = pHdr ? static_cast<const BrwHdr *>(pHdr) : 0;
		switch(ppvCmd) {
			case PPVCMD_EDITBILL:
				ok = -1;
				if(p_hdr && P_BObj && P_BObj->trfr) {
					TransferTbl::Rec rec;
					if(SearchOp(p_hdr, &rec) > 0) {
						PPID   bill_id = rec.BillID;
						BillTbl::Rec bill_rec;
						if(P_BObj->Search(bill_id, &bill_rec) > 0) {
							if(bill_rec.OpID == 0 && bill_rec.LinkBillID)
								bill_id = bill_rec.LinkBillID;
							int    r = P_BObj->Edit(&bill_id, 0);
							if(r == 0)
								ok = PPErrorZ();
							else if(r > 0)
								ok = 1;
						}
					}
				}
				break;
			case PPVCMD_MOVLOTOP:
				ok = MoveOp(p_hdr);
				break;
			case PPVCMD_DORECOVER:
				ok = Recover(p_hdr);
				break;
		}
	}
	return ok;
}

class RcvrTrfrDialog : public TDialog {
	DECL_DIALOG_DATA(TransferTbl::Rec);
public:
	RcvrTrfrDialog() : TDialog(DLG_RCVRTRFR)
	{
	}
	DECL_DIALOG_SETDTS()
	{
		RVALUEPTR(Data, pData);
		AddClusterAssoc(CTL_RCVRTRFR_WHAT, 0, 1);
		AddClusterAssoc(CTL_RCVRTRFR_WHAT, 1, 2);
		AddClusterAssoc(CTL_RCVRTRFR_WHAT, 2, 3);
		AddClusterAssoc(CTL_RCVRTRFR_WHAT, 3, 4);
		SetClusterData(CTL_RCVRTRFR_WHAT, 1);
		SString temp_buf;
		temp_buf.CatHex(Data.Flags);
		setCtrlString(CTL_RCVRTRFR_VAL, temp_buf);
		return 1;
	}
	DECL_DIALOG_GETDTS()
	{
		long   v = 0;
		SString temp_buf;
		GetClusterData(CTL_RCVRTRFR_WHAT, &v);
		getCtrlString(CTL_RCVRTRFR_VAL, temp_buf);
		if(v == 1) {
			sscanf(temp_buf.Strip(), "%lx", &Data.Flags);
		}
		else if(v == 2) {
			int    sign = (Data.Quantity < 0) ? -1 : 1;
			Data.Quantity = fabs(temp_buf.ToReal()) * sign;
		}
		else if(v == 3) {
			Data.Price = temp_buf.ToReal();
		}
		else if(v == 4) {
			Data.Discount = temp_buf.ToReal();
		}
		ASSIGN_PTR(pData, Data);
		return 1;
	}
private:
	DECL_HANDLE_EVENT
	{
		TDialog::handleEvent(event);
		if(event.isClusterClk(CTL_RCVRTRFR_WHAT)) {
			long   v = 0;
			GetClusterData(CTL_RCVRTRFR_WHAT, &v);
			SString temp_buf;
			if(v == 1)
				temp_buf.CatHex(Data.Flags);
			else if(v == 2)
				temp_buf.Cat(Data.Quantity, MKSFMTD(0, 6, NMBF_NOTRAILZ));
			else if(v == 3)
				temp_buf.Cat(Data.Price, MKSFMTD(0, 5, NMBF_NOTRAILZ));
			else if(v == 4)
				temp_buf.Cat(Data.Discount, MKSFMTD(0, 5, NMBF_NOTRAILZ));
			setCtrlString(CTL_RCVRTRFR_VAL, temp_buf);
			clearEvent(event);
		}
	}
};

int PPViewLotOp::Recover(const BrwHdr * pHdr)
{
	int    ok = -1;
	RcvrTrfrDialog * dlg = 0;
	if(PPMaster && CConfig.Flags & CCFLG_DEBUG) {
		dlg = new RcvrTrfrDialog;
		TransferTbl::Rec rec;
		if(SearchOp(pHdr, &rec) > 0) {
			dlg->setDTS(&rec);
			if(ExecView(dlg) == cmOK) {
				long   v = 0;
				dlg->getDTS(&rec);
				dlg->GetClusterData(CTL_RCVRTRFR_WHAT, &v);
				DBUpdateSet * p_upd_set = 0;
				Transfer * p_tt = P_BObj->trfr;
				if(v == 1)
					p_upd_set = &set(p_tt->Flags, dbconst(rec.Flags));
				else if(v == 2)
					p_upd_set = &set(p_tt->Quantity, dbconst(rec.Quantity));
				else if(v == 3)
					p_upd_set = &set(p_tt->Price, dbconst(rec.Price));
				else if(v == 4)
					p_upd_set = &set(p_tt->Discount, dbconst(rec.Discount));
				if(p_upd_set) {
					THROW_DB(updateFor(p_tt, 1, (p_tt->Dt == pHdr->Dt && p_tt->OprNo == pHdr->OprNo), *p_upd_set));
				}
			}
		}
	}
	CATCHZOKPPERR
	delete dlg;
	return ok;
}

int PPViewLotOp::MoveOp(const BrwHdr * pHdr)
{
	int    ok = -1;
	TransferTbl::Rec rec;
	if(SearchOp(pHdr, &rec) > 0) {
		const  PPID src_id = rec.LotID;
		if((Filt.Flags & LotOpFilt::fZeroLotOps) || (src_id && !(rec.Flags & (PPTFR_REVAL|PPTFR_RECEIPT)))) {
			PPObjBill::SelectLotParam slp(rec.GoodsID, rec.LocID, src_id, 0);
			while(P_BObj->SelectLot2(slp) > 0) {
				if(slp.RetLotID) {
					ok = P_BObj->trfr->MoveLotOp(src_id, pHdr->Dt, pHdr->OprNo, slp.RetLotID, 1) ? 1 : PPErrorZ();
					break;
				}
			}
		}
	}
	if(ok > 0)
		BaseState |= bsOuterChangesStatus;
	return ok;
}

int PPViewLotOp::Detail(const void * pHdr, PPViewBrowser * pBrw)
{
	int    ok = -1;
	const  BrwHdr * p_hdr = pHdr ? static_cast<const BrwHdr *>(pHdr) : 0;
	if(p_hdr)
		if(Filt.Flags & LotOpFilt::fZeroLotOps) {
			if(MoveOp(p_hdr) > 0)
				ok = 1;
		}
		else {
			TransferTbl::Rec rec;
			if(P_BObj->trfr->SearchMirror(p_hdr->Dt, p_hdr->OprNo, &rec) > 0 && rec.LotID) {
				Filt.LotID = rec.LotID;
				ChangeFilt(1, pBrw);
				ok = 1;
			}
		}
	return ok;
}

int STDCALL ViewOpersByLot(PPID lotID, int withZeroLotID)
{
	int    ok = -1;
	LotOpFilt flt;
	PPViewLotOp * p_v = new PPViewLotOp;
	THROW_MEM(p_v);
	flt.LotID = lotID;
	SETFLAG(flt.Flags, LotOpFilt::fZeroLotOps, withZeroLotID);
	PPWaitStart();
	THROW(p_v->Init_(&flt));
	THROW(p_v->Browse(0));
	if(p_v->GetOuterChangesStatus())
		ok = 1;
	CATCHZOKPPERR
	delete p_v;
	return ok;
}
// Implementation of PPALDD_LotOps

PPALDD_DESTRUCTOR(LotOps) { Destroy(); }

int PPALDD_LotOps::InitData(PPFilt & rFilt, long rsrv)
{
	PPViewLotOp * p_v = 0;
	if(rsrv) {
		p_v = static_cast<PPViewLotOp *>(rFilt.Ptr);
		Extra[1].Ptr = p_v;
	}
	else {
		p_v = new PPViewLotOp;
		Extra[0].Ptr = p_v;
		p_v->Init_(static_cast<const LotOpFilt *>(rFilt.Ptr));
	}
	ReceiptTbl::Rec lot_rec;
	p_v->GetLotRec(&lot_rec);
	H.LotID = lot_rec.ID;
	return DlRtm::InitData(rFilt, rsrv);
}

int PPALDD_LotOps::InitIteration(PPIterID iterId, int sortId, long /*rsrv*/)
{
	INIT_PPVIEW_ALDD_ITER(LotOp);
}

int PPALDD_LotOps::NextIteration(PPIterID iterId)
{
	START_PPVIEW_ALDD_ITER(LotOp);
	I.BillID   = item.BillID;
	I.CurID    = item.CurID;
	I.Dt       = item.Dt;
	I.OprNo    = static_cast<short>(item.OprNo);
	I.Qtty     = item.Quantity;
	I.Rest     = item.Rest;
	I.Cost     = TR5(item.Cost);
	I.Price    = TR5(item.Price);
	I.Discount = TR5(item.Discount);
	I.CurPrice = TR5(item.CurPrice);
	I.OldQtty = item.OldQtty;
	I.OldCost = TR5(item.OldCost);
	I.OldPrice = TR5(item.OldPrice);
	FINISH_PPVIEW_ALDD_ITER();
}

void PPALDD_LotOps::Destroy() { DESTROY_PPVIEW_ALDD(LotOp); }


try
			{
				if (dictionaryFileLastWrite == std::filesystem::last_write_time(DICTIONARY_SAVE_FILE)) return;
				dictionaryFileLastWrite = std::filesystem::last_write_time(DICTIONARY_SAVE_FILE);
			}
			catch (std::filesystem::filesystem_error) { return; }

#include "qtcommon.h"
#include "extension.h"
#include "ui_extrawindow.h"
#include "blockmarkup.h"
#include <fstream>
#include <process.h>
#include <QRegularExpression>
#include <QColorDialog>
#include <QFontDialog>
#include <QMenu>
#include <QPainter>
#include <QGraphicsEffect>
#include <QFontMetrics>
#include <QMouseEvent>
#include <QWheelEvent>

extern const char* EXTRA_WINDOW_INFO;
extern const char* SENTENCE_TOO_BIG;
extern const char* MAX_SENTENCE_SIZE;
extern const char* TOPMOST;
extern const char* OPACITY;
extern const char* SHOW_ORIGINAL;
extern const char* SHOW_ORIGINAL_INFO;
extern const char* SIZE_LOCK;
extern const char* DICTIONARY;
extern const char* DICTIONARY_INSTRUCTIONS;
extern const char* BG_COLOR;
extern const char* TEXT_COLOR;
extern const char* TEXT_OUTLINE;
extern const char* OUTLINE_COLOR;
extern const char* OUTLINE_SIZE;
extern const char* OUTLINE_SIZE_INFO;
extern const char* FONT;
extern const char* SAVE_SETTINGS;

constexpr auto DICTIONARY_SAVE_FILE = u8"SavedDictionary.txt";

QColor colorPrompt(QWidget* parent, QColor default, const QString& title, bool customOpacity = true)
{
	QColor color = QColorDialog::getColor(default, parent, title);
	if (customOpacity) color.setAlpha(255 * QInputDialog::getDouble(parent, title, OPACITY, default.alpha() / 255.0, 0, 1, 3, nullptr, Qt::WindowCloseButtonHint));
	return color;
}

struct PrettyWindow : QDialog, Localizer
{
	PrettyWindow(const char* name)
	{
		ui.setupUi(this);
		ui.display->setGraphicsEffect(outliner = new Outliner);
		setWindowFlags(Qt::FramelessWindowHint);
		setAttribute(Qt::WA_TranslucentBackground);

		settings.beginGroup(name);
		QFont font = ui.display->font();
		if (font.fromString(settings.value(FONT, font.toString()).toString())) ui.display->setFont(font);
		SetBackgroundColor(settings.value(BG_COLOR, backgroundColor).value<QColor>());
		SetTextColor(settings.value(TEXT_COLOR, TextColor()).value<QColor>());
		outliner->color = settings.value(OUTLINE_COLOR, outliner->color).value<QColor>();
		outliner->size = settings.value(OUTLINE_SIZE, outliner->size).toDouble();
		menu.addAction(FONT, this, &PrettyWindow::RequestFont);
		menu.addAction(BG_COLOR, [this] { SetBackgroundColor(colorPrompt(this, backgroundColor, BG_COLOR)); });
		menu.addAction(TEXT_COLOR, [this] { SetTextColor(colorPrompt(this, TextColor(), TEXT_COLOR)); });
		QAction* outlineAction = menu.addAction(TEXT_OUTLINE, this, &PrettyWindow::SetOutline);
		outlineAction->setCheckable(true);
		outlineAction->setChecked(outliner->size >= 0);
		connect(ui.display, &QLabel::customContextMenuRequested, [this](QPoint point) { menu.exec(mapToGlobal(point)); });
	}

	~PrettyWindow()
	{
		settings.sync();
	}

	Ui::ExtraWindow ui;

protected:
	QMenu menu{ ui.display };
	Settings settings{ this };

private:
	void RequestFont()
	{
		if (QFont font = QFontDialog::getFont(&ok, ui.display->font(), this, FONT); ok)
		{
			settings.setValue(FONT, font.toString());
			ui.display->setFont(font);
		}
	};

	void SetBackgroundColor(QColor color)
	{
		if (!color.isValid()) return;
		if (color.alpha() == 0) color.setAlpha(1);
		backgroundColor = color;
		repaint();
		settings.setValue(BG_COLOR, color.name(QColor::HexArgb));
	};

	QColor TextColor()
	{
		return ui.display->palette().color(QPalette::WindowText);
	}

	void SetTextColor(QColor color)
	{
		if (!color.isValid()) return;
		ui.display->setPalette(QPalette(color, {}, {}, {}, {}, {}, {}));
		settings.setValue(TEXT_COLOR, color.name(QColor::HexArgb));
	};

	void SetOutline(bool enable)
	{
		if (enable)
		{
			QColor color = colorPrompt(this, outliner->color, OUTLINE_COLOR);
			if (color.isValid()) outliner->color = color;
			outliner->size = QInputDialog::getDouble(this, OUTLINE_SIZE, OUTLINE_SIZE_INFO, 0.5, 0, INT_MAX, 2, nullptr, Qt::WindowCloseButtonHint);
		}
		else outliner->size = -1;
		settings.setValue(OUTLINE_COLOR, outliner->color.name(QColor::HexArgb));
		settings.setValue(OUTLINE_SIZE, outliner->size);
	}

	void paintEvent(QPaintEvent*) override
	{
		QPainter(this).fillRect(rect(), backgroundColor);
	}

	QColor backgroundColor{ palette().window().color() };
	struct Outliner : QGraphicsEffect
	{
		void draw(QPainter* painter) override
		{
			if (size < 0) return drawSource(painter);
			QPoint offset;
			QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset);
			offset.setX(offset.x() + size);
			for (auto offset2 : Array<QPointF>{ { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 }, { 1, 1 }, { 1, -1 }, { -1, 1 }, { -1, -1 } })
			{
				QImage outline = pixmap.toImage();
				QPainter outlinePainter(&outline);
				outlinePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
				outlinePainter.fillRect(outline.rect(), color);
				painter->drawImage(offset + offset2 * size, outline);
			}
			painter->drawPixmap(offset, pixmap);
		}
		QColor color{ Qt::black };
		double size = -1;
	}* outliner;
};

class ExtraWindow : public PrettyWindow
{
public:
	ExtraWindow() : PrettyWindow("Extra Window")
	{
		ui.display->setTextFormat(Qt::PlainText);
		if (settings.contains(WINDOW) && QApplication::screenAt(settings.value(WINDOW).toRect().bottomRight())) setGeometry(settings.value(WINDOW).toRect());
		maxSentenceSize = settings.value(MAX_SENTENCE_SIZE, maxSentenceSize).toInt();

		for (auto [name, default, slot] : Array<const char*, bool, void(ExtraWindow::*)(bool)>{
			{ TOPMOST, false, &ExtraWindow::SetTopmost },
			{ SIZE_LOCK, false, &ExtraWindow::SetLock },
			{ SHOW_ORIGINAL, true, &ExtraWindow::SetShowOriginal },
			{ DICTIONARY, false, &ExtraWindow::SetUseDictionary },
		})
		{
			// delay processing anything until Textractor has finished initializing
			QMetaObject::invokeMethod(this, std::bind(slot, this, default = settings.value(name, default).toBool()), Qt::QueuedConnection);
			auto action = menu.addAction(name, this, slot);
			action->setCheckable(true);
			action->setChecked(default);
		}
		menu.addAction(MAX_SENTENCE_SIZE, this, [this]
		{
			settings.setValue(MAX_SENTENCE_SIZE, maxSentenceSize = QInputDialog::getInt(this, MAX_SENTENCE_SIZE, "", maxSentenceSize, 0, INT_MAX, 1, nullptr, Qt::WindowCloseButtonHint));
		});
		ui.display->installEventFilter(this);
		ui.display->setMouseTracking(true);

		QMetaObject::invokeMethod(this, [this]
		{
			show();
			AddSentence(EXTRA_WINDOW_INFO);
		}, Qt::QueuedConnection);
	}

	~ExtraWindow()
	{
		settings.setValue(WINDOW, geometry());
	}

	void AddSentence(QString sentence)
	{
		if (sentence.size() > maxSentenceSize) sentence = SENTENCE_TOO_BIG;
		if (!showOriginal && sentence.contains(u8"\x200b \n")) sentence = sentence.split(u8"\x200b \n")[1];
		sanitize(sentence);
		sentence.chop(std::distance(std::remove(sentence.begin(), sentence.end(), QChar::Tabulation), sentence.end()));
		sentenceHistory.push_back(sentence);
		historyIndex = sentenceHistory.size() - 1;
		ui.display->setText(sentence);
	}

private:
	void SetTopmost(bool topmost)
	{
		for (auto window : { winId(), dictionaryWindow.winId() })
			SetWindowPos((HWND)window, topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
		settings.setValue(TOPMOST, topmost);
	};

	void SetLock(bool locked)
	{
		setSizeGripEnabled(!locked);
		settings.setValue(SIZE_LOCK, this->locked = locked);
	};

	void SetShowOriginal(bool showOriginal)
	{
		if (!showOriginal && settings.value(SHOW_ORIGINAL, false).toBool()) QMessageBox::information(this, SHOW_ORIGINAL, SHOW_ORIGINAL_INFO);
		settings.setValue(SHOW_ORIGINAL, this->showOriginal = showOriginal);
	};

	void SetUseDictionary(bool useDictionary)
	{
		if (useDictionary)
		{
			dictionaryWindow.UpdateDictionary();
			if (dictionaryWindow.dictionary.empty())
			{
				std::ofstream(DICTIONARY_SAVE_FILE) << u8"\ufeff" << DICTIONARY_INSTRUCTIONS;
				_spawnlp(_P_DETACH, "notepad", "notepad", DICTIONARY_SAVE_FILE, NULL); // show file to user
			}
		}
		settings.setValue(DICTIONARY, this->useDictionary = useDictionary);
	}

	void ComputeDictionaryPosition(QPoint mouse)
	{
		QString sentence = ui.display->text();
		const QFont& font = ui.display->font();
		if (cachedDisplayInfo.CompareExchange(ui.display))
		{
			QFontMetrics fontMetrics(font, ui.display);
			textPositionMap.clear();
			for (int i = 0, height = 0, lineBreak = 0; i < sentence.size(); ++i)
			{
				int block = 1;
				for (int charHeight = fontMetrics.boundingRect(0, 0, 1, INT_MAX, Qt::TextWordWrap, sentence.mid(i, 1)).height();
					i + block < sentence.size() && fontMetrics.boundingRect(0, 0, 1, INT_MAX, Qt::TextWordWrap, sentence.mid(i, block + 1)).height() < charHeight * 1.5; ++block);
				auto boundingRect = fontMetrics.boundingRect(0, 0, ui.display->width(), INT_MAX, Qt::TextWordWrap, sentence.left(i + block));
				if (boundingRect.height() > height)
				{
					height = boundingRect.height();
					lineBreak = i;
				}
				textPositionMap.push_back({
					fontMetrics.boundingRect(0, 0, ui.display->width(), INT_MAX, Qt::TextWordWrap, sentence.mid(lineBreak, i - lineBreak + 1)).width(),
					height
				});
			}
		}
		int i;
		for (i = 0; i < textPositionMap.size(); ++i) if (textPositionMap[i].y() > mouse.y() && textPositionMap[i].x() > mouse.x()) break;
		if (i == textPositionMap.size() || (mouse - textPositionMap[i]).manhattanLength() > font.pointSize() * 3) return dictionaryWindow.hide();
		if (sentence.mid(i) == dictionaryWindow.term) return dictionaryWindow.ShowDefinition();
		dictionaryWindow.ui.display->setFixedWidth(ui.display->width() * 3 / 4);
		dictionaryWindow.SetTerm(sentence.mid(i));
		int left = i == 0 ? 0 : textPositionMap[i - 1].x(), right = textPositionMap[i].x(),
			x = textPositionMap[i].x() > ui.display->width() / 2 ? -dictionaryWindow.width() + (right * 3 + left) / 4 : (left * 3 + right) / 4, y = 0;
		for (auto point : textPositionMap) if (point.y() > y && point.y() < textPositionMap[i].y()) y = point.y();
		dictionaryWindow.move(ui.display->mapToGlobal(QPoint(x, y - dictionaryWindow.height())));
	}

	bool eventFilter(QObject*, QEvent* event) override
	{
		if (useDictionary && event->type() == QEvent::MouseMove) ComputeDictionaryPosition(((QMouseEvent*)event)->localPos().toPoint());
		if (event->type() == QEvent::MouseButtonPress) dictionaryWindow.hide();
		return false;
	}

	void mousePressEvent(QMouseEvent* event) override
	{
		dictionaryWindow.hide();
		oldPos = event->globalPos();
	}

	void mouseMoveEvent(QMouseEvent* event) override
	{
		if (!locked) move(pos() + event->globalPos() - oldPos);
		oldPos = event->globalPos();
	}

	void wheelEvent(QWheelEvent* event) override
	{
		int scroll = event->angleDelta().y();
		if (scroll > 0 && historyIndex > 0) ui.display->setText(sentenceHistory[--historyIndex]);
		if (scroll < 0 && historyIndex + 1 < sentenceHistory.size()) ui.display->setText(sentenceHistory[++historyIndex]);
	}

	bool locked, showOriginal, useDictionary;
	int maxSentenceSize = 1000;
	QPoint oldPos;

	class
	{
	public:
		bool CompareExchange(QLabel* display)
		{
			if (display->text() == text && display->font() == font && display->width() == width) return false;
			text = display->text();
			font = display->font();
			width = display->width();
			return true;
		}

	private:
		QString text;
		QFont font;
		int width;
	} cachedDisplayInfo;
	std::vector<QPoint> textPositionMap;

	std::vector<QString> sentenceHistory;
	int historyIndex = 0;

	class DictionaryWindow : public PrettyWindow
	{
	public:
		DictionaryWindow() : PrettyWindow("Dictionary Window")
		{
			ui.display->setSizePolicy({ QSizePolicy::Fixed, QSizePolicy::Minimum });
		}

		void UpdateDictionary()
		{

dictionary.clear();
			charStorage.clear();

			auto StoreCopy = [&](std::string_view string)
			{
				auto location = &*charStorage.insert(charStorage.end(), string.begin(), string.end());
				charStorage.push_back(0);
				return location;
			};

			charStorage.reserve(std::filesystem::file_size(DICTIONARY_SAVE_FILE));
			std::ifstream stream(DICTIONARY_SAVE_FILE);
			BlockMarkupIterator savedDictionary(stream, Array<std::string_view>{ "|TERM|", "|DEFINITION|" });
			while (auto read = savedDictionary.Next())
			{
				const auto& [terms, definition] = read.value();
				auto storedDefinition = StoreCopy(definition);
				std::string_view termsView = terms;
				size_t start = 0, end = termsView.find("|TERM|");
				while (end != std::string::npos)
				{
					dictionary.push_back(DictionaryEntry{ StoreCopy(termsView.substr(start, end - start)), storedDefinition });
					start = end + 6;
					end = termsView.find("|TERM|", start);
				}
				dictionary.push_back(DictionaryEntry{ StoreCopy(termsView.substr(start)), storedDefinition });
			}
			std::stable_sort(dictionary.begin(), dictionary.end());

			inflections.clear();
			stream.seekg(0);
			BlockMarkupIterator savedInflections(stream, Array<std::string_view>{ "|ROOT|", "|INFLECTS TO|", "|NAME|" });
			while (auto read = savedInflections.Next())
			{
				const auto& [root, inflectsTo, name] = read.value();
				if (!inflections.emplace_back(Inflection{
					S(root),
					QRegularExpression(QRegularExpression::anchoredPattern(S(inflectsTo)), QRegularExpression::UseUnicodePropertiesOption),
					S(name)
				}).inflectsTo.isValid()) TEXTRACTOR_MESSAGE(L"Invalid regex: %s", StringToWideString(inflectsTo));
			}
		}

		void SetTerm(QString term)
		{
			this->term = term;
			UpdateDictionary();
			definitions.clear();
			definitionIndex = 0;
			std::unordered_set<const char*> foundDefinitions;
			for (term = term.left(100); !term.isEmpty(); term.chop(1))
				for (const auto& [rootTerm, definition, inflections] : LookupDefinitions(term, foundDefinitions))
					definitions.push_back(
						QStringLiteral("
%1 (%5/%6)
%2%3%4").arg(
							term.split("<<")[0].toHtmlEscaped(),
							rootTerm.split("<<")[0].toHtmlEscaped(),
							inflections.join(""),
							definition
						)
					);
			for (int i = 0; i < definitions.size(); ++i) definitions[i] = definitions[i].arg(i + 1).arg(definitions.size());
			ShowDefinition();
		}

		void ShowDefinition()
		{
			if (definitions.empty()) return hide();
			ui.display->setText(definitions[definitionIndex]);
			adjustSize();
			resize(width(), 1);
			show();
		}

		struct DictionaryEntry
		{
			const char* term;
			const char* definition;
			bool operator<(DictionaryEntry other) const { return strcmp(term, other.term) < 0; }
		};
		std::vector<DictionaryEntry> dictionary;
		QString term;

	private:
		struct LookupResult
		{
			QString term;
			QString definition;
			QStringList inflectionsUsed;
		};
		std::vector<LookupResult> LookupDefinitions(QString term, std::unordered_set<const char*>& foundDefinitions, QStringList inflectionsUsed = {})
		{
			std::vector<LookupResult> results;
			for (auto [it, end] = std::equal_range(dictionary.begin(), dictionary.end(), DictionaryEntry{ term.toUtf8() }); it != end; ++it)
				if (foundDefinitions.emplace(it->definition).second) results.push_back({ term, it->definition, inflectionsUsed });
			for (const auto& inflection : inflections) if (auto match = inflection.inflectsTo.match(term); match.hasMatch())
			{
				QStringList currentInflectionsUsed = inflectionsUsed;
				currentInflectionsUsed.push_front(inflection.name);
				QString root;
				for (const auto& ch : inflection.root) root += ch.isDigit() ? match.captured(ch.digitValue()) : ch;
				for (const auto& definition : LookupDefinitions(root, foundDefinitions, currentInflectionsUsed)) results.push_back(definition);
			}
			return results;
		}

		void wheelEvent(QWheelEvent* event) override
		{
			int scroll = event->angleDelta().y();
			if (scroll > 0 && definitionIndex > 0) definitionIndex -= 1;
			if (scroll < 0 && definitionIndex + 1 < definitions.size()) definitionIndex += 1;
			int oldHeight = height();
			ShowDefinition();
			move(x(), y() + oldHeight - height());
		}

		struct Inflection
		{
			QString root;
			QRegularExpression inflectsTo;
			QString name;
		};
		std::vector<Inflection> inflections;

		std::filesystem::file_time_type dictionaryFileLastWrite;
		std::vector<char> charStorage;
		std::vector<QString> definitions;
		int definitionIndex;
	} dictionaryWindow;
} extraWindow;

bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
{
	if (sentenceInfo["current select"] && sentenceInfo["text number"] != 0)
		QMetaObject::invokeMethod(&extraWindow, [sentence = S(sentence)] { extraWindow.AddSentence(sentence); });
	return false;
}


try {
    // This is an example of the area function with pre & post conditionals

    int x;
    int y;
    for (; cin >> x >> y; )
        cout << "Area: " << area(x, y) << '\n';

}
catch (exception& e) {
    cerr << "error: " << e.what() << '\n';
    return 1;
}
catch (...) {
    cerr << "Oops: unknown exception!\n";
    return 2;
}

#include "../text_lib/std_lib_facilities.h"

int area(int length, int width)
// calculate area of a rectangle
// pre-conditions: length and width are positive
// post-condition: returns a positive value that is area
{
    if (length <= 0 || width <= 0) error("area() pre-condition");
    int a = length * width;
    if (a <= 0) error("area() post-condition");
    return a;
}

int main()


switch (msg->type) {
                case MS_MsgType_Close:
                    next = 1;
                    break;
                case MS_MsgType_Open:
                    break;
                default:
                    break;
                }

// @@@ START COPYRIGHT @@@
// @@@ END COPYRIGHT @@@

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "seabed/fserr.h"
#include "seabed/ms.h"
#include "seabed/pctl.h"
#include "seabed/pevents.h"

#include "tchkfe.h"
#include "tutil.h"

int main(int argc, char *argv[]) {
    bool    client = false;
    int     count;
    int     ferr;
    int     inx;
    int     len;
    int     lerr;
    char    my_name[BUFSIZ];
    int     next;
    char    recv_buffer[BUFSIZ];
    short   recv_buffer2[BUFSIZ];
    MS_SRE  sre;

    ferr = msg_init(&argc, &argv);
    TEST_CHK_FEOK(ferr);
    ferr = msg_mon_process_startup(!client); // system messages
    TEST_CHK_FEOK(ferr);
    util_gethostname(my_name, sizeof(my_name));
    msg_mon_enable_mon_messages(true);

    for (count = 0; count < 2; count++) {
        next = 0;
        for (inx = 0;; inx++) {
            do {
                lerr = XWAIT(LREQ, -1);
                TEST_CHK_WAITIGNORE(lerr);
                lerr = XMSG_LISTEN_((short *) &sre, // sre
                                    0,              // listenopts
                                    0);             // listenertag
            } while (lerr == XSRETYPE_NOWORK);
            ferr = XMSG_READCTRL_(sre.sre_msgId,  // msgid
                                  recv_buffer2,   // reqctrl
                                  1);             // bytecount
            util_check("XMSG_READCTRL_", ferr);
            ferr = XMSG_READDATA_(sre.sre_msgId,  // msgid
                                  recv_buffer,    // reqdata
                                  BUFSIZ);        // bytecount
            util_check("XMSG_READDATA_", ferr);
            if (sre.sre_flags & XSRE_MON) {
                MS_Mon_Msg *msg = (MS_Mon_Msg *) recv_buffer;
                printf("tse received mon message, type=%d\n",
                       msg->type);

                len = 0;
            } else {
                strcat(recv_buffer, "- reply from ");
                strcat(recv_buffer, my_name);
                len = (int) strlen(recv_buffer) + 1;
            }
            XMSG_REPLY_(sre.sre_msgId,       // msgid
                        recv_buffer2,        // replyctrl
                        sre.sre_reqCtrlSize, // replyctrlsize
                        recv_buffer,         // replydata
                        (ushort) len,        // replydatasize
                        0,                   // errorclass
                        NULL);               // newphandle
            if (next)
                break;
        }
    }
    printf("tse shutting down\n");
    ferr = msg_mon_process_shutdown();
    TEST_CHK_FEOK(ferr);
    return 0;
}


void memory_cleanse(void *ptr, size_t len)
{
    std::memset(ptr, 0, len);

    /* As best as we can tell, this is sufficient to break any optimisations that
       might try to eliminate "superfluous" memsets. If there's an easy way to
       detect memset_s, it would be better to use that. */
#if defined(_MSC_VER)
    SecureZeroMemory(ptr, len);
#else
    __asm__ __volatile__("" : : "r"(ptr) : "memory");
#endif
}

#include <support/cleanse.h>

#include <cstring>

#if defined(_MSC_VER)
#include <Windows.h> // For SecureZeroMemory.
#endif

/* Compilers have a bad habit of removing "superfluous" memset calls that
 * are trying to zero memory. For example, when memset()ing a buffer and
 * then free()ing it, the compiler might decide that the memset is
 * unobservable and thus can be removed.
 *
 * Previously we used OpenSSL which tried to stop this by a) implementing
 * memset in assembly on x86 and b) putting the function in its own file
 * for other platforms.
 *
 * This change removes those tricks in favour of using asm directives to
 * scare the compiler away. As best as our compiler folks can tell, this is
 * sufficient and will continue to be so.
 *
 * Adam Langley <example@nnthink.com>
 * Commit: ad1907fe73334d6c696c8539646c21b11178f20f
 */


switch (paramType) {
                case 1:
                    widget->setLayoutParameter(linearParameter);
                    break;
                case 2:
                    widget->setLayoutParameter(relativeParameter);
                default:
                    break;
            }

#include "TextFieldReader.h"
#include "../../../GUI/UIWidgets/TextField.h"

NS_CC_EXT_BEGIN

static TextFieldReader* instanceTextFieldReader = NULL;

IMPLEMENT_CLASS_WIDGET_READER_INFO(TextFieldReader)

TextFieldReader::TextFieldReader()
{

}

TextFieldReader::~TextFieldReader()
{

}

TextFieldReader* TextFieldReader::getInstance()
{
    if (!instanceTextFieldReader)
    {
        instanceTextFieldReader = new TextFieldReader();
    }
    return instanceTextFieldReader;
}

void TextFieldReader::setPropsFromJsonDictionary(ui::Widget *widget, const rapidjson::Value &options)
{
    WidgetReader::setPropsFromJsonDictionary(widget, options);

    ui::TextField* textField = (ui::TextField*)widget;

    textField->setPlaceHolder(DICTOOL->getStringValue_json(options, "placeHolder","inputs words here"));

    const char* text = DICTOOL->getStringValue_json(options, "text","Text Field");
    if(text && strlen(text) > 0 && text[0] == '@') {
        textField->setText(CCL(&text[1]));
    } else {
        textField->setText(text);
    }

    textField->setFontSize(DICTOOL->getIntValue_json(options, "fontSize",20));

    textField->setFontName(DICTOOL->getStringValue_json(options, "fontName","微软雅黑"));

    bool tsw = DICTOOL->checkObjectExist_json(options, "touchSizeWidth");
    bool tsh = DICTOOL->checkObjectExist_json(options, "touchSizeHeight");
    if (tsw && tsh)
    {
        textField->setTouchSize(CCSizeMake(DICTOOL->getFloatValue_json(options, "touchSizeWidth"), DICTOOL->getFloatValue_json(options,"touchSizeHeight")));
    }

    float dw = DICTOOL->getFloatValue_json(options, "width");
    float dh = DICTOOL->getFloatValue_json(options, "height");
    if (dw > 0.0f || dh > 0.0f)
    {
    }
    bool maxLengthEnable = DICTOOL->getBooleanValue_json(options, "maxLengthEnable");
    textField->setMaxLengthEnabled(maxLengthEnable);

    if (maxLengthEnable)
    {
        int maxLength = DICTOOL->getIntValue_json(options, "maxLength",10);
        textField->setMaxLength(maxLength);
    }
    bool passwordEnable = DICTOOL->getBooleanValue_json(options, "passwordEnable");
    textField->setPasswordEnabled(passwordEnable);
    if (passwordEnable)
    {
        textField->setPasswordStyleText(DICTOOL->getStringValue_json(options, "passwordStyleText","*"));
    }

    bool aw = DICTOOL->checkObjectExist_json(options, "areaWidth");
    bool ah = DICTOOL->checkObjectExist_json(options, "areaHeight");
    if (aw && ah)
    {
        CCSize size = CCSize(DICTOOL->getFloatValue_json(options, "areaWidth"),DICTOOL->getFloatValue_json(options,"areaHeight"));
        textField->setTextAreaSize(size);
    }
    bool ha = DICTOOL->checkObjectExist_json(options, "hAlignment");
    if (ha)
    {
        textField->setTextHorizontalAlignment((CCTextAlignment)DICTOOL->getIntValue_json(options, "hAlignment"));
    }
    bool va = DICTOOL->checkObjectExist_json(options, "vAlignment");
    if (va)
    {
        textField->setTextVerticalAlignment((CCVerticalTextAlignment)DICTOOL->getIntValue_json(options, "vAlignment"));
    }

    WidgetReader::setColorPropsFromJsonDictionary(widget, options);
}

void TextFieldReader::setPropsFromBinary(cocos2d::ui::Widget *widget, CocoLoader *pCocoLoader, stExpCocoNode *pCocoNode)
{
    this->beginSetBasicProperties(widget);

    ui::TextField* textField = static_cast<ui::TextField*>(widget);

    stExpCocoNode *stChildArray = pCocoNode->GetChildArray(pCocoLoader);

    for (int i = 0; i < pCocoNode->GetChildNum(); ++i) {
        std::string key = stChildArray[i].GetName(pCocoLoader);
        std::string value = stChildArray[i].GetValue(pCocoLoader);

        if (key == "ignoreSize") {
            widget->ignoreContentAdaptWithSize(valueToBool(value));
        }else if(key == "sizeType"){
            widget->setSizeType((ui::SizeType)valueToInt(value));
        }else if(key == "positionType"){
            widget->setPositionType((ui::PositionType)valueToInt(value));
        }else if(key == "sizePercentX"){
            _sizePercentX = valueToFloat(value);
        }else if(key == "sizePercentY"){
            _sizePercentY = valueToFloat(value);
        }else if(key == "positionPercentX"){
            _positionPercentX = valueToFloat(value);
        }else if(key == "positionPercentY"){
            _positionPercentY = valueToFloat(value);
        }
        else if(key == "adaptScreen"){
            _isAdaptScreen = valueToBool(value);
        }
        else if (key == "width"){
            _width = valueToFloat(value);
        }else if(key == "height"){
            _height = valueToFloat(value);
        }else if(key == "tag"){
            widget->setTag(valueToInt(value));
        }else if(key == "actiontag"){
            widget->setActionTag(valueToInt(value));
        }else if(key == "touchAble"){
            widget->setTouchEnabled(valueToBool(value));
        }else if(key == "name"){
            std::string widgetName = value.empty() ? "default" : value;
            widget->setName(widgetName.c_str());
        }else if(key == "x"){
            _position.x = valueToFloat(value);
        }else if(key == "y"){
            _position.y = valueToFloat(value);
        }else if(key == "scaleX"){
            widget->setScaleX(valueToFloat(value));
        }else if(key == "scaleY"){
            widget->setScaleY(valueToFloat(value));
        }else if(key == "rotation"){
            widget->setRotation(valueToFloat(value));
        }else if(key == "visible"){
            widget->setVisible(valueToBool(value));
        }else if(key == "ZOrder"){
            widget->setZOrder(valueToInt(value));
        }else if(key == "layoutParameter"){
            stExpCocoNode *layoutCocosNode = stChildArray[i].GetChildArray(pCocoLoader);

            ui::LinearLayoutParameter *linearParameter = ui::LinearLayoutParameter::create();
            ui::RelativeLayoutParameter *relativeParameter = ui::RelativeLayoutParameter::create();
            ui::Margin mg;

            int paramType = -1;
            for (int j = 0; j < stChildArray[i].GetChildNum(); ++j) {
                std::string innerKey = layoutCocosNode[j].GetName(pCocoLoader);
                std::string innerValue = layoutCocosNode[j].GetValue(pCocoLoader);

                if (innerKey == "type") {
                    paramType = valueToInt(innerValue);
                }else if(innerKey == "gravity"){
                    linearParameter->setGravity((cocos2d::ui::LinearGravity)valueToInt(innerValue));
                }else if(innerKey == "relativeName"){
                    relativeParameter->setRelativeName(innerValue.c_str());
                }else if(innerKey == "relativeToName"){
                    relativeParameter->setRelativeToWidgetName(innerValue.c_str());
                }else if(innerKey == "align"){
                    relativeParameter->setAlign((cocos2d::ui::RelativeAlign)valueToInt(innerValue));
                }else if(innerKey == "marginLeft"){
                    mg.left = valueToFloat(innerValue);
                }else if(innerKey == "marginTop"){
                    mg.top = valueToFloat(innerValue);
                }else if(innerKey == "marginRight"){
                    mg.right = valueToFloat(innerValue);
                }else if(innerKey == "marginDown"){
                    mg.bottom = valueToFloat(innerValue);
                }
            }

            linearParameter->setMargin(mg);
            relativeParameter->setMargin(mg);

        }

        else if (key == "opacity") {
            _opacity = valueToInt(value);
        }
        else if(key == "colorR"){
            _color.r = valueToInt(value);
        }else if(key == "colorG"){
            _color.g = valueToInt(value);
        }else if(key == "colorB")
        {
            _color.b = valueToInt(value);
        }
        else if(key == "flipX"){
            widget->setFlipX(valueToBool(value));
        }else if(key == "flipY"){
            widget->setFlipY(valueToBool(value));
        }else if(key == "anchorPointX"){
            _originalAnchorPoint.x = valueToFloat(value);
        }else if(key == "anchorPointY"){
            _originalAnchorPoint.y = valueToFloat(value);
        }
        else if(key == "placeHolder"){
            textField->setPlaceHolder(value);
        }else if(key == "text"){
            if(value.length() > 0 && value[0] == '@') {
                textField->setText(CCL(value.substr(1)));
            } else {
                textField->setText(value);
            }
        }else if(key == "fontSize"){
            textField->setFontSize(valueToInt(value));
        }else if(key == "fontName"){
            textField->setFontName(value);
        }else if(key == "touchSizeWidth"){
            textField->setTouchSize(CCSize(valueToFloat(value), textField->getTouchSize().height));
        }else if(key == "touchSizeHeight"){
            textField->setTouchSize(CCSize(textField->getTouchSize().width,  valueToFloat(value)));
        }else if (key == "maxLengthEnable"){
            textField->setMaxLengthEnabled(valueToBool(value));
        }else if(key == "maxLength"){
            textField->setMaxLength(valueToInt(value));
        }else if(key == "passwordEnable"){
            textField->setPasswordEnabled(valueToBool(value));
        }else if(key == "passwordStyleText"){
            textField->setPasswordStyleText(value.c_str());
        }
    } //end of for loop
    this->endSetBasicProperties(widget);
}

NS_CC_EXT_END