#ifndef BIND_MORE_CARD_HPP__
#define BIND_MORE_CARD_HPP__

#include <time.h>
#include <thread>
#include <chrono>
#include <functional>
#include <unordered_map>
#include <mutex>
#include <sstream>
#include <map>
#include <functional>
#include "card.h"
#include "zlist.h"
#include "cardMgr.h"
#include "log.h"

#define TIME_WIN_MILLISEC (15*60*1000)
#define TIME_LIM_SEC	  (3*1000)
struct MyHash
{
	typedef uint64_t result_type;
	result_type operator ()(uint64_t c1,uint64_t c2) const
	{
		std::string s;
		if(c1<c2)
		  s = std::to_string(c1) + std::to_string(c2);
		else 
		  s = std::to_string(c2) + std::to_string(c1);
		result_type st1 = std::hash<std::string>{}(s);
		return st1;
	}
};
struct Data
{
	Data(uint64_t ct)
	{
		reset();
		m_timestamp = ct;
	}
	void reset()
	{
		m_index = 0;
		m_ct.fill(0);
		m_totaldistance = 0;
		m_flag = false;
	}
	void increase(const int index)
	{
		m_ct[index]++;
	}
	uint32_t m_index;
	std::array<uint16_t,2> m_ct;
	double   m_totaldistance;
	uint64_t m_timestamp;
	bool m_flag;
};

struct TcardInterface
{
	// TcardInterface()=default;
	TcardInterface(std::pair<uint64_t,uint64_t> sp,uint64_t ct,uint64_t owner,int size)
		:m_cardid(sp)
		 ,m_timestamp(ct)
		 ,m_owner(owner)
		 ,SIZE(size)
	{}

	std::tuple<bool,std::string> setindex(uint64_t tsp,const uint64_t cid,const double d=0,bool ctflag =false)
	{
		bool flag = false;
		assert(m_timestamp != 0||tsp != 0);
		std::tuple<bool,std::string> stp(flag,"");

		if (empty())
		  grow(tsp);
		uint64_t tmp_tsp_min = back(0).m_timestamp;
		uint64_t tmp_tsp_max = back(0).m_timestamp + TIME_WIN_MILLISEC;
		//assert(tsp >= tmp_tsp_min);

		do
		{
			if(tsp>=tmp_tsp_min && tsp < tmp_tsp_max)
			{
				if(!ctflag)
				{
					back(0).m_index++;
					back(0).m_totaldistance+=d;
				}
				else
				{
					int arrindex = getstrindex(cid);
					if(arrindex == -1)
					{
						log_error("bindmorecard error.not match the cardid");
						return stp;
					}
					back(0).increase(arrindex);
				}
				break;
			}
			else
			{
				if(timeout())
				{
					auto tp = get();
					stp = check(std::get<0>(tp),std::get<1>(tp));
					skip(1);
				}
				grow(tsp);
				tmp_tsp_min = back(0).m_timestamp;
				tmp_tsp_max = back(0).m_timestamp + TIME_WIN_MILLISEC;
			}

		}while(1);
		return stp;
	}

	std::tuple<bool,std::string> checkLast()
	{
		//		std::tuple<bool,std::string> stp(false,std::string(""));
		//		if (!timeout() && size()>=ConfStruct::getCs().remote_slot)
		//		{
		//			auto tp = get();
		//			stp = check(std::get<0>(tp),std::get<1>(tp));
		//		}
		//		return stp;
		return std::make_tuple(false,"");
	}
	void skip(int count)
	{
		for(int i =0;i<count&&i<size();i++)
		  m_arr.pop_front();
	}
	bool empty()
	{
		return m_arr.empty();
	}
	int size() 
	{
		return m_arr.size();
	}
	void  grow(uint64_t ct)
	{
		Data d(ct);
		d.reset();
		m_arr.push_back(d);
	}
	Data &back(int index)
	{
		assert(index<(int)size() && index >= 0);
		return m_arr.at(size()-index-1);
	}
	std::tuple<int,int,double> get()
	{
		int total_ix=0,total_ct=0;
		double dis = 0;
		std::for_each(m_arr.begin(),m_arr.end(),[&total_ix,&total_ct,&dis](const Data &d){total_ix += d.m_index;total_ct+=(d.m_ct[0]+d.m_ct[1]);dis+=d.m_totaldistance;});
		return std::make_tuple(total_ix,total_ct,dis);
	}
	bool timeout()
	{
		return size() == SIZE-1;
	}
	std::string getInfo_1()
	{
		std::stringstream ss;
		ss<<m_cardid.first<<"&"<<m_cardid.second<<","<<m_arr[0].m_timestamp/1000<<","<<(back(0).m_timestamp+TIME_WIN_MILLISEC)/1000 <<",";
		std::for_each(m_arr.begin(),m_arr.end(),[&ss](Data& d){
					if (!d.m_flag)
					{
					ss<<d.m_timestamp/1000<<"&"<<d.m_totaldistance<<"&"<<d.m_index<<",";
					d.m_flag = true;
					}

					});
		return ss.str();
	}
	std::string getInfo()
	{
		auto tp = get();
		std::stringstream ss;
		ss<<"{T:"<<std::get<0>(tp)<<","<<std::get<1>(tp)<<","<<std::get<2>(tp)<<"}";
		//ss<<"{Total index:"<<std::get<0>(tp)<<","<<std::get<1>(tp)<<"},";
		//m_arr.for_each([&ss](const Data&x){
		//	ss<<"["<<x.m_index<<","<<x.m_ct[0]+x.m_ct[1]<<"]";
		//});
		return std::move(ss.str());
	}
	inline std::tuple<bool,std::string> check(int index,int ct)
	{
		if(index*1.0/ct>=0.72)
		{
			std::string s = getInfo_1();
			return std::make_tuple(true,s);
		}
		return std::make_tuple(false,std::string(""));
	}
	inline int getstrindex(const uint64_t cardid)
	{
		if(m_cardid.first == cardid)
		  return 0;
		else
		  return 1;
		return -1;
	}
	public:
	std::pair<uint64_t,uint64_t> m_cardid;
	uint64_t m_timestamp;
	uint64_t m_owner;
	std::deque<Data> m_arr;
	int SIZE;
	virtual ~TcardInterface(){} 
};

struct CardFactory
{
	CardFactory(cardMgr*owner)
		:m_owner(owner)
	{}
	std::map<uint64_t,std::string> setCT(uint64_t cid)
	{
		std::map<uint64_t,std::string> tmpvec;
		auto opcard=card_list::instance()->get(cid);
		uint64_t cttime = opcard->time_();
		auto vec = m_owner->getcard(cid);
		for(const auto cardid:vec)
		{
			
			uint64_t id = MyHash{}(cardid,cid);
			auto iter = m_map.find(id);
			if(iter != m_map.end())
			{
				std::shared_ptr<TcardInterface> ptr = iter->second;
				auto sp = ptr->setindex(cttime,cid,0,true);
				if (std::get<0>(sp))
				{
					tmpvec.insert(std::make_pair(id,std::get<1>(sp)));
				}

			}
		}
		return std::move(tmpvec);
	}


	std::map<uint64_t,std::string> handlecard(const std::vector<uint64_t>& vec,uint64_t cid)
	{
		std::map<uint64_t,std::string> tempvec;
		auto opcard = card_list::instance()->get(cid);
		uint64_t cttime = opcard->time_();
		std::shared_ptr<TcardInterface> ptr = nullptr;
		for(const auto cardid:vec)
		{
			uint64_t id = MyHash{}(cardid,cid);
			auto iter = m_map.find(id);
			if(iter != m_map.end())
			  ptr = iter->second;
			else
			{
				log_info("handlecard..%lld,%lld,%lld",id,cardid,cid);
				ptr = make_shared_(cardid,cid,cttime,id);
				m_map.insert({id,ptr});
				backup(cid,cardid);
			}
			auto npcard=card_list::instance()->get(cardid);
			double dis = opcard->dist(*npcard);
			auto sp=ptr->setindex(cttime,cid,dis);
			if (std::get<0>(sp))
			{
				tempvec.insert(std::make_pair(id,std::get<1>(sp)));
			}
		}
		return std::move(tempvec);
	}
	void erase(uint64_t id)
	{
		auto it = m_map.find(id);
		if(it != m_map.end())
		{
			std::pair<uint64_t,uint64_t> p = it->second->m_cardid;
			m_owner->remove_edge(p.first,p.second);
			reset(id);
			m_map.erase(it);
		}
	}
	inline std::string InfoMessage()
	{
		std::stringstream ss;
		ss<<"S: "<<m_map.size();
		for(auto it : m_map)
		{
			//ss<< " hash info["<<it.first <<"]first cardid:["<<it.second->m_cardid.first<<"]second cardid:["<<it.second->m_cardid.second<<"]Info:{total_size:"<<it.second->size()<<","<<it.second->getInfo()<<"}";
			ss<< "["<<(uint32_t)(it.second->m_cardid.first)<<"]["<<(uint32_t)(it.second->m_cardid.second)<<"]{s:"<<it.second->size()<<","<<it.second->getInfo()<<"}";
		}
		return std::move(ss.str());

	}
	virtual std::map<uint64_t,std::string> selectcard(std::vector<uint64_t> &v,uint64_t cid) = 0;
	virtual std::shared_ptr<TcardInterface> make_shared_(const uint64_t cid1,const uint64_t cid2,const uint64_t ctime,uint64_t key) = 0;
	virtual void backup (uint64_t cid1,uint64_t cid2){}
	virtual void reset (uint64_t cid){}
	virtual std::tuple<bool,std::string> checkLast(std::shared_ptr<TcardInterface> &ti)=0;
	virtual ~CardFactory(){}
	public:
	std::unordered_map<uint64_t,std::shared_ptr<TcardInterface>>     m_map;
	cardMgr * m_owner;
};
struct CloserCardFactory : CardFactory
{
	CloserCardFactory(cardMgr*);
	int m_closer_slot=0;
	std::map<uint64_t,int>		 m_count;
	bool getAccess(uint64_t cid1,uint64_t cid2)
	{
		uint64_t cid = MyHash{}(cid1,cid2);
		if(m_count[cid] < 600)
		{
			m_count[cid]++;
			return false;
		}
		else
		  return true;
	}
	void reset(uint64_t id)
	{
		auto it = m_count.find(id);
		if(it != m_count.end())
		  m_count.erase(it);
	}
	virtual std::map<uint64_t,std::string> selectcard(std::vector<uint64_t> &v,uint64_t cid)
	{
		std::map<uint64_t,std::string> vec;
		if(v.empty()) return vec;
		std::vector<uint64_t> rc(v.begin(),v.end());
		auto opcard=card_list::instance()->get(cid);
		rc.erase(std::remove_if(rc.begin(),rc.end(),[&](uint64_t cardid){
						if(!getAccess(cid,cardid))
						return true;
						if(cardid == cid)
						return true;
						auto npcard = card_list::instance()->get(cardid);
						if(npcard->type_()!=opcard->type_())
						return true;
						uint64_t ct1 = npcard->time_();
						uint64_t ct2 = opcard->time_();
						uint64_t ct3 = ct1>ct2?ct1-ct2:ct2-ct1;
						return ct3 > TIME_LIM_SEC; 
						}),rc.end());

		vec = handlecard(rc,cid);
		return std::move(vec);
	}
	virtual void backup (uint64_t cid1,uint64_t cid2)
	{
		m_owner->addVertex(cid1,cid2);
	}
	virtual std::shared_ptr<TcardInterface> make_shared_(const uint64_t cid1,const uint64_t cid2,const uint64_t ctime,uint64_t key)
	{
		return std::make_shared<TcardInterface>(std::make_pair(cid1,cid2),ctime,key,m_closer_slot);
	}
	std::tuple<bool,std::string> checkLast(std::shared_ptr<TcardInterface> &ti)
	{
		return ti->checkLast();
	}
};

struct RemoteCardFactory : CardFactory
{
	RemoteCardFactory(cardMgr*);
	int m_remote_slot=0;
	virtual std::map<uint64_t,std::string> selectcard(std::vector<uint64_t> &ov,uint64_t cid)
	{
		auto vcard=m_owner->getcard(cid);
		log_info("%d....remote%d",vcard.size(),(vcard.empty()?0:vcard[0]));
		std::sort(vcard.begin(),vcard.end());
		std::sort(ov.begin(),ov.end());
		std::vector<uint64_t> v(ov.size()+vcard.size());

		auto it = std::set_difference(vcard.begin(),vcard.end(),ov.begin(),ov.end(),v.begin());
		v.resize(it-v.begin());
		auto opcard = card_list::instance()->get(cid);
		v.erase(std::remove_if(v.begin(),v.end(),[&](uint64_t cardid){
						auto npcard = card_list::instance()->get(cardid);
						uint64_t ct1 = opcard->time_();
						uint64_t ct2 = npcard->time_();
						uint64_t ct3 = ct1>ct2?ct1-ct2:ct2-ct1;
						return ct3 > TIME_LIM_SEC; 
						}),v.end());

		auto vec = handlecard(v,cid);
		return std::move(vec);
	}
	virtual std::shared_ptr<TcardInterface> make_shared_(const uint64_t cid1,const uint64_t cid2,const uint64_t ctime,uint64_t key)
	{
		return std::make_shared<TcardInterface>(std::make_pair(cid1,cid2),ctime,key,m_remote_slot);
	}
	std::tuple<bool,std::string> checkLast(std::shared_ptr<TcardInterface> &ti)
	{
		return std::make_tuple(false,std::string(""));
	}
};
#endif
