#!/usr/bin/perl -w

use strict;
use OpenGL qw/ :all /;

use lib 'd:/Programs/Perl/lib';
use Sci qw(mod);
use Graphics::OpenGL;
use Crystal::OpenGL;
use Crystal::CIF;
use Crystal::Crystal;

#my $CIFPath = "D:/Programs/Perl/lib/Crystal/BaTiO3.cif";
my $CIFPath = "D:/MyWebs/Research/CrystalStructure/Na6Al6Si6O24.cif"; #SiO2-a-quartz.cif";
my $SaveImagePath = 'g:/MyImage.tga';

my $Light0 = new Graphics::OpenGLMaterial(
				-Position => [2.0, 8.0, 2.0, 0.0],
				-Diffuse  => [1.0, 1.0, 1.0, 1.0],
				-Ambient  => [0.15, 0.15, 0.15, 0.15],
				-Specular => [1.0, 1.0, 1.0, 1.0],
			);
my $Light1 = new Graphics::OpenGLMaterial(
				-Position => [-2.0, -8.0, 2.0, 0.0],
				-Diffuse  => [1.0, 1.0, 1.0, 1.0],
				-Ambient  => [0.15, 0.15, 0.15, 0.15],
				-Specular => [1.0, 1.0, 1.0, 1.0],
			);
my $Mater1 = new Graphics::OpenGLMaterial(
				-Diffuse   => [0.5, 0.7, 0.5, 0.5],
				-Ambient   => [0.5, 0.7, 0.5, 0.5],
				-Specular  => [1.0, 1.0, 1.0, 1.0],
#				-Emission  => [0.0, 0.0, 1.0, 1.0],
				-Shininess => [50.0],
			);
my $IonRadiusScaleFactor = 0.6;
my $MaxBondLength        = 2.6;
my $nMaxBonds            = 1000;
#my ($nx, $ny, $nz)       = (1, 1, 1);
my ($nx, $ny, $nz)       = (1.5, 1.1, 1.1);

my ($WindowWidth, $WindowHeight) = (800, 800);
my $SelectionBufferSize = 100;

my $OGL = new Graphics::OpenGL();
$OGL->OnInitializeForMouseTranslation();
$OGL->OnInitializeForMouseRotation();
$OGL->OnInitializeForMouseZoom(20.0, 30.0);
$OGL->{KeyMouseMode} = 'rot';

$OGL->Initialize(GLUT_RGBA | GLUT_DEPTH, 0, 0, $WindowWidth, $WindowHeight, 'Test');
$OGL->SetFunctions(
		-Reshape    => sub { &resize($OGL, @_);   },
		-Display    => sub { &display($OGL, @_);  },
		-Mouse      => sub { &mouse($OGL, @_);    },
		-Motion     => sub { &motion($OGL, @_);   },
		-Keyboard   => sub { &keyboard($OGL, @_); },
		);
$OGL->CreateSelectionBuffer($SelectionBufferSize);
&InitializeOpenGL($OGL);

my $Crystal = &ReadCIFFile($CIFPath);
die $! if(!$Crystal);
my $COGL = new Crystal::OpenGL();
die $! if(!$COGL);
$COGL->SetSampleName($Crystal->CrystalName());
$COGL->Prepare($OGL, $Crystal, $nx, $ny, $nz, $IonRadiusScaleFactor);

&scene($OGL, $COGL);
$OGL->Run();

exit;

sub ReadCIFFile
{
	my ($CIFFile) = @_;

	my $CIF = new CIF();
	my $Crystal = new Crystal();
	unless($CIF->Read($CIFFile)) {
		print "aError: Can not read $CIFFile.\n";
		exit;
	}
	$Crystal = $CIF->GetCCrystal();
	$Crystal->ExpandCoordinates();
	return $Crystal;
}

sub keyboard
{
	my ($OGL, $key, $x, $y) = @_;

	if($key == unpack('c', 'q') or $key == unpack('c', 'Q')) {
		exit;
	}
	elsif($key == unpack('c', 'c') or $key == unpack('c', 'C')) {
		if($OGL->{KeyMouseMode} eq 'zoom') {
			$OGL->{KeyMouseMode} = 'rot';
			print "Mouse mode changed to 'rotation'.\n";
		}
		else {
			$OGL->{KeyMouseMode} = 'zoom';
			print "Mouse mode changed to 'zoom'.\n";
		}
	}
}

sub motion {
	my ($OGL, $x, $y) = @_;

	if($OGL->{MouseMode} eq 'rot') {
		if($OGL->{KeyMouseMode} eq 'zoom') {
			if($OGL->IsCtrlPressed()) {
				$OGL->OnMotionForMouseRotation($x, $y);
			}
			else {
				$OGL->OnMotionForMouseZoom($x, $y);
			}
		}
		else {
			if($OGL->IsCtrlPressed()) {
				$OGL->OnMotionForMouseZoom($x, $y);
			}
			else {
				$OGL->OnMotionForMouseRotation($x, $y);
			}
		}
	}
	elsif($OGL->{MouseMode} eq 'tr') {
		$OGL->OnMotionForMouseTranslation($x, $y);
	}
}

sub mouse {
	my ($OGL, $button, $state, $x, $y) = @_;

#print "b=$button, $state\n";
	if($button == GLUT_LEFT_BUTTON) {
		if($state == GLUT_DOWN) {
			$OGL->EnterSelectionMode($x, $y, 10, 10);

#もう一度シーンを描画する
			$OGL->PushMatrix();
			$OGL->OnDrawForMouseZoom();
			$OGL->OnDrawForMouseTranslation();
			$OGL->OnDrawForMouseRotation();
			&scene($OGL, $COGL);
			$OGL->PopMatrix();

			my $hits = $OGL->ExitSelectionMode();
print "hits = $hits\n";

			my @SelectedObjects = $OGL->GetSelectedObjects();
			for (my $i = 0; $i < @SelectedObjects ; $i++) {
				my $pObj  = $SelectedObjects[$i];
				my $near  = $pObj->{Zmin};
				my $far   = $pObj->{Zmax};
				my ($ox, $oy, $oz) = $OGL->Get3DPosition($x, $y);
				my $piObj = $pObj->{piObjects};
				my $nObj  = @$piObj;
print "  n[$i]=$nObj: Z=$near, $far ($pObj->{piObjects})\n";
print "      ($ox, $oy, $oz)\n";
				for (my $j = 0; $j < $nObj ; $j++) {
					my $iObj = $piObj->[$j];
print "      iObj(blue)[$j]=$iObj\n";
#					$material[$iObj]->{-Diffuse} = $blue;
				}
			}
		}
		else {
#SelectionBufferの内容はマウスの左ボタンをクリックした時にしか変更されないので，まだ残っている
			my @SelectedObjects = $OGL->GetSelectedObjects();
			for(my $i = 0; $i < @SelectedObjects ; $i++) {
				my $pObj  = $SelectedObjects[$i];
				my $piObj = $pObj->{piObjects};
				my $nObj  = @$piObj;
print "  n[$i]=$nObj ($pObj->{piObjects})\n";
				for (my $j = 0 ; $j < $nObj ; $j++) {
					my $iObj = $piObj->[$j];
print "      iObj(gray)[$j]=$iObj\n";
#					$material[$iObj]->{-Diffuse} = $gray;
				}
			}
		}
		$OGL->Redraw();
	}

	if($button == GLUT_LEFT_BUTTON) {
		if($state == GLUT_DOWN) {
			if($OGL->{KeyMouseMode} eq 'zoom') {
				if($OGL->IsCtrlPressed()) {
					$OGL->OnMouseDownForMouseRotation($x, $y);
				}
				else {
					$OGL->OnMouseDownForMouseZoom($x, $y);
				}
			}
			else {
				if($OGL->IsCtrlPressed()) {
					$OGL->OnMouseDownForMouseZoom($x, $y);
				}
				else {
					$OGL->OnMouseDownForMouseRotation($x, $y);
				}
			}
			$OGL->{MouseMode} = 'rot';
		}
		elsif($state == GLUT_UP) {
			if($OGL->{KeyMouseMode} eq 'zoom') {
				$OGL->OnMouseUpForMouseZoom($x, $y);
			}
			else {
				$OGL->OnMouseUpForMouseRotation($x, $y);
			}
			$OGL->{MouseMode} = '';
		}
	}
	elsif($button == GLUT_RIGHT_BUTTON) {
		if($state == GLUT_DOWN) {
			$OGL->OnMouseDownForMouseTranslation($x, $y);
			$OGL->{MouseMode} = 'tr';
		}
		elsif($state == GLUT_UP) {
			$OGL->OnMouseUpForMouseTranslation($x, $y);
			$OGL->{MouseMode} = '';
		}
	}
	$OGL->Redraw();
}

sub resize
{
	my ($OGL, $w, $h) = @_;

#  /* ウィンドウ全体をビューポートにする */
	$OGL->Viewport(0, 0, $w, $h);

#  /* 透視変換行列を設定する */
#	$OGL->MatrixMode(GL_PROJECTION);
#	$OGL->InitializeMatrix();
#	$OGL->Perspective(30.0, $w / $h, 1.0, 100.0);
	$OGL->OnResizeForMouseZoom($w, $h);
	$OGL->OnResizeForMouseTranslation($w, $h, 5.0);
	$OGL->OnResizeForMouseRotation($w, $h);

#  /* モデルビュー変換行列を指定しておく */
	$OGL->MatrixMode(GL_MODELVIEW);
}

sub scene
{
	my ($OGL, $COGL) = @_;

	$COGL->Draw($OGL, $Crystal, $nx, $ny, $nz, $MaxBondLength, $nMaxBonds, $IonRadiusScaleFactor);
}

sub display {
	my ($OGL) = @_;

	$OGL->OnDisplayForMouseTranslation();
	$OGL->OnDisplayForMouseRotation();

	$OGL->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#	$OGL->LookAt(0.0, 0.0, -10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	$OGL->OnDisplayForMouseZoom();

	$OGL->PushMatrix();
	$OGL->OnDrawForMouseZoom();
	$OGL->OnDrawForMouseTranslation();
	$OGL->OnDrawForMouseRotation();
	&scene($OGL, $COGL);
	$OGL->PopMatrix();

	$OGL->Flush();
}

sub InitializeOpenGL
{
	my ($OGL) = @_;

	$OGL->ClearColor(0.5, 1.0, 1.0, 0.0);
	$OGL->Enable(GL_DEPTH_TEST, GL_CULL_FACE, GL_LIGHT0, GL_LIGHTING);
#$OGL->OnDisplayForMouseZoom()の初期化のために呼んでいるだけ
	$OGL->LookAt(0.0, 0.0, -10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	$OGL->MatrixMode(GL_PROJECTION);
	$OGL->InitializeMatrix();
	$OGL->Perspective(30.0, $WindowWidth / $WindowHeight, 1.0, 100.0);
	$OGL->MatrixMode(GL_MODELVIEW);
}
