/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
// openglwin3.cpp

#include "stdafx.h"
#include "mg/Straight.h"
#include "mg/PickObjectSB.h"
#include "mg/PickObjectCB.h"
#include "mg/PickObjectFB.h"
#include "topo/Face.h"
#include "GLPropPanel.h"
#include "fugenView.h"
#include "PickDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//convert this pick objects to the pick objects that include boundary information.
//function's return value is true if any was converted.
bool fugenView::convert_pick_object_to_boundary(
	const CPoint& point,	//screen position.
	MGPickObjects& pobjs//input and output.
){
	int n=(int)pobjs.size();
	bool converted=false;
	for(int i=n-1; i>=0; i--){
		MGGelPosition& pobj=pobjs[i];
		MGObject* obj = pobj.top_object();
		if(MGFace* f = dynamic_cast<MGFace*>(obj)){
			const MGEdge* e = pick_edge(*f, point);
			if(e){
				pobjs.reset(i,MGPickObjectFB(pobj,e));
				converted=true;
			}else pobjs.erase(i);
		}else if(MGSurface* srf = dynamic_cast<MGSurface*>(obj)){
			int perim = pick_perimeter(*srf, point);
			if(perim != -1){
				pobjs.reset(i,MGPickObjectSB(pobj,perim));
				converted=true;
			}else pobjs.erase(i);
		}else if(MGCurve* curve= dynamic_cast<MGCurve*>(obj)){
			int SE = pick_start_end(*curve, point);
			pobjs.reset(i,MGPickObjectCB(pobj,SE));
			converted=true;
		}else pobjs.erase(i);
	}
	return converted;
}

//Returns all of objects that were inside the pick aperture.
//pick will invoke make_RC_current();
//Function's return value is the picked objects,
//and the number of the picked objects is at most one.
MGPickObjects fugenView::pick(
	const CPoint& point, // client area position. (left, top) is (0, 0).
	const MGAbstractGels& objtypes, //Target pick object kind.
		//Target pick object kind. See MGGEL_KIND in "mg/types.h" or "mg/default.h"
	bool boundary,//true if the boundary of the gell is to select.
	double apx,//specifies pick aperture of x and y.
	double apy//When <=0. value is specified, default value(the value
			//obtained by pick_aperture() will be used.
){
	MGPickObjects pobjs;

	mgVBO* dlistSave=display_list();
	MGAttribedGel* gl=dlistSave->gel();
	const MGGroup* currentGrp=document().current_group();
	if(gl && currentGrp){

	int sx,sy;
	change_sc(point,sx,sy);

	if (apx <= 0.)
		apx = pick_aperture();
	if (apy <= 0.)
		apy = pick_aperture();
	float centrApertr[4]={(float)sx,(float)sy,(float)apx,(float)apy};
        ///<Screen coordinates. (left, bottom) is (0,0).

	mgVBO* dlist2pick=currentGrp->dlist_name();
	set_display_list(dlist2pick);	
	pobjs=pick_glv(centrApertr,objtypes);
	set_display_list(dlistSave);

	if(pobjs.size()>1){
		CPickDlg dlg(this, pobjs);
		if(IDCANCEL == dlg.DoModal()){
			pobjs.clear();
		}else{
			pobjs.resize(1);
			pobjs.reset(0, dlg.GetResult());
		}
		SetFocus();//Focus is changed from dialogue to this Fugenview.
	}
	if(pobjs.size()){
		if(boundary)
			convert_pick_object_to_boundary(point,pobjs);
		else{
			MGPickObject& pobj=pobjs[0];
			const MGObject* obj=pobj.leaf_object();
			MGStraight cursorSL;
			unproject_to_sl_glv(sx,sy,cursorSL);
			MGPosition param=obj->pick_closest(cursorSL);
			//std::cout<<"Pick:param="<<param<<std::endl;
			pobj.set_parameter(param);
		}
	}

	}
	return pobjs;
}

//Determine if point is closer to the start point or to the end
//of the curve curve.
//Functin's return value is 0: if start point, 1: if end point.
int fugenView::pick_start_end(
	const MGCurve& curve,
	const CPoint& point	// client area coordinates. (left, top) is (0,0).
){
	int sx, sy;
	change_sc(point,sx,sy);
	return pick_start_end_glv(curve, sx, sy);
}

struct PtOutRect : std::unary_function<CPoint, bool>{
	PtOutRect(const CRect* rc):m_rc(rc){ASSERT(rc);}
	bool operator()(const CPoint& point) const{
		return !m_rc->PtInRect(point);
	}
	const CRect* m_rc;
};

//Returns all of objects that were inside rectangle [pt1, pt2] into pobj.
//pick will invoke make_RC_current();
//function's return value is
//true: if pick_rect() was valid since pt1 and pt2 were different.
//false: if pick_reck() was invalid since pt1 and pt2 were the same.
//When false was returned, pobj will be unchanged.
bool fugenView::pick_rect(
	const CPoint& pt1, // client area position. (left, top) is (0, 0).
	const CPoint& pt2, // the same as above
	MGPickObjects& pobj,
	const MGAbstractGels& objtypes //Target pick object kind.
		//Target pick object kind. See MGGEL_KIND in "mg/types.h" or "mg/default.h"
){
	int dx = abs(pt1.x - pt2.x), dy = abs(pt1.y - pt2.y);
	if(dx<=0 || dy<=0)
		return false;

	int cx=(pt1.x + pt2.x)/2, cy=(pt1.y + pt2.y)/2;
	int sx, sy;
	change_sc(cx,cy,sx,sy);

	MGPickObjects pobj2;
	const MGGroup* currentGrp=document().current_group();
	if(currentGrp){
		mgVBO* dlistSave=display_list();
		mgVBO* dlist2pick=currentGrp->dlist_name();
		set_display_list(dlist2pick);
		float centrApertr[4] = {(float)sx,(float)sy,(float)dx,(float)dy};
		pobj2=pick_glv(centrApertr,objtypes);
		set_display_list(dlistSave);
	}

	CRect rc(pt1,pt2);
	rc.NormalizeRect();
	size_t n=pobj2.size();
	for(size_t i=0; i<n; i++){
		MGPickObject& pobj2i=pobj2[(int)i];
		if(pt1.x>=pt2.x)
			pobj.push_back(pobj2[(int)i]);
		else{
			MGObject* obji=pobj2i.top_object();
			std::vector<CPoint> sc;
			convert_to_screen(obji->box().vertex(), sc);
			if(std::find_if(sc.begin(), sc.end(), PtOutRect(&rc)) == sc.end())
				pobj.push_back(pobj2[(int)i]);
		}
	}
	return true;
}

//Pick an edge of the face f. That is, obtain the edge number
//that passes input (sx,sy) when drawn in the current view matrix.
//Function's return value is the edge pointer picked.
//When no edges are picked, null will be returned.
const MGEdge* fugenView::pick_edge(
	const MGFace& f,
	const CPoint& point,	// client area coordinates. (left, top) is (0,0).
	float apx,//specifies pick aperture of x and y.
	float apy,//When <=0. value is specified, default value(the value
			//obtained by pick_aperture() will be used.
	MGPosition* uv	//surface parameter (u,v) nearest to (sx,sy) will be returned.
){
	int sx, sy;
	change_sc(point,sx,sy);
	return pick_edge_glv(f, sx, sy, uv, apx, apy);
}

//Pick a perimeter of the surface surf. That is, obtain the perimeter number
//that passes input (sx,sy) when drawn in the current view matrix.
//Function's return value is perimeter number picked.
//When no perimeters are picked, -1 will be returned.
int fugenView::pick_perimeter(
	const MGSurface& surf,
	const CPoint& point,	// client area coordinates. (left, top) is (0,0).
	double apx,
	double apy,
	MGPosition* uv  //surface parameter (u,v) nearest to (sx,sy) will be returned.
){
	int sx, sy;
	change_sc(point,sx,sy);
	return pick_perimeter_glv(surf, sx, sy, uv, (float)apx, (float)apy);
}

//Returns all of objects that were inside rectangle [pt1, pt2] into pobj.
//pick will invoke make_RC_current();
//function's return value is true: if pick_rect() was valid since pt1 and pt2 were different.
//false: if pick_reck() was invalid since pt1 and pt2 were the same.
//When false was returned, pobj will be unchanged
bool fugenView::pick_rect2(
	UINT nFlags,
	const CPoint& old_point,
	const CPoint& new_point,
		//Above parameters are of CWnd::OnLButtonDown. See the document.
	MGPickObjects& pobjs,//input(objects so far) and output(updated pobjs).
	const MGAbstractGels& types,//gell type to pick.
	bool multi_selection,	//true if multi gells can be selected.
							//false if only one is allowed to select.
	bool boundary		//true if the boundary of the gell is to select.
){
	MGPickObjects pobjs2;
	// 矩形領域に含まれるオブジェクトをそのまま選択する(pbojs2)
	if(!pick_rect(old_point,new_point,pobjs2,types)) return false;

	if(nFlags&MK_CONTROL){//When control key is down.
		pobjs.reset_with_symmetric_difference(pobjs2);
	}else{
		// 効率重視
		//pobjs=pobjs2;
		pobjs.object_vector().swap(pobjs2.object_vector());
	}

	if(!multi_selection)
		pobjs.erase_except_front();

	if(boundary) convert_pick_object_to_boundary(new_point,pobjs);

	return true;
}
void fugenView::OnUpdateCPlaneGrid(CCmdUI* pCmdUI)
{
	pCmdUI->Enable();
}
