
/*=============================================================================================
	author:ZhaoJun
	version:1.2
	email:2732810281@qq.com
=============================================================================================*/


//严格模式
/*-------------------------------------------------------------------------------------------*/	
'use strict';
/*-------------------------------------------------------------------------------------------*/

let fs=require('fs')

// 复用操作
/*-------------------------------------------------------------------------------------------*/	
	class doc
	{
		static async hotBack(df,tag)
		{
			if(!tag)return
			if($Config.loadApiAble && tag && df.includes('/nodejs/control/'))
			{
				if($Config.loadApiAble && $ExtendPool.worker.able)
				{
					let obj=await $Sys.model('/fun/worker','parse',[{path:'/tool/config/doc',method:'compile',data:[df,undefined,true]}])
					try
					{
						await $Sys.model('/fun/worker','parse',[{path:'/tool/config/doc',method:'writeFile',data:[obj]}])
					}
					catch(error)
					{
						if($Config.loadNotice)console.error('[自动生成文档]',error)
					}
				}
			}
		}

		static parse(str,obj={},df)
		{ 
			var _apiList={};
			var reg=/(\/\/\-{10,100})([\S|\s]*?)\1/g
			var regn=/\<(.*?)\>(.*?)(?=\<.*?\>)/g
			var resu;
			var resun;
			var tag;
			var tag_sub;
			var tag_cur;
			while (resu=reg.exec(str)) 
			{
				_apiList={}
				resu[2]=resu[2].replace(/\s|\/{2}/g,'');
				tag=0;
		
				while(resun=regn.exec(resu[2]))
				{
					if(tag==0 && resun[1]=='分组' && resun[2])
					{
						tag_sub=resun[2].split('-');
						for (const iteam in tag_sub) 
						{
							if(iteam==0)
							{
								if(!obj[tag_sub[0]])
								{
									obj[tag_sub[0]]={};
								}
								tag_cur=obj[tag_sub[0]];
							}
							if(iteam==1)
							{
								if(!obj[tag_sub[0]][tag_sub[1]])
								{
									obj[tag_sub[0]][tag_sub[1]]={};
								}
								tag_cur=obj[tag_sub[0]][tag_sub[1]];
							}
							if(iteam==2)
							{
								if(!obj[tag_sub[0]][tag_sub[1]][tag_sub[2]])
								{
									obj[tag_sub[0]][tag_sub[1]][tag_sub[2]]={};
								}
								tag_cur=obj[tag_sub[0]][tag_sub[1]][tag_sub[2]];
							}
						}
					}
					else if(tag==0)
					{
						obj['其他']=obj['其他']?obj['其他']:{};
						tag_cur=obj['其他'];
					}
					if(resun[1]=='请求参数')
					{
						_apiList[resun[1]]=(_apiList[resun[1]]?_apiList[resun[1]]:'')+resun[2]+'&';
					}
					else
					{
						_apiList[resun[1]]=(_apiList[resun[1]]?_apiList[resun[1]]:'')+resun[2];
					}
					tag++;
				}
				tag_cur.$list=tag_cur.$list?tag_cur.$list:[];
				try {_apiList['请求参数']=require('querystring').parse(_apiList['请求参数']);} catch (error) {}
				if(_apiList['返回参数'])
				{
					try 
					{
						_apiList['返回参数']=JSON.parse(_apiList['返回参数']);
					} 
					catch (error) 
					{
						console.warn("[api文档生成-${_apiList['返回参数']}]",`返回参数不是json格式\n${df}\r\n`);
					}
				}

				let tagIndex
				for (let index = 0; index < tag_cur.$list.length; index++) 
				{
					if(tag_cur.$list[index]['请求地址']==_apiList['请求地址'])
					{
						tagIndex=true
						tag_cur.$list[index]=_apiList
					}
				}
				if(!tagIndex)tag_cur.$list.push(_apiList)
			}
		}


		static parseParam(curRequest,curObjPri)
		{
			if(curRequest && curRequest instanceof Object)
			{
				let obj={}
				for (const key in curRequest) 
				{
					if(curRequest[key]['hide'])continue
					obj[key]={...curRequest[key]}
					if(curRequest[key].value)curRequest[key].value=String(obj[key].value)
					obj[key]['descript']=obj[key]['descript']?(obj[key]['descript']):'暂未定义'
					obj[key]['descript']+=obj[key]['delete']?('-可不传'):''
					obj[key]['descript']+=obj[key]['reg']?(',正则验证'+obj[key]['reg']):''
					obj[key]['descript']+=obj[key]['len']?(',固定长度'+obj[key]['len']+'个字符'):''
					obj[key]['descript']+=obj[key]['minLen']?(',最短'+obj[key]['minLen']+'个字符'):''
					obj[key]['descript']+=obj[key]['maxLen']?(',最长'+obj[key]['maxLen']+'个字符'):''
					obj[key]['descript']+=obj[key]['number']?(',需要为数字'):''
					obj[key]['descript']+=(obj[key]['min']!==undefined)?(',最小'+obj[key]['min']):''
					obj[key]['descript']+=obj[key]['max']?(',最大'+obj[key]['max']):''
					obj[key]['descript']=obj[key]['descript']

					if(obj[key]['fun'])Reflect.deleteProperty(obj[key],'fun')
				}
				if(curObjPri)curObjPri['请求参数']=obj
			}
			return curRequest
		}

		static parseNew(objBack={},obj,df,tag)
		{
			try 
			{
				if(tag)
				{
					var funsource=require(df)
				} 
				else
				{
					var funsource=require(df)
				}
				var fun=new funsource
			} 
			catch (error) 
			{
				console.error(`[自动生成文档]发生一个错误,可能正在缓存请等待${df}`,error)
				return	
			}
			
			function hand(dataStore)
			{
				let handler = 
				{  
					get(target, key, proxy) 
					{
						return hand(()=>{return ''})
					} 
				}
				
				return new Proxy(dataStore, handler); 
			}
			
			for (const iterator of Object.getOwnPropertyNames(funsource.prototype)) 
			{
				if(iterator=='constructor')continue
				if(Object.prototype.toString.call(fun[iterator])=='[object AsyncFunction]')continue

				let req=hand({})
				let res=hand({})
				
				try{fun[iterator](req,res)}
				catch (error)
				{ 
					console.error(`[自动生成文档]发生一个错误${df}`,error.message?error.message:error.stack)
				}
				let funCur
				if(fun.$doc)funCur=fun.$doc
				else continue
				
				let split=funCur.base.group.split('-')
				let curObj=obj
				let curObjBack=objBack?objBack:{}

				let curObjBackList
				let curObjList=[]
				for (const iterator of split)
				{
					if(curObj && !curObj[iterator] )curObj[iterator]={$list:[]}
					else if(typeof curObj[iterator].$list == 'undefined')curObj[iterator].$list=[]
					curObjList=curObj[iterator].$list	
					curObj=curObj[iterator]
					if(!curObjBack[iterator])curObjBack[iterator]={$list:[]}
					curObjBackList=curObjBack[iterator].$list	
					curObjBack=curObjBack[iterator]
				}
				

				let curObjPri={}	
				curObjPri['分组']=funCur.base.group	
				curObjPri['消息备注']=funCur.base.descript
				curObjPri['请求方式']=funCur.base.requestMethod
				curObjPri['请求方法']=iterator
				if(funCur.auto)
				{
					if(!(funCur.auto instanceof Object))
					{
						funCur.auto={}
					}
					if(!funCur.auto.group)
					{
						funCur.auto.group=df.
						replace($Config.root+'/nodejs/control','')
						.replace('.class.js','')
					}
					funCur.auto.group='$'+funCur.auto.group
					curObjPri['自动测试']=funCur.auto
				}
				
				curObjPri['请求地址']=(()=>
				{
					if(!tag) return df.replace($Config.root+'/nodejs/control','').replace('.class.js','/')+iterator
					else return df.replace($Config.root+'/nodejs/control','').replace('.class.js','/')+iterator
				})()	
				let curRequest=funCur.requestParam	

				curRequest=doc.parseParam(curRequest,curObjPri)
				
				curObjPri['返回参数']=(()=>
				{
					if(funCur.backParam)return funCur.backParam
					else
					{
						for (let index = 0; index < curObjBackList.length; index++) 
						{
							if(curObjBackList[index]['消息备注']==curObjPri['消息备注'])
							{
								return curObjBackList[index]['返回参数']
							}
						}
						return '暂未定义'
					}
				})()
				
				if(fun.descriptParam)curObjPri['参数描述']=fun.descriptParam
				else 
				{
					for (let index = 0; index < curObjBackList.length; index++) 
					{
						if(curObjBackList[index]['消息备注']==funCur.base.descript)curObjPri['参数描述']=curObjBackList[index]['参数描述']
					}
				}
				
				let tagIndex
				try 
				{
					for (let index = 0; index < curObjList.length; index++) 
					{
						if(curObjList[index]['请求地址']==curObjPri['请求地址'])
						{
							tagIndex=true
							curObjList[index]=curObjPri
						}
					}
				} 
				catch (error){console.warn('[发生错误]',error)}

				if(!tagIndex)curObjList.push(curObjPri)

				fun.$doc=undefined

			}
			return obj;
		} 

		static parseFile()
		{
			let objBack
			let objBackStr
			let url=$Config.root+$Config.loadUrl
			let date=$Sys.model('/tool/time','format',['y_m_d@h_i_s'])
			let _url=url.replace('.js','解析失败'+date+'.js').replace(/[\+|-]/g,'')
			try 
			{
				objBackStr=fs.readFileSync(url).toString()
				//fs.writeFileSync(url+'.back',objBackStr)
				objBack=objBackStr.replace('var GlobalObj=','')
				objBack=JSON.parse(objBack)
				if(!(objBack instanceof Object))
				{
					fs.writeFileSync(_url,objBackStr)
					objBack={}
					console.warn('[api文档生成发生警告]','之前的文档不可用')            
				}
			} 
			catch (error) 
			{
				
				fs.writeFileSync(_url,objBackStr)
				objBack={}
				console.warn('[api文档生成发生警告]','之前的文档不可用')          
			} 
			return objBack
		} 

		static writeFile(obj)
		{
			if(Array.isArray(obj))obj=obj[0]
			if(obj instanceof Object)obj=JSON.stringify(obj,null,4)

			if(!obj) 
			{
				console.warn('[api文档生成发生警告]','文档生成失败，请重试，可能生成太频繁')
				return
			}

			fs.writeFileSync($Config.root+$Config.loadUrl,'var GlobalObj='+obj)
		}

		static apiBack(group,descript,param='',descriptParam,path,method,globalParam={})
		{
			if(!descript)descript=''
			let obj=doc.parseFile()
			let split=group.split('-')
			let curObjList={}
			let curObj=obj
			let tag
			if(!path)return
			try 
			{
				var funsource=require($Config.root+'/nodejs/control'+path+'.class.js')
				var fun=new funsource
				fun[method]()
				if(fun)fun=fun.$doc 
			} 
			catch (error)
			{
				console.warn("[api文档生成发生错误]",error);
				return
			}
			
			let globalParamRequest={}
			doc.parseParam(globalParam.requestParam,globalParamRequest)
			for (const iterator of split)
			{
				if(!curObj[iterator])curObj[iterator]={$list:[]}
				curObjList=curObj[iterator].$list	
				curObj=curObj[iterator]
			}

			for (let index = 0; index < curObjList.length; index++) 
			{ 
				if(curObjList[index]['消息备注']==descript)
				{
					curObjList[index]['返回参数']=param
					if(fun && fun.$doc && fun.$doc.descriptParam)curObjList[index]['参数描述']=fun.descriptParam
					else if(descriptParam) curObjList[index]['参数描述']=descriptParam
					curObjList[index]['请求参数']=globalParamRequest['请求参数']
					curObjList[index]['请求方式']==globalParam.base?globalParam.base:globalParam.base.requestMethod
					tag=true
				}
			}
			
			let objOver={"消息备注":descript,"返回参数":param}
			if(fun && fun.$doc && fun.$doc.descriptParam)objOver['参数描述']=fun.descriptParam
			else if(descriptParam) objOver['参数描述']=descriptParam
			objOver['请求参数']==globalParamRequest['请求参数']
			objOver['请求方式']==globalParam.base?globalParam.base:globalParam.base.requestMethod
			if(!tag)curObjList.push(objOver)

			doc.writeFile(obj)
		}

		static compile(df,obj,tag)
		{
			let dateTime=Date.now()
			if((dateTime - $Config.loadApi.next)<$Config.loadApi.time)
			{
				$Config.loadApi.next=dateTime
				return
			}
			$Config.loadApi.next=dateTime

			let objBack=doc.parseFile()
			if(!obj)obj=objBack
			var str 
			df=df.replace(/\\/g,'/');	
			function check(df)
			{ 
				var statInfo = fs.statSync(df);
				if(statInfo)
				{
					if(statInfo.isDirectory())
					{
						var st1=fs.readdirSync(df);
						if(st1)
						{
							for (let x of st1) 
							{
								x= df.concat('/').concat(x);
								check(x);
							}
						}
					}
					else if(statInfo.isFile() && df.indexOf('.js')!=-1)
					{
						console.info('[文档生成]','当前文件'+df)
						if(tag)$Load.clearn(df)
						str=fs.readFileSync(df,{encoding:'utf8'})
						doc.parse(str,obj,df)
						doc.parseNew(objBack,obj,df,tag)
					}
				}
			}
			check(df)
			return obj;
		}
  }
/*-------------------------------------------------------------------------------------------*/    
module.exports=doc;