# Use Dans_Diffraction package to generate a simulated XRD pattern
# from the CIF data.
#
import os
import numpy as np

from pyDataVis.utils import isNumeric, isNotNumeric, listToArray, dataToFile


def CIF(flines, savename):
    """ Generate a simulated XRD pattern from the CIF data in 'flines'.

        The reflection list are generated by Dans_Diffraction package, which is
        a Python package for loading crystal structures from cif files
        and calculating diffraction information:
        https://pypi.org/project/Dans-Diffraction/

    :param flines: Text line list containing the CIF data.
    :param savename: Name of the file where the pattern will be saved.
    :return: Return None if success otherwise return an error message.
    """
    try:
        import Dans_Diffraction as dif
    except ImportError:
        return "Cannot find Dans_Diffraction module\n" \
                "It can be installed in a terminal with\n" \
                "pip install Dans-Diffraction"
    # Look for lines starting with 'data_' and 'loop_'
    tags = 0
    for lin in flines:
        if lin.startswith("data_") or lin.startswith("DATA_"):
            tags += 1
        if lin.startswith("loop_") or lin.startswith("LOOP_"):
            tags += 1
        if tags > 1:
            break
    if tags < 1:
        return "It seems that this is not a CIF format"

    errmsg = None
    dir = os.path.dirname(savename)
    if not os.access(dir, os.W_OK):
        return "Cannot save 'dans.cif' temporary file"
    # Save flines in 'dans.cif'
    outnam = "{0}/dans.cif".format(dir)
    with open(outnam, "w") as outfile:
        outfile.write("\n".join(flines))
    # load cif file
    xtl=dif.Crystal(outnam)
    # Get Crystal structure parameters
    info = xtl.info()
    lines = info.splitlines()
    lines.append("#\n")
    # Generate reflection list
    #xtl.Scatter.setup_scatter(type='x-ray', energy_kev=8.0)
    reflec = xtl.Scatter.print_all_reflections(print_symmetric=False, min_intensity=0.01, units='tth')
    # convert reflec into list
    reflst = reflec.splitlines()
    # Add the elements of reflst at the end of lines
    lines.extend(reflst)
    errmsg = HKL(lines[3:], savename)
    return errmsg


def HKL(flines, savename):
    """ Convert the reflection list in flines in a standard format

    :param flines: Text line list containing the CIF data.
    :param savename: Name of the file where the pattern will be saved.
    :return: Return None if success otherwise return an error message.
    """
    errmsg = None
    # remove empty lines
    lines = [ lin for lin in flines if lin ]
    # remove multiple spaces
    lines = [ " ".join(lin.split()) for lin in lines]
    # remove "()," characters
    for i, lin in enumerate(lines):
        lines[i] = lin.translate({ord(c): None for c in '(),'})
    # split each line in items
    lines = [ lin.split() for lin in lines]
    n = len(lines)
    if len(lines[n-2]) != 5:
        return 'This file cannot be processed'

    # copy comments in header
    head = filter(isNotNumeric, lines)
    header = [ ' '.join(lin) for lin in head]
    # Remove the line with "( h, k, l)      TwoTheta  Intensity"
    del header[-2]

    # Keep only lines containing numerical values
    numlines = [lin for lin in filter(isNumeric, lines)]
    numdata = listToArray(numlines).transpose()
    (ncol, nrow) = np.shape(numdata)
    if ncol != 5:
        return 'This file cannot be processed'

    # The 3 first columns should be h, k, l
    for r in range(nrow):
        for c in range(3):
            if int(numdata[c][r]) != numdata[c][r]:
                return 'h, k, l should be integers'

    # the 4th column should be two theta in range 5 to 180
    if numdata[3].min() < 5 or numdata[3].max() > 180.0:
        return '2theta out of range'
    # the 5th column should contain I
    if numdata[4].min() < 0.0 or numdata[4].max() > 100000:
        return 'I out of range'

    xmin = int(numdata[3].min()/10.0) * 10.0
    if xmin > 20:
        xmin = 20
    xmax = int((numdata[3].max()/10.0) + 1) * 10.0
    dx = 0.1
    X = np.arange(xmin, xmax, dx)
    newdata = np.zeros((2, X.size))
    newdata[0] = X
    for i in range(nrow):
        idx = int((1/dx)*(numdata[3][i] - xmin) + dx/2.0)
        newdata[1][idx] = numdata[4][i]

    # Save generated data in a text file
    # Insert, if needed, a # character at the beginning of comment lines
    for i in range(len(header)):
        if not header[i].startswith('#'):
            header[i] = '#' + header[i]
    header.append('#\n#2theta\tSimuI\n')
    header = [('\n'.join(header))]
    (errno, errmsg) = dataToFile(savename, [newdata], header)
    if errno == 0:
        errmsg = None
    return errmsg

