#!/usr/bin/perl

BEGIN {
use lib 'c:/Programs/Perl/lib';
use lib 'd:/Programs/Perl/lib';
#use lib '/home/tkamiya/bin/lib';
my $BaseDir = $ENV{'TkPerlDir'};
print "\n\nBaseDir: $BaseDir\n";
@INC = ("$BaseDir/lib", "$BaseDir/GULP", @INC);
#use lib "$BaseDir/lib";
#use lib "$BaseDir/GULP";
}

use strict;
#use warnings;
use File::Basename;

use Math::Matrix;
use Math::MatrixReal;

use Deps;
use Utils;

use ProgVars;
use MyApplication;
use Sci::GeneralFileFormat;

use Crystal::CIF;
use Crystal::Crystal;
use Crystal::SpaceGroup;
use Crystal::AtomType;
use Crystal::LAMMPS;
#use Crystal::GULP;

#===============================================
# デバッグ関係変数
#===============================================
#$PrintLevelが大きいほど、情報が詳しくなる
my $PrintLevel = 0;

#===============================================
# 大域変数
#===============================================
my $HOME = $ENV{'HOME'};
my $ProgramDir        = ProgVars::ProgramDir();
my $LAMMPSPerlDir     = ProgVars::LAMMPSPerlDir();
my $TemplateDir       = Deps::MakePath($LAMMPSPerlDir, 'Template', 0);
my $LAMMPSDatabaseDir = Deps::MakePath($LAMMPSPerlDir, 'Potentials', 0);
my $GULPDir           = ProgVars::GULPDir(); #Deps::MakePath($HOME, "gulp", 0);
my $GULPLibDir        = Deps::MakePath($GULPDir, "Libraries", 0);
my @PotentialLibs     = ForceField::PotentialLibs();

#===============================================
# 文字コード関係変数
#===============================================
# sjis, euc, jis, noconv
my $PrintCharCode      = Deps::PrintCharCode();
my $OSCharCode         = Deps::OSCharCode();
my $FileSystemCharCode = Deps::FileSystemCharCode();

my $LF        = Deps::LF();
my $DirSep    = Deps::DirSep();
my $RegDirSep = Deps::RegDirSep();

#===============================================
# Applicationオブジェクト作成
#===============================================
my $App = new MyApplication;
exit if($App->Initialize() < 0);

#$App->SetLF("<br>\n");
#$App->SetPrintCharCode("sjis");
#$App->SetDebug($Debug);
$App->SetDeleteHTMLFlag(1);

#===============================================
# スクリプト大域変数
#===============================================
my $InitialDirectory = Deps::GetWorkingDirectory();

#==========================================
# コマンドラインオプション読み込み
#==========================================
$App->AddArgument("--Action",        "--Action=[ShowDatabases|FindDatabases|MakeInput|ConvCustomDump]",       '');
$App->AddArgument("--Function",      "--Function=[Optimize|Fit|MD-NVT|MD-NPT] (Def:Optimize)", 'Optimize');
$App->AddArgument("--PotentialType", "--PotentialType=[buck] (Def:)", '');
$App->AddArgument("--CopyScript",    "--CopyScript=[0|1] (Def:0)",     0);
$App->AddArgument("--UseShellModelForCation", "--UseShellModelForCation=[0|1] (Def:0)",   0);
$App->AddArgument("--UseShellModelForAnion",  "--UseShellModelForAnion=[0|1]  (Def:0)",   0);
$App->AddArgument("--gewald",       "--gewald=[|val] (Def:)", '');
$App->AddArgument("--LibraryFile",  "--LibraryFile=[Kamiya.lib|Kamiya-NoShell.lib|bush.lib|catlow.lib|"
				   ."lewis.lib|suttonchen.lib|glass.lib] (Def:0)",   "Kamiya-NoShell.lib");
$App->AddArgument("--DebugMode", "--DebugMode: Set DebugMode", '');
exit 1 if($App->ReadArgs(0) != 1);
my $Args = $App->Args();

#==========================================
# メイン関数スタート
#==========================================

#Utils::InitHTML("Research", $WebCharSet, "_self");

my $Debug = $Args->GetGetArg("DebugMode");
$App->SetDebug($Debug);
my $Action = $Args->GetGetArg("Action");

if($Action =~ /MakeInput/i) {
	&MakeInputFile();
}
elsif($Action =~ /ShowDatabases/i) {
	&ShowDatabases();
}
elsif($Action =~ /FindDatabases/i) {
	&FindDatabases();
}
elsif($Action =~ /ConvCustomDump/i) {
	&ConvCustomDump();
}
else {
	$App->print("Error: Invald Action: $Action\n");
}

#Utils::EndHTML();

exit;

#===============================================
# スクリプト終了
#===============================================

#==========================================
# &Subroutines
#==========================================

sub ConvCustomDump
{
	$App->print("\n\nMake CIF/XSF/CSV files from custom dump and log files:\n");

	my $InFile         = $Args->GetGetArg(0);
	my $DataFile       = $Args->GetGetArg(1);
	my $LogFile        = $Args->GetGetArg(2);
	my $DumpFile       = $Args->GetGetArg(3);
	my $InitialCIFFile = $Args->GetGetArg(4);
	my $FinalCIFFile   = $Args->GetGetArg(5);
	my $XSFFile        = $Args->GetGetArg(6);
	my $LogHistoryFile = $Args->GetGetArg(7);
	my $HistoryFile    = $Args->GetGetArg(8);

	$App->print("  Read initial crystal structure from [$InFile] and [$DataFile].\n");
	$App->print("  Read output log                from [$LogFile].\n");
	$App->print("  Read Relaxation/MD data        from [$DumpFile].\n");
	$App->print("  Save Initial structure         to $InitialCIFFile.\n");
	$App->print("  Save Optimized structure       to $FinalCIFFile.\n");
	$App->print("  Save Relaxation/MD animation   to $XSFFile.\n");
	$App->print("  Save log history               to $LogHistoryFile.\n");
	$App->print("  Save Relaxation/MD crystal structure history to $HistoryFile.\n");

	unless($HistoryFile) {
		$App->print("File name should be specified.\n");
		$App->print("  usage: LAMMPS.pl --Action=ConvCustomDump InFile DataFile DumpFile InitialCIFFile FinalCIFFile XSFFile HistoryCSVFile.\n");
		return 0;
	}

	my $LAMMPS = new LAMMPS;
	my $IniCrystal = $LAMMPS->ReadCrystalStructureFromInput($InFile, $DataFile);

	print "Delete old [$InitialCIFFile]..\n";
	print "Delete old [$FinalCIFFile]..\n";
	print "Delete old [$XSFFile]..\n";
	print "Delete old [$LogHistoryFile]..\n";
	print "Delete old [$HistoryFile]..\n";
	unlink($InitialCIFFile);
	unlink($FinalCIFFile);
	unlink($XSFFile);
	unlink($LogHistoryFile);
	unlink($HistoryFile);

	my $pLogHashArray = $LAMMPS->ReadLogFileToHashArray($LogFile);
	my $p = $pLogHashArray->[0];
#print "hash: ", join(', ', keys %$p), "\n";
#exit;
	my (@step, @time, @temp);
	my $c = 0;
	for(my $i = 0 ; $i < @$pLogHashArray ; $i++) {
		my $p = $pLogHashArray->[$i];
		my $ps = $p->{Step};
		for(my $j = 0 ; $j < @$ps ; $j++) {
			$step[$c] = $p->{Step}[$j];
			$time[$c] = $p->{Time}[$j];
			$temp[$c] = $p->{Temp}[$j];
			$c++;
		}
	}
#my $out = JFile->new("temp.csv", 'w');
#$out->print("step,time,temp\n");
#for(my $i = 0 ; $i < @step ; $i++) {
#$out->print(" $step[$i],$time[$i],$temp[$i]\n");
#}
#$out->Close();
#exit;

	$LAMMPS->SaveHistoryFromLog($LogFile, $LogHistoryFile);

	my $CIF = new CIF;
	print "Save initial crystal structure to [$InitialCIFFile]..\n";
	my $ret = $CIF->CreateCIFFileFromCCrystal($IniCrystal, $InitialCIFFile, 0);
	if(!$ret) {
		print "Error: Can not write to [$InitialCIFFile].\n";
		exit;
	}

	my $FinalCrystal = $LAMMPS->MakeXSFFromCustomDump(\@step, \@time, \@temp, $DumpFile, $XSFFile, $IniCrystal, $HistoryFile);

	print "Save final crystal structure to [$FinalCIFFile]..\n";
	my $ret = $CIF->CreateCIFFileFromCCrystal($FinalCrystal, $FinalCIFFile, 0);
	if(!$ret) {
		print "Error: Can not write to [$FinalCIFFile].\n";
		exit;
	}

#	unless($InitialCrystalCIF->WriteSimpleCIFFile($InitialFile)) {
#		$App->print("  Error: Could not write to ($InitialFile).\n");
#		return -1;
#	}
}

sub MakeInputFile
{
	my ($this, $IsPrint) = @_;
	$IsPrint = 0 if(!defined $IsPrint);

	$App->print("\n\nMake LAMMPS input files:\n");

	my $Function               = $Args->GetGetArg("Function");
	my $UseShellModelForCation = $Args->GetGetArg("UseShellModelForCation");
	my $UseShellModelForAnion  = $Args->GetGetArg("UseShellModelForAnion");
	my $gewald                 = $Args->GetGetArg("gewald");
	my $LibraryFile            = $Args->GetGetArg("LibraryFile");
	my $CopyScript             = $Args->GetGetArg("CopyScript");
	$CopyScript = 0 if(!defined $CopyScript);
	$Function = "Optimize" unless($Function);
	$UseShellModelForCation = 0 unless($UseShellModelForCation);
	$UseShellModelForAnion  = 0 unless($UseShellModelForAnion);
	$gewald = '' if(!defined $gewald);
	$LibraryFile = "Kamiya-NoShell.lib" unless($LibraryFile);

	my $CIFFile  = $Args->GetGetArg(0);
	my $InFile   = $Args->GetGetArg(1);
	my $DataFile = $Args->GetGetArg(2);
	unless($DataFile) {
		$App->print("File name should be specified.\n");
		$App->print("  usage: LAMMPS.pl --Action=MakeInput CIFFile .in_File .data_Filie.\n");
		return 0;
	}
	my $ScriptPath = Deps::MakePath($TemplateDir, 'GoLAMMPS.bat', 0);

#ファイル名を（ベース名, ディレクトリ名, 拡張子）に分解
	my ($drive, $dir, $filename, $ext, $lastdir, $filebody)
		= Deps::SplitFilePath($CIFFile);
	my $SampleName = $filebody;

	$App->print("  Read $CIFFile.\n");
	$App->print("  Save .in file to $InFile.\n");
	$App->print("  Save .data file to $DataFile.\n");
	$App->print("  Copy script from [$ScriptPath].\n");
	$App->print("  Function              : $Function\n");
#	$App->print("  UseShellModelForCation: $UseShellModelForCation\n");
#	$App->print("  UseShellModelForAnion : $UseShellModelForAnion\n");
	$App->print("  LibraryFile           : $LibraryFile in [$GULPLibDir]\n");
	$App->print("  gewald                : $gewald\n");
	$App->print("  Copy script file      : $CopyScript\n");

	$App->print("    Read [$CIFFile].\n");

	my $CIF = new CIF();
	unless($CIF->Read($CIFFile)) {
		$App->print("Error: Can not read $CIFFile.\n\n");
		return 0;
	}

#CIFクラスの内容から、Crystalクラスを作成
	my $Crystal = $CIF->GetCCrystal();
	$Crystal->ExpandCoordinates();

#	$App->print("    Save to [$InFile] and [$DataFile].\n");
	my $LAMMPS = new LAMMPS;
	$LAMMPS->SetLibraryDir($GULPLibDir);
	$LAMMPS->SetSampleName($SampleName);
	$LAMMPS->SetUseShellModelForCation($UseShellModelForCation);
	$LAMMPS->SetUseShellModelForAnion($UseShellModelForAnion);
	for(my $i = @PotentialLibs - 1 ; $i >= 0  ; $i--) {
		my $path = Deps::MakePath($PotentialLibs[$i], $LibraryFile, 0);
		if(-f $path) {
			$LibraryFile = $path;
			last;
		}
	}
	if(-f $LibraryFile) {
		$LAMMPS->SetLibraryFile($LibraryFile);
	}
	else {
		print "Error: Can not find library [$LibraryFile].\n";
		return;
	}
	$LAMMPS->SaveInputFiles($Crystal, $Function, $InFile, $DataFile, $CopyScript, $ScriptPath, 0,
		gewald => $gewald,
		);

	$App->print("\n***Make input files Finished.\n\n");

	return;
}

sub FindDatabases
{
	my ($this, $IsPrint) = @_;
	$IsPrint = 0 if(!defined $IsPrint);

	my $PotentialType               = $Args->GetGetArg("PotentialTyp");
	my $CIFFile  = $Args->GetGetArg(0);
	unless($CIFFile) {
		$App->print("File name should be specified.\n");
		$App->print("  usage: LAMMPS.pl --Action=FindDatabases --PotentialType=[buck|lennard] CIFFile\n");
		return 0;
	}

	$App->print("\nFind available potential databases from the chemical composition in [$CIFFile].\n");
	$App->print("  PotentialType: $PotentialType\n");
	$App->print("  Read $CIFFile...\n");

	my $CIF = new CIF();
	unless($CIF->Read($CIFFile)) {
		$App->print("Error: Can not read $CIFFile.\n\n");
		return 0;
	}

	my $LAMMPS = new LAMMPS;

#CIFクラスの内容から、Crystalクラスを作成
	my $Crystal = $CIF->GetCCrystal();
#	$Crystal->ExpandCoordinates();
	my @AtomTypeList           = $Crystal->GetCAtomTypeList();
	my $nAtomType              = @AtomTypeList;

	print "\n";
	print "Search databases for ";
	for(my $i = 0 ; $i < $nAtomType ; $i++) {
		my $name = $AtomTypeList[$i]->AtomNameOnly();
		print "$name ";
	}
	print "\n";

#	print "Databases in [$LAMMPSDatabaseDir] and [$GULPLibDir].\n" if($IsPrint);
#	my @files = sort glob(Deps::MakePath($LAMMPSDatabaseDir, '*', 0));
#	my @files = sort glob(Deps::MakePath($GULPLibDir, '*', 0));
	my @files;
	for(my $k = 0 ; $k < @PotentialLibs ; $k++) {
		my $dir = $PotentialLibs[$k];
		next if(!-d $dir);

	print "Search databases in [$dir]...\n";
		my @f = sort glob(Deps::MakePath($dir, '*', 0));
		for(my $i = 0 ; $i < @f ; $i++) {
			next if(!-f $f[$i]);
			next if($f[$i] =~ /\.htm/i);
			push(@files, $f[$i]);
		}
	}

	my $nHit = 0;
	for(my $i = 0 ; $i < @files ; $i++) {
		next if($files[$i] =~ /eledata$/i);
#next if($files[$i] !~ /kamiya/i);

#print "$files[$i]\n" if($IsPrint);
		my ($phash, $pAtomArray) = $LAMMPS->ReadPotentialToHash($files[$i], $IsPrint);
		my $AllFound = 1;
		for(my $i = 0 ; $i < $nAtomType ; $i++) {
			my $name = $AtomTypeList[$i]->AtomNameOnly();
			my $IsFound = 0;
			for(my $j = 0 ; $j < @$pAtomArray ; $j++) {
				if($name eq $pAtomArray->[$j]) {
					$IsFound = 1;
					last;
				}
			}
			if(!$IsFound) {
				$AllFound = 0;
				last;
			}
		}
		if($AllFound) {
			print "All potentials are found in [$files[$i]].\n";
			print "   Available atoms:";
			for(my $i = 0 ; $i < @$pAtomArray ; $i++) {
				print " $pAtomArray->[$i]";
			}
			print "\n\n";
			$nHit++;
		}
	}
	print "\n$nHit databases are found.\n";
	print "\n";
}

sub ShowDatabases
{
	my ($this, $IsPrint) = @_;
	$IsPrint = 1 if(!defined $IsPrint);
	
	print "\n";
	print "Databases in [$LAMMPSDatabaseDir] and [$GULPLibDir].\n" if($IsPrint);
	my @files = sort glob(Deps::MakePath($LAMMPSDatabaseDir, '*', 0));
#	for(my $i = 0 ; $i < @files ; $i++) {
#		print "  $files[$i]\n";
#	}
	print "\n";

	my $LAMMPS = new LAMMPS;
	for(my $k = 0 ; $k < @PotentialLibs ; $k++) {
		my $dir = $PotentialLibs[$k];
		next if(!-d $dir);

		print "\n";
		print "Search [$dir]...\n";
		my @files = sort glob(Deps::MakePath($dir, '*', 0));
		for(my $i = 0 ; $i < @files ; $i++) {
			next if(!-f $files[$i]);
			next if($files[$i] =~ /eledata$/i);
			next if($files[$i] =~ /\.htm/i);

			print "$files[$i]\n" if($IsPrint);
			my ($phash, $pAtomArray) = $LAMMPS->ReadPotentialToHash($files[$i], $IsPrint);
			foreach my $key (sort keys %$phash) {
				my $p2 = $phash->{$key};
				print "    $key: $p2->{Type} $p2->{nAtom} ";
				for(my $j = 1 ; $j <= $p2->{nAtom} ; $j++) {
					print " " . $p2->{"AtomName$j"};
				}
				print "\n";
			}
			print "    atoms: ", join(', ', @$pAtomArray), "\n";
		}
	}
}
