package Physics::PVD::Interface::QuantumATK;
use strict;
use warnings;
use Carp;
use File::Path qw(make_path);
use File::Spec;

# ═══════════════════════════════════════════════════════════════════════════════
# Interface to QuantumATK (formerly Atomistix ToolKit)
#
# Uses QuantumATK's Python API to perform:
#   - DFT calculations of surface binding energies
#   - Ab-initio MD for adatom dynamics on surfaces
#   - Electronic transport through thin films
#   - Sputter yield calculations from BCA (Binary Collision Approximation)
#   - Semi-empirical tight-binding for large-scale film models
# ═══════════════════════════════════════════════════════════════════════════════

sub new {
    my ($class, %opts) = @_;
    my $self = bless {
        python_path => $opts{python_path} // 'atkpython',   # QuantumATK Python interpreter
        atk_path    => $opts{atk_path}    // $ENV{QUANTUMATK_PATH} // '',
        work_dir    => $opts{work_dir}    // './quantumatk_pvd',
        n_procs     => $opts{n_procs}     // 1,
        license     => $opts{license}     // $ENV{QUANTUMATK_LICENSE} // '',

        # Calculation settings
        calculator  => $opts{calculator}  // 'DFTB',  # DFT | DFTB | ForceField | SemiEmpirical
        xc_functional => $opts{xc_functional} // 'GGA-PBE',
        basis_set   => $opts{basis_set}   // 'DZP',
        k_points    => $opts{k_points}    // [4, 4, 1],
        energy_cutoff => $opts{energy_cutoff} // 150,  # Hartree (for DFT)

        # Structure
        material    => $opts{material}    // 'Ta',
        surface     => $opts{surface}     // [1, 1, 0],  # Miller indices
        slab_layers => $opts{slab_layers} // 6,
        vacuum      => $opts{vacuum}      // 15,  # Angstrom

        verbose => $opts{verbose} // 0,
    }, $class;

    mkdir $self->{work_dir} unless -d $self->{work_dir};
    return $self;
}

# Check if QuantumATK is available
sub check_availability {
    my ($self) = @_;
    my $output = `which $self->{python_path} 2>/dev/null`;
    chomp $output;
    return $output ? 1 : 0;
}

# Generate Python script for surface binding energy calculation
sub calculate_binding_energy {
    my ($self, %opts) = @_;
    my $adatom   = $opts{adatom}   // $self->{material};
    my $site     = $opts{site}     // 'hollow';  # hollow | bridge | atop
    my $filename = $opts{filename} // "$self->{work_dir}/binding_energy.py";

    my $material = $self->{material};
    my $surface  = join(',', @{$self->{surface}});
    my $calc     = $self->{calculator};
    my $xc       = $self->{xc_functional};
    my $kpts     = join(',', @{$self->{k_points}});
    my $layers   = $self->{slab_layers};
    my $vacuum   = $self->{vacuum};

    my $script = <<PYTHON;
# QuantumATK: Surface binding energy calculation
# Generated by Physics::PVD::Interface::QuantumATK
from QuantumATK import *
import numpy as np

# Build surface slab
bulk = BulkConfiguration(
    bravais_lattice=FaceCenteredCubic(3.3058*Angstrom),
    elements=[${material}],
    fractional_coordinates=[[0.0, 0.0, 0.0]]
)

surface = SlabConfiguration(
    bulk,
    surface_normal=[$surface],
    number_of_layers=$layers,
    vacuum=$vacuum*Angstrom
)

# Calculator setup
calculator = LCAOCalculator(
    exchange_correlation=$xc.${xc},
    basis_set=getattr(${calc}BasisSet, '$self->{basis_set}'),
    k_point_sampling=MonkhorstPackGrid($kpts),
)

# Calculate clean surface energy
surface.setCalculator(calculator)
E_slab = TotalEnergy(surface).evaluate().inUnitsOf(eV)

# Add adatom at $site site
adatom_pos = surface.cartesianCoordinates()[-1] + np.array([0, 0, 2.5])*Angstrom
surface_with_adatom = surface.copy()
surface_with_adatom.addAtoms(
    elements=[${adatom}],
    cartesian_coordinates=[adatom_pos]
)

# Optimize adatom position
optimizer = QuasiNewtonOptimizer(max_forces=0.01*eV/Angstrom)
surface_with_adatom = optimizer.optimize(surface_with_adatom)

E_with_adatom = TotalEnergy(surface_with_adatom).evaluate().inUnitsOf(eV)

# Isolated adatom
isolated = MoleculeConfiguration(elements=[${adatom}], ...)
isolated.setCalculator(calculator)
E_isolated = TotalEnergy(isolated).evaluate().inUnitsOf(eV)

# Binding energy
E_binding = E_slab + E_isolated - E_with_adatom
print(f"Binding energy ({adatom} on {material}({surface}) $site): {E_binding:.3f} eV")

# Save results
nlsave('$self->{work_dir}/binding_results.hdf5', surface_with_adatom)
PYTHON

    open my $fh, '>', $filename or croak "Cannot write $filename: $!";
    print $fh $script;
    close $fh;

    return $filename;
}

# Generate script for sputtering simulation (BCA + MD)
sub setup_sputtering {
    my ($self, %opts) = @_;
    my $ion         = $opts{ion}         // 'Ar';
    my $ion_energy  = $opts{ion_energy}  // 500;   # eV
    my $angle       = $opts{angle}       // 0;     # degrees
    my $n_ions      = $opts{n_ions}      // 100;
    my $filename    = $opts{filename}    // "$self->{work_dir}/sputtering.py";

    my $material = $self->{material};

    my $script = <<PYTHON;
# QuantumATK: Sputtering yield simulation (MD + BCA)
# Generated by Physics::PVD::Interface::QuantumATK
from QuantumATK import *
import numpy as np

# Build target surface
bulk = BulkConfiguration(
    bravais_lattice=BodyCenteredCubic(3.3058*Angstrom),
    elements=[$material],
    fractional_coordinates=[[0.0, 0.0, 0.0]]
)

target = SlabConfiguration(bulk, surface_normal=[1,1,0],
                           number_of_layers=12, vacuum=30*Angstrom)

# MD Calculator (Tersoff or EAM potential)
calculator = TersoffCalculator(target)
target.setCalculator(calculator)

# Ion bombardment parameters
ion_energy = $ion_energy * eV
ion_angle = $angle * np.pi / 180.0
n_ions = $n_ions

sputtered_atoms = 0
for i in range(n_ions):
    # Random impact point
    x = np.random.uniform(0, target.bravaisLattice().a())
    y = np.random.uniform(0, target.bravaisLattice().b())
    z_top = max(target.cartesianCoordinates()[:, 2]) + 5.0*Angstrom

    # Ion velocity
    mass_ion = {'Ar': 39.948, 'Ne': 20.18, 'Xe': 131.29}['$ion'] * amu
    speed = np.sqrt(2 * ion_energy / mass_ion)
    vx = speed * np.sin(ion_angle)
    vy = 0.0
    vz = -speed * np.cos(ion_angle)

    # Add ion and run MD
    config = target.copy()
    config.addAtoms(elements=['$ion'], cartesian_coordinates=[[x, y, z_top]])
    config.setVelocities([vx, vy, vz], indices=[-1])

    md = MolecularDynamics(
        configuration=config,
        trajectory_filename='$self->{work_dir}/cascade_{}.hdf5'.format(i),
        steps=2000,
        time_step=0.5*fs,
        log_interval=100
    )
    md.run()

    # Count sputtered atoms (z > z_top)
    final = md.finalConfiguration()
    z_final = final.cartesianCoordinates()[:, 2]
    sputtered_atoms += np.sum(z_final > z_top)

sputter_yield = sputtered_atoms / n_ions
print(f"Sputter yield ({ion} -> {material}, E={ion_energy} eV, θ={angle}°): {sputter_yield:.3f}")
PYTHON

    open my $fh, '>', $filename or croak "Cannot write $filename: $!";
    print $fh $script;
    close $fh;

    return $filename;
}

# Generate script for adatom diffusion MD
sub run_md {
    my ($self, %opts) = @_;
    my $temperature = $opts{temperature} // 600;  # K
    my $steps       = $opts{steps}       // 50000;
    my $filename    = $opts{filename}    // "$self->{work_dir}/adatom_md.py";

    my $material = $self->{material};
    my $calc = $self->{calculator};

    my $script = <<PYTHON;
# QuantumATK: Adatom diffusion MD on $material surface
# Generated by Physics::PVD::Interface::QuantumATK
from QuantumATK import *
import numpy as np

# Build surface + adatom
bulk = BulkConfiguration(
    bravais_lattice=BodyCenteredCubic(3.3058*Angstrom),
    elements=[$material],
    fractional_coordinates=[[0.0, 0.0, 0.0]]
)

slab = SlabConfiguration(bulk, surface_normal=[1,1,0],
                         number_of_layers=$self->{slab_layers}, vacuum=20*Angstrom)

# Place adatom
top_z = max(slab.cartesianCoordinates()[:, 2])
center_xy = np.mean(slab.cartesianCoordinates()[:, :2], axis=0)
slab.addAtoms(elements=['$material'],
              cartesian_coordinates=[[center_xy[0], center_xy[1], top_z + 2.5]])

# Calculator
calculator = ${calc}Calculator(slab)
slab.setCalculator(calculator)

# NVT MD
md = MolecularDynamics(
    configuration=slab,
    trajectory_filename='$self->{work_dir}/adatom_diffusion.hdf5',
    steps=$steps,
    time_step=1.0*fs,
    thermostat=NoseHooverThermostat(temperature=$temperature*Kelvin),
    log_interval=100
)
md.run()

# Analyze diffusion: MSD of adatom
traj = Trajectory('$self->{work_dir}/adatom_diffusion.hdf5')
positions = [frame.cartesianCoordinates()[-1][:2] for frame in traj]
msd = [np.sum((p - positions[0])**2) for p in positions]
D = msd[-1] / (4 * len(msd) * 1.0*fs)  # 2D diffusion coefficient
print(f"Diffusion coefficient at {$temperature} K: {D:.3e} cm²/s")
PYTHON

    open my $fh, '>', $filename or croak "Cannot write $filename: $!";
    print $fh $script;
    close $fh;

    return $filename;
}

# Execute a QuantumATK Python script
sub run_script {
    my ($self, $script_file) = @_;
    croak "Script not found: $script_file" unless -f $script_file;

    my $cmd;
    if ($self->{n_procs} > 1) {
        $cmd = "mpirun -np $self->{n_procs} $self->{python_path} $script_file";
    } else {
        $cmd = "$self->{python_path} $script_file";
    }
    $cmd .= " > $self->{work_dir}/log.atk 2>&1";

    print "  Running QuantumATK: $cmd\n" if $self->{verbose};
    my $rc = system($cmd);
    croak "QuantumATK run failed (exit: $rc)" if $rc != 0;
    return $self;
}

# Parse results from output log
sub get_results {
    my ($self) = @_;
    my $logfile = "$self->{work_dir}/log.atk";
    return {} unless -f $logfile;

    my %results;
    open my $fh, '<', $logfile or return \%results;
    while (<$fh>) {
        if (/Binding energy.*?:\s*([\d.+-e]+)\s*eV/) {
            $results{binding_energy_eV} = $1 + 0;
        }
        if (/Sputter yield.*?:\s*([\d.+-e]+)/) {
            $results{sputter_yield} = $1 + 0;
        }
        if (/Diffusion coefficient.*?:\s*([\d.+-e]+)/) {
            $results{diffusion_coeff} = $1 + 0;
        }
    }
    close $fh;
    return \%results;
}

1;

__END__

=head1 NAME

Physics::PVD::Interface::QuantumATK - Interface to QuantumATK for ab-initio PVD

=head1 DESCRIPTION

Generates QuantumATK Python scripts for first-principles calculations
relevant to PVD: surface binding energies, sputter yields, adatom
diffusion barriers, and electronic transport through deposited films.

=cut
