"""
@package pmsco.elements.bindingenergy
electron binding energies of the elements

extends the element table of the `periodictable` package
(https://periodictable.readthedocs.io/en/latest/index.html)
by the electron binding energies.

the binding energies are compiled from Gwyn Williams' web page
(https://userweb.jlab.org/~gwyn/ebindene.html).
please refer to the original web page or the x-ray data booklet
for original sources, definitions and remarks.

usage
-----

this module requires the periodictable package (https://pypi.python.org/pypi/periodictable).

~~~~~~{.py}
import periodictable as pt
import pmsco.elements.bindingenergy

# read any periodictable's element interfaces, e.g.
print(pt.gold.binding_energy['4f7/2'])
print(pt.elements.symbol('Au').binding_energy['4f7/2'])
print(pt.elements.name('gold').binding_energy['4f7/2'])
print(pt.elements[79].binding_energy['4f7/2'])
~~~~~~

note that attributes are writable.
you may assign refined values in your instance of the database.

the query_binding_energy() function queries all terms with a particular binding energy.


@author Matthias Muntwiler

@copyright (c) 2020 by Paul Scherrer Institut @n
Licensed under the Apache License, Version 2.0 (the "License"); @n
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0
"""

import json
import numpy as np
import os
import periodictable as pt
from pmsco.compat import open


index_energy = np.zeros(0)
index_number = np.zeros(0)
index_term = []


def load_data():
    data_path = os.path.join(os.path.dirname(__file__), "bindingenergy.json")
    with open(data_path) as fp:
        data = json.load(fp)
    return data


def init(table, reload=False):
    if 'binding_energy' in table.properties and not reload:
        return
    table.properties.append('binding_energy')

    pt.core.Element.binding_energy = {}
    pt.core.Element.binding_energy_units = "eV"

    data = load_data()
    for el_key, el_data in data.items():
        try:
            el = table[int(el_key)]
        except ValueError:
            el = table.symbol(el_key)
        el.binding_energy = el_data


def build_index():
    """
    build an index for query_binding_energy().

    the index is kept in global variables of the module.

    @return None
    """
    global index_energy
    global index_number
    global index_term

    n = 0
    for element in pt.elements:
        n += len(element.binding_energy)

    index_energy = np.zeros(n)
    index_number = np.zeros(n)
    index_term = []

    for element in pt.elements:
        for term, energy in element.binding_energy.items():
            index_term.append(term)
            i = len(index_term) - 1
            index_energy[i] = energy
            index_number[i] = element.number


def query_binding_energy(energy, tol=1.0):
    """
    search the periodic table for a specific binding energy and return all matching terms.

    @param energy: binding energy in eV.

    @param tol: tolerance in eV.

    @return: list of dictionaries containing element and term specification.
             the list is ordered arbitrarily.
             each dictionary contains the following keys:
             @arg 'number': element number
             @arg 'symbol': element symbol
             @arg 'term': spectroscopic term
             @arg 'energy': actual binding energy
    """
    if len(index_energy) == 0:
        build_index()
    sel = np.abs(index_energy - energy) < tol
    idx = np.where(sel)
    result = []
    for i in idx[0]:
        el_num = int(index_number[i])
        d = {'number': el_num,
             'symbol': pt.elements[el_num].symbol,
             'term': index_term[i],
             'energy': index_energy[i]}
        result.append(d)

    return result


def export_flat_text(f):
    """
    export the binding energies to a flat general text file.

    @param f: file path or open file object
    @return: None
    """
    if hasattr(f, "write") and callable(f.write):
        f.write("number symbol term energy\n")
        for element in pt.elements:
            for term, energy in element.binding_energy.items():
                f.write(f"{element.number} {element.symbol} {term} {energy}\n")
    else:
        with open(f, "w") as fi:
            export_flat_text(fi)
