#ifndef __INCLUDE_HIS_LOCATION_HPP
#define __INCLUDE_HIS_LOCATION_HPP
#include <complex>
#include <queue>
#include "point.h"
#include "log.h"
#include "tool_time.h"
#include "card_path.h"
#include "db_api/CDBSingletonDefine.h"
//速度
//区域 地图变换
//运动方向
//路径变换
struct location_card
{
	location_card(uint32_t id,uint64_t type)
		:m_cardid(id)
		 ,m_type(type)
	{
		init();
	}
	uint32_t    m_cardid;//卡id
	uint16_t	m_type;//卡类型
	double		m_arg;//运动方向角度值
	int 		m_areaid;//区域
	int 		m_mapid;//地图
	uint64_t	m_timestamp;//入库后的时间
	point		m_p;//入库后的点
	struct mini_data
	{
		mini_data(const point &p,uint64_t t)
			:p(p)
			 ,time(t)
		{}
		point p;
		uint64_t time;
	};
	std::queue<mini_data> m_d;
	void init()
	{
		m_areaid=-1;
		m_mapid=-1;
		m_timestamp=0;
		m_p.set(0,0);
		init_att();
	}
	void init_att()
	{
		m_arg=0x12345678;
		std::queue<mini_data> tmp;
		m_d.swap(tmp);
	}
	void set_invalid()
	{
		m_arg=0x12345678;
	}
	bool is_valid()
	{
		return m_arg!=0x12345678;
	}
	double make_arg(const point &pt,const point &p)
	{
		log_info("his_location arg[%d],(%.2f,%.2f)--->(%.2f,%.2f)---->(%.2f,%.2f)",m_cardid,m_p.x,m_p.y,p.x,p.y,pt.x,pt.y);
		return  std::arg(std::complex<double>(pt.x,pt.y)-std::complex<double>(p.x,p.y));
	}
	void set(const point &pt,uint64_t time)
	{
		m_timestamp=time;
		m_p.set(pt);
	}
	bool line_changed(const point &pt)
	{
		if(!is_valid())
		  return false;
		point p=m_d.back().p;
		//if(p==pt)return false;
		if(point::eq(p.x,pt.x,0.05) && point::eq(p.y,pt.y,0.05)) return false;
		double arg = make_arg(pt,p);
		log_info("his_location:line_changed:%d,%f,m_arg:%f,%s,%f",m_cardid,arg,m_arg, (arg-m_arg > -1e-10 && arg-m_arg<1e-10)?"same":"not same",m_arg-arg);
		return !point::eq(arg,m_arg,1e-10);
	}
	bool is_speed_changed(const point& pt,uint64_t time)
	{
		bool flag = false;
		double v=0.0;
		if(m_d.size()>=4)
		{
			mini_data d1 = m_d.front();
			double dist = d1.p.dist(pt);
			double t = time-d1.time;
			v = dist/t*1000;
			m_d.pop();
		}
		m_d.emplace(pt,time);
		if(v==0.0)
		  return flag;
		double dist = m_p.dist(pt);
		double t	= time - m_timestamp;
		double avge_speed= dist/t*1000;
		log_info("his_location cardid:%d:v:%.2f,avge_v:%.2f,(%.2f--%.2f)",m_cardid,v,avge_speed,0.8*avge_speed,1.2*avge_speed);
		if(v<(1-0.2)*avge_speed && v>(1+0.2)*avge_speed)
		  flag=true;
		  
		return flag;
	}
	bool time_out(const point &p,uint64_t time)
	{
		if(time-m_timestamp>=60*1000 && !(p == m_p))
			return true;
		return false;
	}
	bool is_area_changed(int new_areaid)
	{
		bool flag =false;
		if(m_areaid != new_areaid)
		{
			m_areaid = new_areaid;
			flag=true;
		}
		return flag;
	}
	bool is_map_changed(int new_mapid)	
	{
		bool flag =false;
		if(m_mapid != new_mapid)
		{
			m_mapid = new_mapid;
			flag=true;
		}
		return flag;
	}
	void push(uint64_t timestamp,const point & p,int32_t areaid,int32_t mapid)
	{
		if(m_p.empty() || m_timestamp==0||m_areaid<=0||m_mapid<=0)	
		{
		  set(p,timestamp);
		  m_areaid=areaid;m_mapid=mapid;
		  m_d.emplace(p,timestamp);
		  return;
		}
		
		if(!is_valid())
		{
		  if(p==m_p)
		  {
			  init_att();set(p,timestamp);
			  m_d.emplace(p,timestamp);
			  return ;
		  }
		  m_arg=make_arg(p,m_p);
		  log_info("his_location:%d arg:%f",m_cardid,m_arg);
		  insert();
		}
		bool flag=false;
		bool iflag=false;
		flag=handle_message(p,timestamp);
		if(time_out(p,timestamp))
			if(!flag)iflag=true;
		if(is_speed_changed(p,timestamp))
			if(!flag)iflag=true;
		if(is_area_changed(areaid))
			if(!flag)iflag=true;
		if(is_map_changed(mapid))
			if(!flag)iflag=true;
		if(iflag)
		{
			update(p,timestamp);
			//set_invalid();	
			set(p,timestamp);
			insert();
		}
	}
	virtual std::string getTabName()=0;
	void insert()
	{
		std::string tabName=getTabName();
		char nsql[512]={0};
		const char * sql = "replace into %s (card_id,begin_time,map_id,area_id,begin_pt,direction)values(%03d%010d,'%s',%d,%d,'%.2f,%.2f',%f);";
		snprintf(nsql,512,sql,tabName.c_str(),m_type,m_cardid,tool_time::to_str_ex(m_timestamp).c_str(),m_mapid,m_areaid,m_p.x,m_p.y,m_arg);
		log_info("his_location[%d]:%s",m_cardid,nsql);
		sDBConnPool.PushAsync(nsql);
	}
	void update(const point &p,uint64_t timestamp)
	{
		std::string tabName=getTabName();
		char nsql[512]={0};
		const char * sql = "update %s set last_time='%s',speed=%.2f where card_id=%03d%010d and begin_time='%s';";
		double dist = m_p.dist(p);
		double t	= timestamp - m_timestamp;
		double avge_speed= dist/t*1000;
		if(std::isnan(avge_speed)|| std::isinf(avge_speed))avge_speed=0;
		snprintf(nsql,512,sql,tabName.c_str(),tool_time::to_str_ex(timestamp).c_str(),avge_speed,m_type,m_cardid,tool_time::to_str_ex(m_timestamp).c_str());
		log_info("his_location[%d]:%s",m_cardid,nsql);
		sDBConnPool.PushAsync(nsql);
	}

	virtual bool  handle_message(const point &p,uint64_t timestamp)=0;
};
struct location_staff:location_card
{
	location_staff(uint32_t id,uint16_t type)
		:location_card(id,type)
	{}
	virtual std::string getTabName(){return std::string{"his_location_staff_"};}
	//人员如果运行方向发生变化的话，需要求的拐点。如果有拐点，入拐点。时间大与120s则不做处理。没有拐点，则不求
	virtual bool  handle_message(const point &p,uint64_t timestamp)
	{
		bool flag = false;
		if(line_changed(p))
		{
			log_info("his_location:line_changed %d",m_cardid);
			flag=true;
			if(timestamp-m_timestamp>120*1000)
			{
				log_info("his_location:line_changed_timeout %d",m_cardid);
				update(m_d.back().p,m_d.back().time);
				set(p,timestamp);
			}
			else
			{
				log_info("his_location:line_changed_find_path %d,(%.2f,%.2f)-->(%.2f,%.2f)-->(%.2f,%.2f)",m_cardid,m_p.x,m_p.y,m_d.back().p.x,m_d.back().p.y,p.x,p.y);
				std::vector<point> rc=card_path::inst().find_path(point(m_d.back().p.x,-m_d.back().p.y),point(p.x,-p.y));
				if(rc.empty())				
				{
					log_info("his_location:line_changed rc.empty() %d",m_cardid);
					update(m_d.back().p,m_d.back().time);
					set(m_d.back().p,m_d.back().time);
				}
				else
				{
					for(uint32_t i=0;i<rc.size();i++)
						log_info("his_location:find_path..x=%.3lf,y=%.3lf\n",rc[i].x,rc[i].y);
					if(rc.size()==1)
					{
						log_info("his_location:line_changed %d circle point(%.2f,%.2f)",m_cardid,rc[0].x,rc[0].y);
						point tmp(rc[0].x,-rc[0].y);
						update(tmp,m_d.back().time);
						set(tmp,m_d.back().time);
					}
					else if(rc.size()==2)
					{
						point tmp(rc[0].x,-rc[0].y);
						update(tmp,m_d.back().time);
						set(tmp,m_d.back().time);
						//
						tmp.set(rc[1].x,-rc[1].y);
						m_arg=make_arg(tmp,m_p);
						insert();
						//
						update(tmp,timestamp);
						set(tmp,timestamp);
					}
					else//更多的拐点
					{
						point tmp(rc[0].x,-rc[0].y);
						update(tmp,m_d.back().time);
						tmp.set(rc.back().x,-rc.back().y);
						set(tmp,timestamp);
					}
				}
			}
			init_att();
		}
		return flag;
	}
};
struct location_vehicle:location_card
{
	location_vehicle(uint32_t id,uint16_t type)
		:location_card(id,type)
	{}
	virtual std::string getTabName(){return std::string{"his_location_vehicle_"};}
	//车辆是有推送拐点的，所以不需要求拐点。如果运行方向发生变化的话，直接更新数据库即可，使用上一个点的时间。同时生成这一次的历史。
	virtual bool  handle_message(const point &p,uint64_t timestamp)
	{
		bool flag =false;
		if(line_changed(p))
		{
			log_info("his_location:line_changed %d ",m_cardid);
			flag=true;
			update(m_d.back().p,m_d.back().time);
			set(m_d.back().p,m_d.back().time);init_att();
		}
		return flag;
	}
};
#endif
