package Physics::PVD::Interface::LAMMPS;
use strict;
use warnings;
use Carp;
use File::Temp qw(tempfile);
use File::Spec;

# ═══════════════════════════════════════════════════════════════════════════════
# Interface to LAMMPS for advanced PVD molecular dynamics
#
# Uses LAMMPS to perform:
#   - Classical MD of sputtering impact cascades
#   - Film growth with realistic interatomic potentials (EAM, MEAM, Tersoff)
#   - Substrate heating and thermal effects
#   - Stress evolution during deposition
#   - Adatom mobility on realistic surfaces
# ═══════════════════════════════════════════════════════════════════════════════

sub new {
    my ($class, %opts) = @_;
    my $self = bless {
        executable  => $opts{executable}  // 'lmp',
        mpi_cmd     => $opts{mpi_cmd}     // 'mpirun',
        n_procs     => $opts{n_procs}     // 1,
        work_dir    => $opts{work_dir}    // './lammps_pvd',
        potential_dir => $opts{potential_dir} // './potentials',

        # Simulation parameters
        timestep    => $opts{timestep}    // 1.0,     # fs
        temperature => $opts{temperature} // 300,     # K
        pressure    => $opts{pressure}    // 0,       # bar (0 for NVT)

        # Substrate
        substrate_material => $opts{substrate_material} // 'Cu',
        substrate_size     => $opts{substrate_size}     // [10, 10, 5],  # unit cells
        substrate_orient   => $opts{substrate_orient}   // [1, 0, 0],   # surface normal

        # Potential
        potential_type => $opts{potential_type} // 'eam/alloy',
        potential_file => $opts{potential_file} // '',

        # Deposition
        deposit_species => $opts{deposit_species} // 'Ta',
        deposit_energy  => $opts{deposit_energy}  // 5.0,    # eV
        deposit_rate    => $opts{deposit_rate}    // 1,       # atoms per N timesteps
        deposit_interval => $opts{deposit_interval} // 1000, # timesteps between deposits
        deposit_angle   => $opts{deposit_angle}   // 0,      # degrees from normal

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

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

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

# Generate LAMMPS input script for PVD deposition
sub generate_input {
    my ($self, %opts) = @_;
    my $template = $opts{template} // 'deposition';
    my $filename = $opts{filename} // "$self->{work_dir}/in.pvd";
    my $params   = $opts{params}   // {};

    # Merge default params
    my %p = (
        timestep    => $self->{timestep},
        temperature => $self->{temperature},
        n_deposits  => $params->{n_deposits}  // 100,
        run_between => $params->{run_between} // $self->{deposit_interval},
        total_steps => $params->{total_steps} // 100000,
        %$params,
    );

    my $script;
    if ($template eq 'deposition') {
        $script = $self->_template_deposition(%p);
    } elsif ($template eq 'sputtering') {
        $script = $self->_template_sputtering(%p);
    } elsif ($template eq 'annealing') {
        $script = $self->_template_annealing(%p);
    } else {
        croak "Unknown LAMMPS template: $template";
    }

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

    return $filename;
}

sub _template_deposition {
    my ($self, %p) = @_;
    my ($sx, $sy, $sz) = @{$self->{substrate_size}};
    my $pot_type = $self->{potential_type};
    my $pot_file = $self->{potential_file};
    my $sub_mat  = $self->{substrate_material};
    my $dep_mat  = $self->{deposit_species};
    my $dep_E    = $self->{deposit_energy};
    my $angle    = $self->{deposit_angle};
    my $T        = $p{temperature};
    my $dt       = $p{timestep};
    my $n_dep    = $p{n_deposits};
    my $interval = $p{run_between};

    return <<EOF;
# LAMMPS PVD Deposition Script — Generated by Physics::PVD
# Substrate: $sub_mat, Deposit: $dep_mat, T=$T K

units           metal
atom_style      atomic
boundary        p p f

# Create substrate lattice
lattice         fcc 3.615
region          box block 0 $sx 0 $sy 0 $sz
create_box      2 box
create_atoms    1 box

# Create deposition region above substrate
region          deposit block 0 $sx 0 $sy $sz @{[$sz+20]} units lattice

# Interatomic potential
pair_style      $pot_type
pair_coeff      * * $pot_file $sub_mat $dep_mat

# Groups
group           substrate type 1
group           mobile type 2

# Thermalization of substrate
velocity        substrate create $T 12345 dist gaussian
fix             thermostat substrate nvt temp $T $T 100.0
fix             freeze_bot substrate setforce 0.0 0.0 0.0

# Reflecting wall at top
fix             wall all wall/reflect zhi EDGE

# Time integration
timestep        $dt
fix             integrate mobile nve

# Output
thermo          1000
thermo_style    custom step temp pe ke etotal press vol atoms
dump            film all atom 5000 $self->{work_dir}/dump.pvd.lammpstrj

# Deposition loop
variable        n_deposited equal 0
label           deposit_loop
variable        dep_x equal random(0,$sx*3.615,12345)
variable        dep_y equal random(0,$sy*3.615,23456)
variable        dep_z equal ($sz+2)*3.615
variable        vz equal -sqrt(2*$dep_E*1.602e-19/(180.95*1.66e-27))*1e-5

create_atoms    2 single \${dep_x} \${dep_y} \${dep_z}
group           mobile type 2
velocity        mobile set 0.0 0.0 \${vz} units box

run             $interval

# Repeat deposition
variable        n_deposited equal \${n_deposited}+1
if              "\${n_deposited} < $n_dep" then "jump SELF deposit_loop"

# Final relaxation
run             @{[$interval * 5]}

write_data      $self->{work_dir}/final.data
EOF
}

sub _template_sputtering {
    my ($self, %p) = @_;
    my $T  = $p{temperature};
    my $dt = $p{timestep};
    my $ion_energy = $p{ion_energy} // 500;  # eV

    return <<EOF;
# LAMMPS Sputtering Simulation — Generated by Physics::PVD
# Ion bombardment to study sputter yield and cascade dynamics

units           metal
atom_style      atomic
boundary        p p s

lattice         bcc 3.3
region          target block 0 10 0 10 0 8
create_box      2 target
create_atoms    1 box

pair_style      $self->{potential_type}
pair_coeff      * * $self->{potential_file} $self->{substrate_material} Ar

# Create Ar ion above target
create_atoms    2 single 5.0 5.0 30.0
group           ion type 2
group           target type 1

velocity        target create $T 54321
velocity        ion set 0.0 0.0 -@{[sqrt(2*$ion_energy*1.6e-19/(39.948*1.66e-27))*1e-5]}

fix             nve_all all nve
fix             temp_ctrl target langevin $T $T 100.0 48279

timestep        $dt
thermo          100
dump            cascade all atom 50 $self->{work_dir}/dump.sputter.lammpstrj

run             @{[$p{total_steps} // 10000]}
write_data      $self->{work_dir}/post_sputter.data
EOF
}

sub _template_annealing {
    my ($self, %p) = @_;
    my $T_start = $p{temperature};
    my $T_end   = $p{anneal_temp} // 600;  # K

    return <<EOF;
# LAMMPS Post-Deposition Annealing — Generated by Physics::PVD

units           metal
atom_style      atomic
boundary        p p f

read_data       $self->{work_dir}/final.data

pair_style      $self->{potential_type}
pair_coeff      * * $self->{potential_file} $self->{substrate_material} $self->{deposit_species}

# Ramp temperature for annealing
fix             anneal all nvt temp $T_start $T_end 100.0

timestep        $self->{timestep}
thermo          1000
dump            anneal all atom 5000 $self->{work_dir}/dump.anneal.lammpstrj

run             @{[$p{total_steps} // 50000]}

# Cool down
unfix           anneal
fix             cool all nvt temp $T_end $T_start 100.0
run             @{[$p{total_steps} // 50000]}

write_data      $self->{work_dir}/annealed.data
EOF
}

# Run LAMMPS with the given input file
sub run {
    my ($self, %opts) = @_;
    my $input = $opts{input_file} // "$self->{work_dir}/in.pvd";
    my $np    = $opts{n_procs}    // $self->{n_procs};

    croak "Input file not found: $input" unless -f $input;

    my $cmd;
    if ($np > 1) {
        $cmd = "$self->{mpi_cmd} -np $np $self->{executable} -in $input > $self->{work_dir}/log.lammps 2>&1";
    } else {
        $cmd = "$self->{executable} -in $input > $self->{work_dir}/log.lammps 2>&1";
    }

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

# Parse LAMMPS dump file and return atom positions
sub parse_dump {
    my ($self, $filename) = @_;
    $filename //= "$self->{work_dir}/dump.pvd.lammpstrj";

    croak "Dump file not found: $filename" unless -f $filename;

    my @frames;
    open my $fh, '<', $filename or croak "Cannot read $filename: $!";
    my $frame;
    while (<$fh>) {
        chomp;
        if (/^ITEM: TIMESTEP/) {
            push @frames, $frame if $frame;
            $frame = { timestep => undef, atoms => [] };
            $frame->{timestep} = <$fh> + 0;
        } elsif (/^ITEM: NUMBER OF ATOMS/) {
            $frame->{n_atoms} = <$fh> + 0;
        } elsif (/^ITEM: ATOMS/) {
            # Parse atom lines until next ITEM or EOF
            for (1 .. $frame->{n_atoms}) {
                my $line = <$fh>;
                last unless defined $line;
                chomp $line;
                my @f = split /\s+/, $line;
                push @{$frame->{atoms}}, {
                    id => $f[0], type => $f[1],
                    x => $f[2], y => $f[3], z => $f[4],
                };
            }
        }
    }
    push @frames, $frame if $frame;
    close $fh;

    return \@frames;
}

# Parse LAMMPS log file for thermodynamic data
sub parse_log {
    my ($self, $filename) = @_;
    $filename //= "$self->{work_dir}/log.lammps";

    croak "Log file not found: $filename" unless -f $filename;

    my @thermo;
    my $in_thermo = 0;
    my @headers;

    open my $fh, '<', $filename or croak "Cannot read $filename: $!";
    while (<$fh>) {
        chomp;
        if (/^Step\s/) {
            $in_thermo = 1;
            @headers = split /\s+/;
            next;
        }
        if ($in_thermo) {
            if (/^Loop time/) {
                $in_thermo = 0;
                next;
            }
            my @vals = split /\s+/;
            next unless @vals == @headers;
            my %row;
            $row{$headers[$_]} = $vals[$_] for 0 .. $#headers;
            push @thermo, \%row;
        }
    }
    close $fh;

    return \@thermo;
}

1;

__END__

=head1 NAME

Physics::PVD::Interface::LAMMPS - Interface to LAMMPS for advanced PVD MD

=head1 DESCRIPTION

Generates LAMMPS input scripts and manages molecular dynamics simulations
for PVD processes including deposition, sputtering cascades, and post-
deposition annealing. Supports EAM, MEAM, and Tersoff potentials.

=cut
