#  Copyright (C) 2017 The authors of Py-ChemShell
#
#  This file is part of Py-ChemShell.
#
#  Py-ChemShell is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation, either version 3 of the
#  License, or (at your option) any later version.
#
#  Py-ChemShell is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with Py-ChemShell.  If not, see
#  <http://www.gnu.org/licenses/>.

import chemsh
from os        import path
from ..data    import atomicdata
from ..base    import constants
from ..dl_py2f import utils

# YL 13/08/2022: we need a path prefix for taskfarmed jobs (default to impose in io/keyword.py)
def parse(filename, fmt, receiver=None, regex=None, prefix='', keywords_receiver=[], **kwargs):
    '''File parsers'''

    from os      import getcwd
    from sys     import exit, stdout
    from .files  import amber_prmtop, amber_rst, dcd, dlp_config, dlp_field, dlp_par, dlp_sf, ff, \
                        gulp, json, namd_coor, nwchem, out, prm, pdb, pqr, psf, punch, rtf, udff, xst, xyz
    from inspect import getfullargspec

    # check existance first
    try:
        # YL 15/08/2022: try with the original filename first because traditionlly the user needs to prefix the filename with '..' for taskfarmed jobs
        with open(filename):
            # no prefix if the filename is valid
            prefix = ''
    except FileNotFoundError:
        # YL 13/08/2022: path prefix
        try:
            with open(path.join(prefix, filename)):
                pass
        except FileNotFoundError:
            print(f' >>> ERROR: file {filename} does not exit. Please check your input.')
            stdout.flush()
            exit(999)

    def unknown(filename, **kwargs):
        '''Check filename if format unknown'''

        if chemsh.CHEMSH_DEBUG >=5:
            print(" >>> WARNING: skipping unknown file format of {}...".format(filename))

    if receiver is None and fmt in [ 'punch', 'out' ]:
        print(" ChemShell >>> ERROR: a data receiver must be given to parse file", filename)
        quit(1)

    # formats and corresponding parsers to trigger: please keep the alphabetic order when editing
    selectcases = {
                    'amber_prmtop': amber_prmtop.parse,
                    'amber_rst': amber_rst.parse,
                    'c'        : punch.parse,
                    'config'   : dlp_config.parse,
                    'coo'      : punch.parse,
                    'cjson'    : json.parse,
                    'dcd'      : dcd.parse,
                    'dl'       : dlp_config.parse,
                    'dlp_field': dlp_field.parse,
                    'dlp_par'  : dlp_par.parse,
                    'dlp_sf'   : dlp_sf.parse,
                    'ff'       : ff.parse,
                    'gulp'     : gulp.parse,
                    'json'     : json.parse,
                    'coor'     : namd_coor.parse,
                    'nwchem_bq': nwchem.parse,
                    'out'      : out.parse,
                    'par'      : prm.parse,
                    'pdb'      : pdb.parse,
                    'pqr'      : pqr.parse,
                    'prm'      : prm.parse,
                    'psf'      : psf.parse,
                    'pun'      : punch.parse,
                    'punch'    : punch.parse,
                    'rst'      : amber_rst.parse,
                    'rtf'      : rtf.parse,
                    'udff'     : udff.parse,
                    'xst'      : xst.parse,
                    'xyz'      : xyz.parse,
                     None      : unknown,
                    ''         : unknown,
                  }

    # YL 16/03/2024: get a list of legal keywords of all file parsers and append to keywords_receiver (see io.keyword.parseKeys())
    kwargs_func_all = []
    for k, v in selectcases.items():
        kwargs_func_all += getfullargspec(v).args
    keywords_receiver += list(set(kwargs_func_all))

    fmt = fmt or utils.fileutils.getExtension(filename) or utils.fileutils.getBaseName(filename)
    # AJL, October 2019. Designed to pickup the CONFIG file format (no extension)

    # master command
    try:
        returned = selectcases[fmt.lower()](filename, _regex=regex, receiver=receiver, prefix=prefix, **kwargs)
    except KeyError:
        print(f'\n >>> ERROR: Unknown type of file extension \"{fmt.lower()}\"')
        stdout.flush()
        exit(999)

    return returned


def dump(filename, fmt, obj, mute=False, **kwargs):
    '''Write out a file'''

    from .files import dlp_config, dlp_field, json, pdb, pqr, psf, punch, udff, xyz

    # formats and corresponding parsers to trigger: please keep the alphabetic order when editing
    selectcases = {
                    'c'        : punch.write,
                    'cjson'    : json.dump,
                    'config'   : dlp_config.write,
                    'dlp_field': dlp_field.write,
                    'coo'      : punch.write,
                    'json'     : json.dump,
                    'pdb'      : pdb.write,
                    'pqr'      : pqr.write,
                    'psf'      : psf.write,
                    'pun'      : punch.write,
                    'punch'    : punch.write,
                    'udff'     : udff.write,
                    'xyz'      : xyz.write,
                  }

    # AJL, October 2019. Designed to pickup the CONFIG file format (no extension)
    fmt = fmt or utils.fileutils.getExtension(filename) or utils.fileutils.getBaseName(filename)

    selectcases[fmt.lower()](filename, obj, **kwargs)
    if type(filename) is str and not mute:
        print(" >>> \"{}\" saved.".format(filename))


# FIXME: OBSOLETE
def convertFile(filename=None, format=None):
    '''File-convertion utilities'''

    blist = ["bson"]
    tlist = ["json", "cjson"]
    self.filename = str(filename).lower()
    self.format   = str(format)  .lower()
    self.root = path.splitext(self.filename)[0]
    self.ext  = path.splitext(self.filename)[1][1:]
    if   self.ext in blist or self.format in blist:
        self.b2t()
    elif self.ext in tlist or self.format in tlist:
        self.t2b()


    def b2t(self):
        '''A wrapper: binary to text'''
        if self.ext in ["bson"] or self.format in ["bson"]:
            self.bson2json()

    def t2b(self):
        '''A wrapper: text to binary'''


    def bson2json(self):
        from bson import BSON
        with open(self.filename, 'rb') as fp:
            foobar = BSON.decode(fp.read())
        with open("".join([self.root, ".json"]), 'w') as fp:
            json.dump(foobar, fp, sort_keys=False)


# YL 28/07/2022: this should not be part of the utils (now part of DL_PY2F)
# YL NB: not suitable for getting arrays of complex dtype
def getArrayFromFile(file, fmt, shape, regex=None, number_only=True, prefix='', filename_tmp='.getArrayFromFile'):
    '''Read contents of a text file into a <numpy.ndarray>'''

    from numpy import zeros
    from os    import remove

    abuff = zeros(shape)

    # YL 20/02/2024: we also take lines of strings
    if type(file) is list:
        filename = filename_tmp
        with open(filename, 'w') as fp:
            fp.writelines(file)

    else:
        filename = file

    # YL 30/09/2020: also get the returned data array
    abuff = parse(filename, fmt, receiver=abuff, regex=regex, number_only=number_only, prefix=prefix)

    # remove temporary file
    try:
        remove(filename_tmp)
    except FileNotFoundError:
        pass

    return abuff
