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

/**
 * @file SelectState.cpp
 * @brief SelectState.h の実装
 */
#include "stdafx.h"
#include "mg/PickObjectCB.h"
#include "mg/PickObjectFB.h"
#include "mg/PickObjectSB.h"
#include "mgGL/VBOLeaf.h"
#include "fugenView.h"
#include "Common/SelectState.h"
#include "Common/CommandStateOwner.h"

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

//
//Implements MGSelectState Class.
//MGSelectState is a class to select(pick) objects.
//

//////////Constructor//////////

// Standard constructor. This state belongs to owner.
MGSelectState::MGSelectState(
	MGCommandStateOwner* owner,//Owner of this MGSelectState.
	SELECTION_TYPE rect_selection,//MULTIPLE_SELECT if multiple object is to select.
								//SINGLE_SELECT if only one object is to select.
	const MGAbstractGels& gell_types,	//type of gells to select.
	BOUNDARY_SELECTION boundary,	//BOUNDARY_SELECT if the boundary of the gell is to select.
	PROHIBIT_UNSELECTION prohibit_unselect
):MGCommandBase(owner),
m_rect_selection(rect_selection),m_add(false),
m_gell_types(gell_types),m_boundary(boundary),
m_prohibit_unselect(prohibit_unselect){
}

// constructor that does not have an owner.
MGSelectState::MGSelectState(
	fugenDoc*  pDoc,		//document.
	UINT	command_id,	//command id
	SELECTION_TYPE rect_selection,//MULTIPLE_SELECT if multiple object is to select.
								//SINGLE_SELECT if only one object is to select.
	const MGAbstractGels& gell_types,	//type of gells to select.
	BOUNDARY_SELECTION boundary,	//BOUNDARY_SELECT if the boundary of the gell is to select.
	PROHIBIT_UNSELECTION prohibit_unselect
):MGCommandBase(pDoc,command_id),
m_rect_selection(rect_selection),m_add(false),
m_gell_types(gell_types),m_boundary(boundary),
m_prohibit_unselect(prohibit_unselect){
}

//Virtual Destructor
MGSelectState::~MGSelectState(){
	m_PickRectangle.detachFromWindow();
}

//////////Member Function//////////

//clear the curennt object.
void MGSelectState::clear_pick_object(){
	m_locked_objects.clear();
	m_objects_after_locked.clear();
	MGCommandBase::clear_pick_object();
}

//Exclude objs from the curennt object.
void MGSelectState::exclude_pick_object(
	const MGPickObjects& objs	//selected objects at this selection operation.
){
	m_locked_objects.remove(objs);
	m_objects_after_locked.remove(objs);
	MGCommandBase::exclude_pick_object(objs);
}

//Initiate the class.
//Overrided initiate must invoke MGCommandBase::initiate_tool first.
//Function's return value is true:if this command should be terminated on return,
//false if not.
bool MGSelectState::initiate_tool(){
	MGCommandBase::initiate_tool();
	return false;
}

//Select objects of specified type from the document's current objects,
//and reset the current objects with them.
void MGSelectState::reset_current_objects(
	const MGAbstractGels& type
){
	m_locked_objects.reset_objects(type);
	m_objects_after_locked.reset_objects(type);
	MGCommandBase::reset_current_objects(type);
}

//lock so far selected objects.
void MGSelectState::lock_so_far(){
	m_locked_objects=current_objects();
	m_objects_after_locked.clear();
}
void MGSelectState::unlock_so_far(){
	m_locked_objects.clear();
	m_objects_after_locked.clear();
}

//Perform selection operation.
bool MGSelectState::selection(
	fugenView* pView,   // ピックイベントの発生したビュー
	MGPickObjects& picked,//the oroginal picked objects are input. 
		//Will be modified so that picked contains newly picked objects.
	UINT           nFlags   // MK_CONTROL, MK_SHIFT を見るため
){
	bool bCtrl = (nFlags & MK_CONTROL) != 0;    // Ctrl が押されている場合
	bool bShift = (nFlags & MK_SHIFT) != 0;  // Shift が押されている場合

	// 元々 add-mode か、もしくは Ctrl or Shift のどちらか一方のみが押されている
	// 場合、add-mode であるとみなす
	bool add_mode = m_add || (bCtrl ^ bShift);

	// ロックされているオブジェクトがある場合
	// 生の選択オブジェクトからロックオブジェクトを除外する
	picked-=m_locked_objects;

	MGPickObjects curobj = current_objects();
	if(add_mode && (m_prohibit_unselect==PROHIBIT_UNSELECT))
		picked-=curobj;

	MGPickObjects unselected;//unselected objects will be returned
	if(m_locked_objects.empty()){//ロックされているオブジェクトがない場合
		if(add_mode){// 選択モードが add-mode であるとき
			// 1. unselected 決定
			unselected = picked;
			unselected&=curobj;

			// 2. selected 決定
			picked-=unselected;

			// 3. current 決定
			curobj-=unselected;
			curobj+=picked;
		}else{
			// add-mode でないとき
			// current == (生の)selected
			curobj = picked;
		}
	}else{//ロックされているオブジェクトがある場合
		if(add_mode){// 選択モードが add-mode であるとき
			// 2.1. unselected を決定
			unselected = picked;
			unselected&=m_objects_after_locked;

			// 2.2. selected を決定。unselected を利用する。
			picked-=unselected;

			// 2.3 add-mode では after_locked を更新。
			m_objects_after_locked-=unselected;
			m_objects_after_locked+=picked;
		}else{
			// add-mode でないとき
			// after_locked == selected となる
			m_objects_after_locked = picked;
		}
		// 3. current を決定する。
		// locked と after_locked の union となる(直和)。
		curobj = m_locked_objects;
		curobj+=m_objects_after_locked;
	}

	set_current_object(curobj);
	if(!(picked.empty() && unselected.empty())){
		pView->redraw();
		return OnSelected(pView, picked, unselected);
	}
	return false;
}

bool MGSelectState::OnLButtonDown(
	fugenView* pView,
	UINT           nFlags,
	CPoint         point
){
	MGCommandBase::OnLButtonDown(pView,nFlags,point);

	// 1. ピック位置に存在するオブジェクトを高々ひとつ選択する
	// この時点で current_objects() に変更はまだない
	MGPickObjects picked = pView->pick(point, m_gell_types, m_boundary==BOUNDARY_SELECT);
	ASSERT(picked.size() <= 1);
	m_PickRectangle.attatchToWindow(pView);

	// 2. 選択状態を決定する
	bool selected=selection(pView, picked, nFlags);
	return selected;
}

bool MGSelectState::OnLButtonUp(
	fugenView* pView,
	CPoint         old_point,
	UINT           nFlags,
	CPoint         point
){
	MGCommandBase::OnLButtonUp(pView,old_point,nFlags,point);

	bool detached=m_PickRectangle.detachFromWindow();
	if(m_rect_selection==MULTIPLE_SELECT){
		// 1. 矩形領域に含まれるオブジェクトを調べる
		MGPickObjects picked;
		if(pView->pick_rect2(
			0,            // MK_CONTROL が邪魔をするのでゼロを指定
			old_point,
			point,
			picked,       //< 矩形領域内に描画されているすべてのオブジェクトが出力される
			m_gell_types,
			true,
			m_boundary==BOUNDARY_SELECT)
		){
			// 2. 選択状態を決定する
			bool selectResult=selection(pView, picked, nFlags);
			return selectResult;
		}
	}
	return false;
}

bool MGSelectState::OnMouseMove(
	fugenView* window,//The fugenView pointer where this event took place.
	UINT nFlags, CPoint point
		//These parameters are of CWnd::OnMouseMove. See the document.
){
	m_PickRectangle.set_no_display();

	// 複数選択可モードのときに限りラバーバンドを描画する
	if(window->IsPicking() && m_rect_selection==MULTIPLE_SELECT){
		fugenView* windAttated=m_PickRectangle.getAttatchedWindow();
		if(window==windAttated){
			m_PickRectangle.updateRectangle();
			window->redrawOnlythis();
		}
	}

	MGCommandBase::OnMouseMove(window,nFlags,point);
	return false;
}

bool MGSelectState::OnSelected(
	fugenView* pView, //The fugenView pointer where point input event took place.
	MGPickObjects& picked, //selected objects at this selection operation.
	MGPickObjects& unselected//unselected objects at this selection operation.
	//unselected_objects.size()>=1 only when the already selected objects are selected
	//when add mode is set(or when operation is done with a crtl key pressed).
){
	pView->redraw();
	return false;
}

void MGSelectState::ModifyContextMenu(CMenu& popup){
	// 規定の Cancel を加える。
	MGCommandBase::ModifyContextMenu(popup);

	// SelectState 共通メニュー
	InsertMenu(IDR_MENU_SELECT_STATE, popup);
}

bool MGSelectState::CanHandle(UINT nID) const{
	if(MGCommandBase::CanHandle(nID)){
		return true;
	}

	return nID == ID_COMMAND_SELECT_OK;
}

bool MGSelectState::HandleMessage(UINT nID){
	if(MGCommandBase::HandleMessage(nID)){
		return true;
	}

	if(nID == ID_COMMAND_SELECT_OK){

		// XXX: 意味は上のコメントアウトコードのとおり。
		// サブクラスで見境なく OnKeyDown をオーバーライドしているため、
		// 小手先のコードで「選択終了」を実現しておく。
		//TODO!
		fugenView* pView = document()->get_main_view();
		if(pView){
			pView->PostMessage(WM_KEYDOWN, static_cast<WPARAM>(VK_RETURN));
		}
		return true;
	}

	return false;
}

bool MGSelectState::HandleUpdateUI(UINT nID, CCmdUI* pCmdUI){
	if(MGCommandBase::HandleUpdateUI(nID, pCmdUI)){
		return true;
	}

	if(nID == ID_COMMAND_SELECT_OK){
		// XXX: とりあえず無条件に有効にして様子をみる。
		//pCmdUI->Enable(m_rect_selection == MULTIPLE_SELECT);
		pCmdUI->Enable();
		return true;
	}

	return false;
}
