#include <memory>
#include <write-copy.h>
#include "db_api/CDBSingletonDefine.h"
#include "log.h"

#include <area.h>
#include "point.h"
#include "monkey_car/monkeycar_area.h"
#include "landmark.h"
#include "area_business.h"

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

#include"module_service/module_area.h"
#include "area_business.h"

template<> std::shared_ptr<area_list> 
single_base<area_list, int, std::shared_ptr<area>>::m_instance=std::make_shared<area_list>();

struct underground_area:area
{
    underground_area(int id,int limit_count_person, int limit_time_person,double scale,int32_t mapid,int32_t type)
        :area(id,limit_count_person,limit_time_person,scale,mapid,type)
	{
	}
    virtual bool in_area(const std::shared_ptr<site>&s,const std::shared_ptr<card_location_base>&c, const point & p,int &sarid)
	{
		bool f =false;
		int id = c->get_area();
		if(id!=-1)
		  f=true;
		return f;
	}
};

struct special_area:area
{
    special_area(int id,int limit_count_person, int limit_time_person,double scale,int32_t mapid,int32_t type)
        :area(id,limit_count_person,limit_time_person,scale,mapid,type)
	{}
    virtual bool in_area(const std::shared_ptr<site>&s,const std::shared_ptr<card_location_base>&c, const point & p,int &sarid)
	{
		bool f=false;
		int id = c->get_area();
		if(id == m_id)
		{
		  f=true;
		  sarid=m_id;
		}
		return f;
	}
};

area::area(int id,int limit_count_person, int limit_time_person,double scale,int32_t mapid,int32_t type)
     :m_id(id)
     ,m_area_type(type)
     ,m_limit_person_second(limit_time_person)
     ,m_limit_person_count(limit_count_person)
     ,m_scale(scale)
     ,m_mapid(mapid)
     ,m_person_count(0)
     ,m_vehicle_count(0)
    {
		m_area_business_list=area_business::get_instance_list(m_area_type);
    }


void area::on_hover(const std::shared_ptr<area_hover>&a,const std::shared_ptr<card_location_base>&c)
{
 	for(const auto &i:m_area_business_list)
	{
		i->on_hover(a,c,a->get_business_data(i->area_business_type()));
	}
}

void area::on_enter(const std::shared_ptr<area_hover>&a,const std::shared_ptr<card_location_base>&c)
{
	log_info("on_enter..%d  areaId:%d",c->m_id,m_id);
 	for(const auto &i:m_area_business_list)
	{
		i->on_enter(a,c,a->get_business_data(i->area_business_type()));
	}
}

void area::on_leave(const std::shared_ptr<area_hover>&a,const std::shared_ptr<card_location_base>&c)
{
	log_info("on_leave..%d  areaId:%d",c->m_id,m_id);
 	for(const auto &i:m_area_business_list)
	{
		i->on_leave(a,c,a->get_business_data(i->area_business_type()));
	}
}

bool area::in_area(const std::shared_ptr<site>&s,const std::shared_ptr<card_location_base>&c, const point & p,int & sarid)
{
	if(m_bound.empty())
	  return false;
	int counter = 0;
	double xinters;
	point p1,p2;

	p1 = m_bound[0];
	int size = m_bound.size();
	for (int i=1;i<= size;i++) {
		p2 = m_bound[i%size];
		if (p.y > std::min(p1.y,p2.y)) {
			if (p.y <= std::max(p1.y,p2.y)) {
				if (p.x <= std::max(p1.x,p2.x)) {
					if (p1.y != p2.y) {
						xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;
						if (p1.x == p2.x || p.x <= xinters)
						  counter++;
					}
				}
			}
		}
		p1 = p2;
	}
	return (counter % 2 == 0) ? false : true;
}

area_list::area_list()
{
}
void area_list::init_monkeycar_area(int id)
{
    std::string sql = "SELECT a.area_id, a.name, a.map_id, a.area_type_id, a.path, c.scale,\
            over_count_person, over_count_vehicle, over_time_person, over_time_vehicle,\
            over_speed_vehicle, is_attendance ,b.monkeycar_coordinate,b.monkeycar_speed,a.business_type \
            FROM dat_area a ,dat_monkeycar_base_info b ,dat_map c\
            where a.area_id = b.monkeycar_base_info_id and a.map_id = c.map_id";

    if(-1 == id)
    {
        sql.append(";");
    }
    else
    {
        sql.append(" AND a.area_id=");
        sql.append(std::to_string(id));
        sql.append(";");
        log_info("基础数据 monkeycar area 增加或修改区域 sql=%s", sql.c_str());
    }

    std::string Error;
    YADB::CDBResultSet DBRes;
    sDBConnPool.Query(sql.c_str(),DBRes,Error);
    int nCount = DBRes.GetRecordCount( Error );
    if (nCount < 1)
    {
        log_error("基础数据 monkeycar area 增加或修改失败，数据库中找不到: area_id=%d", id);
        return ;
    }

    std::unordered_map<int,std::shared_ptr<area>> map;
    while ( DBRes.GetNextRecod(Error) )
    {
        int area_id  = 0;
        DBRes.GetField( "area_id",area_id, Error );

        int map_id  = 0;
        DBRes.GetField( "map_id",map_id, Error );

        unsigned int area_type_id  = 0;
        DBRes.GetField( "area_type_id",area_type_id, Error );

        int over_count_person = 0;
        DBRes.GetField( "over_count_person",over_count_person, Error );

        int over_count_vehicle = 0;
        DBRes.GetField( "over_count_vehicle",over_count_vehicle, Error );

        int over_time_person = 0;
        DBRes.GetField( "over_time_person",over_time_person, Error );

        int over_time_vehicle = 0;
        DBRes.GetField( "over_time_vehicle",over_time_vehicle, Error );

        std::string path;
        DBRes.GetField( "path",path, Error );

        double monkeycar_speed = 0;
        DBRes.GetField( "monkeycar_speed",monkeycar_speed, Error );

        std::string monkeycar_coor;
        DBRes.GetField( "monkeycar_coordinate",monkeycar_coor, Error );

        double scale=0;
        DBRes.GetField( "scale",scale, Error );

		uint32_t b_type =0;
        DBRes.GetField( "business_type",b_type, Error );

        log_info("monkeycar area init_area : id:%d,path:%s",area_id, path.c_str());

        if(-1 == id)
        {
            std::shared_ptr<db_area>  da = std::make_shared<db_area>();
            da->m_default_speed = monkeycar_speed;
            da->m_point = init_path(monkeycar_coor);
            std::shared_ptr<area> ap = std::make_shared<monkey_area>(da,area_id,over_count_person, over_time_person,scale,map_id,b_type);
            ap->update(over_count_person, over_time_person,scale,map_id,area_type_id, over_count_vehicle,over_time_vehicle);

            ap->m_bound=init_path(path);
            for(const auto &p : ap->m_bound)
              log_info("point:monkey:area_id:%d--x:%.2f,y:%.2f",area_id,p.x,p.y);
            for(const auto &p : da->m_point)
              log_info("point:monkey_coor:area_id:%d--x:%.2f,y:%.2f",area_id,p.x,p.y);

            map.insert({area_id,ap});
        }
        else
        {
			//这里后续需要把猴车得信息传递过去
            std::shared_ptr<db_area>  da = std::make_shared<db_area>();
            da->m_default_speed = monkeycar_speed;
            da->m_point = init_path(monkeycar_coor);
			std::shared_ptr<area> ap = std::make_shared<monkey_area>(da,area_id,over_count_person, over_time_person,scale,map_id,b_type);
			if(auto area_=area_list::instance()->get(id))
				area_=ap;
			else
				area_list::instance()->add(area_id,ap);

            ap->m_bound=init_path(path);
            ap->update(over_count_person, over_time_person,scale,map_id,area_type_id, over_count_vehicle,over_time_vehicle);
            for(const auto &p : ap->m_bound)
              log_info("point:monkey:area_id:%d--x:%.2f,y:%.2f",area_id,p.x,p.y);
            for(const auto &p : da->m_point)
              log_info("point:monkey_coor:area_id:%d--x:%.2f,y:%.2f",area_id,p.x,p.y);

            log_info("基础数据 monkeycar area增加或修改区域成功：区域id:%d,over_count_person:%d over_time_person:%d,\
                     scale:%.2f,map_id:%d,area_type_id:%d,over_count_vehicle:%d,over_time_vehicle:%d",
                     id,over_count_person, over_time_person,scale,map_id,area_type_id,
                     over_count_vehicle,over_time_vehicle);
        }
    }

    if(-1 == id)
    {
        log_info( "monkeycar area init_area. The record count=%d\n", nCount );
        area_list::instance()->add(map);
    }
}

std::shared_ptr<area>  area_list::create(int type,int id,int limit_count_person, int limit_time_person,double scale,int32_t mapid,int32_t b_type) 
{
	if(type==AREA_TYPE_FORBIDDEN)	
	{
		return std::make_shared<special_area>(id,limit_count_person,limit_time_person,scale,mapid,b_type);
	}
	else if(type == AREA_TYPE_DOWNMINE)
	{
		return std::make_shared<underground_area>(id,limit_count_person,limit_time_person,scale,mapid,b_type);

	}
	else
	{
		return std::make_shared<area>(id,limit_count_person,limit_time_person,scale,mapid,b_type);
	}
}
void area_list::init_from_db(int id/*=-1*/)
{
    std::string sql = "SELECT a.area_id, a.name, a.map_id, a.area_type_id, a.path,b.scale, \
            over_count_person, over_count_vehicle, over_time_person, over_time_vehicle, \
            over_speed_vehicle, is_attendance,business_type \
            FROM dat_area a,dat_map b\
            where a.map_id = b.map_id and  area_id not in \
			  (select monkeycar_base_info_id from dat_monkeycar_base_info)";

    if(-1 == id)
    {
        sql.append(";");
    }
    else
    {
        sql.append(" AND s.area_id=");
        sql.append(std::to_string(id));
        sql.append(";");

        log_info("基础数据 增加或修改区域 sql=%s", sql.c_str());
    }

    std::string Error;
    YADB::CDBResultSet DBRes;
    sDBConnPool.Query(sql.c_str(),DBRes,Error);
    int nCount = DBRes.GetRecordCount( Error );
    if (nCount < 1)
    {
        log_error("基础数据 增加或修改失败，数据库中找不到: area_id=%d:%s", id,sql.c_str());
        return ;
    }

    std::unordered_map<int,std::shared_ptr<area>> map;
    while ( DBRes.GetNextRecod(Error) )
    {
        int area_id  = 0;
        DBRes.GetField( "area_id",area_id, Error );

        int map_id  = 0;
        DBRes.GetField( "map_id",map_id, Error );

        unsigned int area_type_id  = 0;
        DBRes.GetField( "area_type_id",area_type_id, Error );

        int over_count_person = 0;
        DBRes.GetField( "over_count_person",over_count_person, Error );

        int over_count_vehicle = 0;
        DBRes.GetField( "over_count_vehicle",over_count_vehicle, Error );

        int over_time_person = 0;
        DBRes.GetField( "over_time_person",over_time_person, Error );

        int over_time_vehicle = 0;
        DBRes.GetField( "over_time_vehicle",over_time_vehicle, Error );

        double over_speed_vehicle=0;
        DBRes.GetField( "over_speed_vehicle",over_speed_vehicle, Error );

        std::string path;
        DBRes.GetField( "path",path, Error );

        double scale = 0;
        DBRes.GetField( "scale",scale, Error );

		uint32_t b_type =0;
        DBRes.GetField( "business_type",b_type, Error );

        log_info("init_area : id:%d,path:%s",area_id, path.c_str());

        if(-1 == id)
        {
            std::shared_ptr<area> ap = create(area_type_id,area_id,over_count_person,over_time_person, scale,map_id,b_type);
            ap->m_limit_vehicle_second = over_time_vehicle;
            ap->m_limit_vehicle_count = over_count_vehicle;
            ap->m_over_speed_vehicle = over_speed_vehicle;

            ap->m_bound=init_path(path);
            for(const auto &p : ap->m_bound)
              log_info("point:area_id:%d--x:%.2f,y:%.2f",area_id,p.x,p.y);
            map.insert({area_id,ap});

//            CheckAreaType(ap,area_type_id,0);
        }
        else
        {
            auto tmp_ptr = area_list::instance()->get(id);
            if(!tmp_ptr)
            {
				tmp_ptr = create(area_type_id,area_id,over_count_person,over_time_person, scale,map_id,b_type);
                area_list::instance()->add(id, tmp_ptr);
            }
            tmp_ptr->update(over_count_person, over_time_person,scale,map_id,area_type_id, over_count_vehicle,over_time_vehicle);
            tmp_ptr->m_bound=init_path(path);
            tmp_ptr->m_over_speed_vehicle = over_speed_vehicle;
            for(const auto &p : tmp_ptr->m_bound)
                log_info("point:area_id:%d--x:%.2f,y:%.2f",area_id,p.x,p.y);
//               CheckAreaType(tmp_ptr,area_type_id,tmp_ptr->m_area_type);

            log_info("基础数据 增加或修改区域成功：区域id:%d,over_count_person:%d over_time_person:%d,scale:%.2f,map_id:%d\
                     ,area_type_id:%d,over_count_vehicle:%d,over_time_vehicle:%d",
                     id,over_count_person, over_time_person,scale,map_id,area_type_id,
                     over_count_vehicle,over_time_vehicle);
        }
    }

    if(-1 == id)
    {
        log_info( "init_area. The record count=%d\n", nCount );
        area_list::instance()->add(map);
    }
    init_monkeycar_area(id);
}


#if 0
//新画禁区功能-给禁区中的卡发送警告及呼叫
void area_list::CheckAreaType(int area_id,int new_area_type,int old_area_type)
{
    auto area_ptr = area_list::instance()->get(area_id);
    if (!area_ptr) {
        log_info("区域已经删除：areaid=%d", area_id);
        return;
    }
    CheckAreaType(area_ptr,new_area_type,old_area_type);
}
void area_list::CheckAreaType( std::shared_ptr<area> pArea,int new_area_type,int old_area_type)
{
    if (nullptr == pArea)
    {
        return ;
    }
    if (new_area_type != AREA_TYPE::AREA_TYPE_FORBIDDEN && old_area_type != AREA_TYPE::AREA_TYPE_FORBIDDEN)
    {
        return;
    }

    struct local_visit:visitor<std::shared_ptr<card_location_base>>
    {
        std::shared_ptr<area> m_area;
        int m_old_area_type;
        bool visit(std::shared_ptr<card_location_base> c)
        {
            //处理
            point pt(c->x,c->y,c->z);
            std::shared_ptr<area> point_area = area_list::instance()->get_area(pt);
            if (point_area == nullptr)
            {
                return true;
            }
            //不在区域里
            if (m_area->m_id != point_area->m_id)
            {
                return true;
            }

            if (m_area->m_area_type == AREA_TYPE::AREA_TYPE_FORBIDDEN)
            {
                //发送进入禁区的警告
                //呼叫
                std::shared_ptr<area_hover> _area_hover = c->get_area_hover();
                if (nullptr != _area_hover)
                {
                    _area_hover->m_area = m_area;
                    _area_hover->m_area->on_enter(_area_hover,c);
                }
            }
            else
            {
                if (m_old_area_type == AREA_TYPE::AREA_TYPE_FORBIDDEN)
                {
                    //之前是禁区，改成非禁区
                    //发送一个离开禁区的警告
                    //停止呼叫
                    std::shared_ptr<area_hover> _area_hover = c->get_area_hover();
                    if (nullptr != _area_hover)
                    {
                        _area_hover->m_area->on_leave(_area_hover,c);
                    }
                }
                else
                {
                    return  false;
                }
            }

            return true;
        }
    };
    local_visit lv;
    lv.m_area = pArea;
    lv.m_old_area_type = old_area_type;
    card_list::instance()->accept(lv);
}

#endif
std::vector<point> area_list::init_path(std::string &str)
{
	if(str.empty())
	  log_error("area path empty()...");
	std::vector<point> vp;
	std::vector<std::string> vs;
	std::vector<std::string> vs1;
	boost::split(vs,str,boost::is_any_of("ML ;"));
	for(auto & s:vs)
	{
		if(s.empty())
		  continue;
		boost::split(vs1,s,boost::is_any_of(","));
		if(vs1.size()!=2)
		  log_error("area path data Error.pls check data table.");
		vp.emplace_back(atof(vs1[0].c_str()),atof(vs1[1].c_str()));
	}
	return std::move(vp);
}

std::vector<std::shared_ptr<area>> area_list::get_area(const std::shared_ptr<site> s,const std::shared_ptr<card_location_base> &c,const point&pt,int & sarid)
{
	std::vector<std::shared_ptr<area>> ret;
		
	auto map = area_list::instance()->m_map;

	for(const auto &a:map)
		if(a.second->in_area(s,c,pt,sarid))
			ret.push_back(a.second);
	//区域覆盖不完全地图，很多车辆人行驶在地图外，如何确认.
	return std::move(ret);
}

void area_tool::on_point(const std::shared_ptr<site>&s,const std::shared_ptr<card_location_base>& c,const point&pt)
{
	log_info("on_point...cardid:%d,type:%d",c->m_id,c->m_type);
	setLandmark(pt);
	
	int special_area=-1;
	std::vector<std::shared_ptr<area>> areas=area_list::instance()->get_area(s,c, pt,special_area);
	if(special_area != -1)
	{
		areas.clear();
		auto area_=area_list::instance()->get(special_area);
		areas.push_back(area_);
	}
	else
		std::sort(areas.begin(),areas.end(),[](const std::shared_ptr<area>&l,const std::shared_ptr<area>&r){
				return l->id()<r->id();
				});

	auto c1=m_hover_list.begin(),ce=m_hover_list.end();
	auto a1=areas.begin() ,ae=areas.end();

	std::vector<std::shared_ptr<area_hover>> nlist;

	while (c1!=ce && a1!=ae)
	{
		if ((*c1)->id()<(*a1)->id()) 
		{ 
			(*c1)->m_area->on_leave(*c1, c);
			++c1;
		}
		else if ((*a1)->id()<(*c1)->id()) 
		{
			nlist.push_back(std::make_shared<area_hover>(*a1,pt));
			(*a1)->on_enter(nlist.back(),c);
			++a1;
		}
		else 
		{ 
			nlist.push_back(*c1);
			(*c1)->m_area->on_hover(*c1,c);
			++c1,++a1;
		}
	}

	while(c1!=ce)
	{
		(*c1)->m_area->on_leave(*c1, c);
		++c1;
	}

	while(a1!=ae)
	{
		nlist.push_back(std::make_shared<area_hover>(*a1,pt));
		(*a1)->on_enter(nlist.back(),c);
		++a1;
	}

	m_hover_list=std::move(nlist);
}

void area_hover::setLandmark(const point &pt)
{
	set(pt);
	auto lm = Landmark_list::instance()->get(mapid(),id(),pt);
	landmark_id = std::get<0>(lm);
	landmark_dir = std::get<1>(lm);
	landmark_dis = std::get<2>(lm)*scale();
	log_info("landmark:%d %d %.2f,(%.2f,%.2f),%f",landmark_id,landmark_dir,landmark_dis,pt.x,pt.y,scale());
}
