#include "SimpleWindow.h"
#include "SpriteModel.h"
#include "MotionCharactor.h"
#include <Fl/x.h>

bool	fkut_SimpleWindow::prevKeySt[256],
		fkut_SimpleWindow::prevSPKeySt[32],
		fkut_SimpleWindow::prevMouseSt[3];

void fkut_SimpleWindow::PushPrevStatus(void)
{
	int	i;

	for(i = 0; i < 255; ++i) {
		prevKeySt[i] = drawWin->getKeyStatus((char)i, false);
	}
	for(i = 0; i < 32; ++i) {
		prevSPKeySt[i] = drawWin->getSpecialKeyStatus((fk_SpecialKey)i, false);
	}
	for(i = 0; i < 3; ++i) {
		prevMouseSt[i] = drawWin->getMouseStatus((fk_MouseButton)i, false);
	}
	for(i = 0; i < inputCount; ++i) {
		prevInput[i] = nowInput[i];
	}

	return;
}

// スクリーンモードのハンドリング処理
void fkut_SimpleWindow::ToggleScreen(void)
{
	// ALT+ENTERでフルスクリーンとウィンドウモードを切り替え
	if(getSpecialKeyStatus(FK_ALT_L) >= FKUT_SW_DOWN
	|| getSpecialKeyStatus(FK_ALT_R) >= FKUT_SW_DOWN) {
		if(getSpecialKeyStatus(FK_ENTER) == FKUT_SW_DOWN) {
			if(fsc.isFullscreen() == true) {
				fsc.changeToWindow();
			} else {
				fsc.changeToFullscreen();
			}
		}
	}
	// ウィンドウのフォーカスが外れたらウィンドウモードに戻す
	if(Fl::focus() != mainWin) {
		toWindow();
	}

	return;
}

fkut_SimpleWindow::fkut_SimpleWindow(void)
{
	fk_System::setcwd();

	mainWin = new Fl_Window(512, 512, "FKUT Window");
	drawWin = new fk_Window(0, 0, 512, 512);
	mainWin->end();

	fk_Material::initDefault();

	light.setShape(&lightShape);
	light.setMaterial(White);
	light.getMaterial()->setAmbDiff(1.0, 1.0, 1.0);
	light.glMoveTo(0.0, 0.0, 0.0);
	light.glFocus(-1.0, -1.0, -1.0);

	scene.setBlendStatus(true);
	scene.entryCamera(&camera);
	scene.entryModel(&light);

	drawWin->setScene(&scene);

	setFPS(60);

	camera.glMoveTo(0.0, 0.0, 100.0);
	camera.glFocus(0.0, 0.0, 0.0);
	camera.glUpvec(0.0, 1.0, 0.0);

	tb = new fk_TrackBall(drawWin, &camera);
	tbFlag = false;
	childMode = false;
	ref_child = NULL;

	ref_camera = &camera;
	ref_scene = &scene;

	PushPrevStatus();

	inputCount = 0;

	return;
}

fkut_SimpleWindow::fkut_SimpleWindow(fkut_SimpleWindow &argParent)
{
	mainWin = argParent.mainWin;
	mainWin->begin();
	argParent.drawWin->resizeWindow(0, 0, mainWin->w(), mainWin->h()/2);
	drawWin = new fk_Window(0, mainWin->h()/2, mainWin->w(), mainWin->h()/2);
	mainWin->end();

	fk_Material::initDefault();

	light.setShape(&lightShape);
	light.setMaterial(White);
	light.getMaterial()->setAmbDiff(1.0, 1.0, 1.0);
	light.glMoveTo(0.0, 0.0, 0.0);
	light.glFocus(-1.0, -1.0, -1.0);

	scene.setBlendStatus(true);
	scene.entryCamera(&camera);
	scene.entryModel(&light);

	drawWin->setScene(&scene);

	setFPS(60);

	camera.glMoveTo(0.0, 0.0, 100.0);
	camera.glFocus(0.0, 0.0, 0.0);
	camera.glUpvec(0.0, 1.0, 0.0);

	tb = new fk_TrackBall(drawWin, &camera);
	tbFlag = false;
	childMode = true;
	ref_child = NULL;
	argParent.ref_child = this;

	ref_camera = &camera;
	ref_scene = &scene;

	PushPrevStatus();

	inputCount = 0;

	return;
}

fkut_SimpleWindow::~fkut_SimpleWindow(void)
{
	input.uninit();
	drawWin->hide();
	mainWin->hide();

	delete tb;
	delete drawWin;
	if(!childMode) delete mainWin;
}

void fkut_SimpleWindow::setWindowName(const string &name)
{
	mainWin->label(fk_Code::utf8(name.c_str(), FK_STR_SJIS));
	return;
}

void fkut_SimpleWindow::setSize(int argW, int argH)
{
	mainWin->resize(mainWin->x(), mainWin->y(), argW, argH);
	if(!childMode) drawWin->resizeWindow(0, 0, argW, argH);
}

void fkut_SimpleWindow::setInnerSize(int argX, int argY, int argW, int argH)
{
	drawWin->resizeWindow(argX, argY, argW, argH);
}

void fkut_SimpleWindow::setBGColor(double r, double g, double b)
{
	ref_scene->setBGColor((float)r, (float)g, (float)b);
}

void fkut_SimpleWindow::setFPS(int argFPS)
{
	if(argFPS == 0) {
		fps = 0;
		fps_admin.OnFrameSkip(false);
	} else {
		fps = argFPS;
		fps_admin.OnFrameSkip(true);
		fps_admin.SetFPS(argFPS);
	}
}

void fkut_SimpleWindow::setTrackBallMode(bool mode)
{
	tbFlag = mode;
}

void fkut_SimpleWindow::showGuide(fkut_GuideMode mode)
{
	guide.entryScene(ref_scene, mode);
}

void fkut_SimpleWindow::hideGuide(void)
{
	guide.removeScene(ref_scene);
}

void fkut_SimpleWindow::setCameraPos(double x, double y, double z)
{
	ref_camera->glMoveTo(x, y, z);
}

void fkut_SimpleWindow::setCameraFocus(double x, double y, double z)
{
	ref_camera->glFocus(x, y, z);
	tb->setLookTo(fk_Vector(x, y, z));
}

void fkut_SimpleWindow::setCameraModel(fk_Model &model)
{
	ref_camera = &model;
	ref_scene->entryCamera(ref_camera);
}

fk_Model * fkut_SimpleWindow::getCameraModel(void)
{
	return ref_camera;
}

void fkut_SimpleWindow::setCameraDefault(void)
{
	setCameraModel(camera);
}

void fkut_SimpleWindow::setScene(fk_Scene &argScene)
{
	ref_scene = &argScene;
	ref_camera = const_cast<fk_Model *>(ref_scene->getCamera());
}

fk_Scene * fkut_SimpleWindow::getScene(void)
{
	return ref_scene;
}

void fkut_SimpleWindow::setSceneDefault(void)
{
	setScene(scene);
}

void fkut_SimpleWindow::entry(fk_Model &model)
{
	ref_scene->entryModel(&model);
}

void fkut_SimpleWindow::remove(fk_Model &model)
{
	ref_scene->removeModel(&model);
}

void fkut_SimpleWindow::entry(fk_Model &model, fkut_GuideObject &argGuide)
{
	argGuide.setParent(&model);
	argGuide.entryScene(ref_scene);
	ref_scene->entryModel(&model);
}

void fkut_SimpleWindow::remove(fk_Model &model, fkut_GuideObject &argGuide)
{
	argGuide.setParent(NULL);
	argGuide.removeScene(ref_scene);
	ref_scene->removeModel(&model);
}

void fkut_SimpleWindow::entry(fkut_SpriteModel &model)
{
	model.MakePixelBase(drawWin, ref_scene);
	ref_scene->entryOverlayModel(&model);
}

void fkut_SimpleWindow::remove(fkut_SpriteModel &model)
{
	ref_scene->removeOverlayModel(&model);
}

void fkut_SimpleWindow::entry(CMotionCharactor &chara)
{
	chara.entryScene(ref_scene);
}

void fkut_SimpleWindow::remove(CMotionCharactor &chara)
{
	chara.removeScene(ref_scene);
}

void fkut_SimpleWindow::clearModel(void)
{
	ref_scene->clearDisplay();
}

void fkut_SimpleWindow::open(void)
{
	mainWin->show();
	drawWin->show();

	if(S_OK == input.init(fl_display, fl_xid(mainWin))) {
		inputCount = input.getJoyCount();
		input.update();
		for(int i = 0; i < inputCount; ++i) {
			prevInput[i] = nowInput[i] = input.getInputStatus(i);
		}
	}

	fsc.init(mainWin, drawWin);

	return;
}

void fkut_SimpleWindow::close(void)
{
	drawWin->hide();
	if(!childMode) {
		toWindow();
		mainWin->hide();
	}

	return;
}

bool fkut_SimpleWindow::update(bool argForceDraw)
{
	if(childMode) return false;

	PushPrevStatus();
	if(fps != 0) fps_admin.TimeRegular();

	if(mainWin->visible() == 0) {
		if(Fl::wait() == 0) {
			return false;
		} else {
			return true;
		}
	}

	if(fps_admin.GetDrawFlag() || fps == 0 || argForceDraw) {
		drawWin->drawWindow();
		if(ref_child != NULL) ref_child->drawWin->drawWindow();
	}
	if(Fl::check() == 0) return false;

	ToggleScreen();

	if(inputCount != 0) {
		input.update();
		for(int i = 0; i < inputCount; ++i) {
			nowInput[i] = input.getInputStatus(i);
		}
	}

	if(tbFlag) tb->update();
	if(ref_child != NULL) {
		ref_child->tb->update();
	}

	return true;
}

fkut_SwitchStatus GetSwitchStatus(bool now, bool pre)
{
	switch((int)now*2 + (int)pre) {
	case 0:
		return FKUT_SW_RELEASE;
	case 1:
		return FKUT_SW_UP;
	case 2:
		return FKUT_SW_DOWN;
	case 3:
		return FKUT_SW_PRESS;
	default:
		return FKUT_SW_RELEASE;
	}
}

fkut_SwitchStatus fkut_SimpleWindow::getKeyStatus(char argKey)
{
	return GetSwitchStatus(drawWin->getKeyStatus(argKey, false), prevKeySt[(int)argKey]);
}

fkut_SwitchStatus fkut_SimpleWindow::getSpecialKeyStatus(fk_SpecialKey argKey)
{
	return GetSwitchStatus(drawWin->getSpecialKeyStatus(argKey, false), prevSPKeySt[(int)argKey]);
}

fkut_SwitchStatus fkut_SimpleWindow::getMouseStatus(fk_MouseButton argButton)
{
	return GetSwitchStatus(drawWin->getMouseStatus(argButton, false), prevMouseSt[(int)argButton]);
}

fk_Vector fkut_SimpleWindow::getMousePosition(void)
{
	int			x, y;
	fk_Vector	retPos;

	drawWin->getMousePosition(&x, &y, false);

	retPos.x = (double)x;
	retPos.y = (double)y;
	retPos.z = 0.0;

	return retPos;
}

int fkut_SimpleWindow::getPadCount(void)
{
	return inputCount;
}

fkut_SwitchStatus fkut_SimpleWindow::getPadButtonStatus(int padID, int buttonID)
{
	if(padID > inputCount || buttonID > 32+4) return FKUT_SW_RELEASE;

	switch(buttonID) {
	case FKUT_PAD_UP:
		return GetSwitchStatus(nowInput[padID].up, prevInput[padID].up);
	case FKUT_PAD_DOWN:
		return GetSwitchStatus(nowInput[padID].down, prevInput[padID].down);
	case FKUT_PAD_LEFT:
		return GetSwitchStatus(nowInput[padID].left, prevInput[padID].left);
	case FKUT_PAD_RIGHT:
		return GetSwitchStatus(nowInput[padID].right, prevInput[padID].right);
	default:
		return GetSwitchStatus(nowInput[padID].button[buttonID],
								prevInput[padID].button[buttonID]);
	}
}

fk_Vector fkut_SimpleWindow::getPadDirection(int padID, int axisID)
{
	fk_Vector	retVec;
	if(padID > inputCount || axisID > 3) return retVec;

	retVec.x = nowInput[padID].x[axisID] * 0.001;
	retVec.y = nowInput[padID].y[axisID] * 0.001;

	return retVec;
}

fkut_Input * fkut_SimpleWindow::getPadManager(void)
{
	return &input;
}

bool fkut_SimpleWindow::isModelPicked(fk_Model &model, int pixel, int mouseX, int mouseY)
{
	static fk_PickData	pick;
	const bool orgMode = model.getPickMode();

	if(mouseX == -1 || mouseY == -1) {
		drawWin->getMousePosition(&mouseX, &mouseY);
	}
	if(mouseX == -1 || mouseY == -1) return false;

	model.setPickMode(true);
	drawWin->getPickModel(&pick, pixel, mouseX, mouseY);
	model.setPickMode(orgMode);

	for(int i = 0; i < pick.getSize(); i++) {
		if(pick.getModel(i) == &model) return true;
	}

	return false;
}

void fkut_SimpleWindow::setGuideAxisWidth(double width)
{
	guide.setAxisWidth(width);
}

void fkut_SimpleWindow::setGuideGridWidth(double width)
{
	guide.setGridWidth(width);
}

void fkut_SimpleWindow::setGuideScale(double scale)
{
	guide.setScale(scale);
}

void fkut_SimpleWindow::setGuideNum(int num)
{
	guide.setNum(num);
}

void fkut_SimpleWindow::setCursorState(bool visible, bool center)
{
	if(visible) {
		ShowCursor(TRUE);	// Win32API
	} else {
		ShowCursor(FALSE);	// Win32API
	}
	if(center) {
		// Win32API
		SetCursorPos(mainWin->x()+mainWin->w()/2, mainWin->y()+mainWin->h()/2);
	}

	return;
}

void fkut_SimpleWindow::procMouseView(fk_Model &camera, double spinX, double spinY, bool lockSW)
{
	static POINT	po;
	static int		nowX, nowY, prevX, prevY, nowZ;
	static double	diffX, diffY;
	static bool		once = false;

	GetCursorPos(&po);	// Win32API
	if(once == false) {
		prevX = po.x;
		prevY = po.y;
		once = true;
	}

	nowX = po.x;
	nowY = po.y;
	diffX = nowX - prevX;
	diffY = nowY - prevY;
	camera.glRotateWithVec(camera.getPosition(), fk_Y, -diffX*spinX);
	camera.glRotateWithVec(camera.getPosition(), camera.getPosition()+(camera.getVec()^camera.getUpVec()), -diffY*spinY);
	if(lockSW == true){
		SetCursorPos(prevX, prevY);	// Win32API
	} else {
		prevX = nowX;
		prevY = nowY;
	}
}

void fkut_SimpleWindow::toFullscreen(void)
{
	if(!fsc.isFullscreen()) fsc.changeToFullscreen();
}

void fkut_SimpleWindow::toWindow(void)
{
	if(fsc.isFullscreen()) fsc.changeToWindow();
}
