#! /bin/sh
#!perl -w # --*- Perl -*--
eval 'exec perl -x $0 ${1+"$@"}'
    if 0;
#------------------------------------------------------------------------------
#$Author: andrius $
#$Date: 2025-05-29 15:51:58 +0300 (Thu, 29 May 2025) $
#$Revision: 10608 $
#$URL: svn+ssh://www.crystallography.net/home/coder/svn-repositories/cod-tools/tags/v3.12.0/tools/pymatgen-to-cif $
#------------------------------------------------------------------------------
#*
#* Convert pymatgen structures from JSON format to CIF.
#*
#* USAGE:
#*    $0 --options structure1.json structure*.json
#**

use strict;
use warnings;

use COD::CIF::Tags::Manage qw( new_datablock set_loop_tag set_tag );
use COD::CIF::Tags::Print qw( print_cif );
use Clone qw( clone );
use File::Basename qw( basename );
use JSON qw( decode_json );
use List::Util qw( uniqstr );

for my $filename (@ARGV) {
    open my $inp, '<', $filename;
    my $json = join '', <$inp>;
    close $inp;

    my $struct = decode_json( $json );
    my @sites = @{$struct->{sites}};

    # Uniquify names if needed
    if( uniqstr( map { $_->{label} } @sites ) < @sites ) {
        for my $i (0..$#sites) {
            $sites[$i]->{label} .= $i + 1;
        }
    }
    @sites = split_occupational_disorder( @sites );

    my $cif = new_datablock( basename $filename, '.json' );

    for (qw(a b c)) {
        set_tag( $cif, '_cell_length_' . $_, $struct->{lattice}{$_} );
    }
    for (qw(alpha beta gamma)) {
        set_tag( $cif, '_cell_angle_' . $_, $struct->{lattice}{$_} );
    }
    set_tag( $cif, '_cell_volume', $struct->{lattice}{volume} );

    set_tag( $cif, '_space_group_name_H-M_alt', 'P 1' );
    set_loop_tag( $cif, '_space_group_symop_id', '_space_group_symop_id', [ 1 ] );
    set_loop_tag( $cif,
                  '_space_group_symop_operation_xyz',
                  '_space_group_symop_id',
                  [ 'x,y,z' ] );

    set_loop_tag( $cif, '_atom_site_label', '_atom_site_label',
                  [ map { $_->{label} } @sites ] );
    set_loop_tag( $cif, '_atom_site_type_symbol', '_atom_site_label',
                  [ map { $_->{species}{element} } @sites ] );
    set_loop_tag( $cif, '_atom_site_fract_x', '_atom_site_label',
                  [ map { $_->{abc}[0] } @sites ] );
    set_loop_tag( $cif, '_atom_site_fract_y', '_atom_site_label',
                  [ map { $_->{abc}[1] } @sites ] );
    set_loop_tag( $cif, '_atom_site_fract_z', '_atom_site_label',
                  [ map { $_->{abc}[2] } @sites ] );
    set_loop_tag( $cif, '_atom_site_occupancy', '_atom_site_label',
                  [ map { $_->{species}{occu} } @sites ] );
    set_loop_tag( $cif,
                  '_[local]_pymatgen_oxidation_state',
                  '_atom_site_label',
                  [ map { $_->{species}{oxidation_state} } @sites ] );

    set_loop_tag( $cif,
                  '_[local]_pymatgen_coordination_label',
                  '_[local]_pymatgen_coordination_label',
                  [ map { ( $_->{label} ) x scalar keys %{$_->{properties}{coordination}} } @sites ] );
    set_loop_tag( $cif,
                  '_[local]_pymatgen_coordination_element',
                  '_[local]_pymatgen_coordination_label',
                  [ map { sort keys %{$_->{properties}{coordination}} } @sites ] );
    set_loop_tag( $cif,
                  '_[local]_pymatgen_coordination_count',
                  '_[local]_pymatgen_coordination_label',
                  [ map { order_coordination_count( $_->{properties}{coordination} ) } @sites ] );

    print_cif( $cif );
}

sub split_occupational_disorder
{
    my @sites = @_;
    my @sites_now;
    for my $site (@sites) {
        if( @{$site->{species}} == 1 ) {
            ( $site->{species} ) = @{$site->{species}};
            push @sites_now, $site;
            next;
        }

        my $letter = 'A';
        for my $i (0..$#{$site->{species}}) {
            my $clone = clone $site;
            $clone->{label} .= $letter;
            $clone->{species} = {
                element         => $site->{species}[$i]{element},
                occu            => $site->{species}[$i]{occu},
                oxidation_state => $site->{species}[$i]{oxidation_state},
            };
            push @sites_now, $clone;
            $letter++;
        }
    }
    return @sites_now;
}

sub order_coordination_count
{
    my( $coordination ) = @_;
    return map { $coordination->{$_} } sort keys %$coordination;
}
