#!/usr/bin/env python
"""Command line utility to run amber antechamber command with AiiDA.

Usage: aiida_antechamber --help
"""

import os

import click

from aiida import cmdline, engine
from aiida.orm import SinglefileData
from aiida.plugins import CalculationFactory, DataFactory

from aiida_amber import helpers

# from aiida_amber.utils import searchprevious


def launch(params):
    """Run antechamber.

    Uses helpers to add amber on localhost to AiiDA on the fly.

    - get inputs and outputs
    - check if they are files
    - add them as nodes
    """

    # Prune unused CLI parameters from dict.
    params = {k: v for k, v in params.items() if v is not None}

    # dict to hold our calculation data.
    inputs = {
        "metadata": {
            "description": params.pop("description"),
        },
    }

    # If code is not initialised, then setup.
    if "code" in inputs:
        inputs["code"] = params.pop("code")
    else:
        computer = helpers.get_computer()
        inputs["code"] = helpers.get_code(entry_point="antechamber", computer=computer)

    # Prepare input parameters in AiiDA formats.
    inputs["input_file"] = SinglefileData(
        file=os.path.join(os.getcwd(), params.pop("i"))
    )

    if "cf" in params:
        inputs["charge_file"] = SinglefileData(
            file=os.path.join(os.getcwd(), params.pop("cf"))
        )
    if "a" in params:
        inputs["additional_file"] = SinglefileData(
            file=os.path.join(os.getcwd(), params.pop("a"))
        )
    if "rf" in params:
        inputs["res_top_file"] = SinglefileData(
            file=os.path.join(os.getcwd(), params.pop("rf"))
        )
    if "ch" in params:
        inputs["check_file"] = SinglefileData(
            file=os.path.join(os.getcwd(), params.pop("ch"))
        )
    if "ge" in params:
        inputs["esp_file"] = SinglefileData(
            file=os.path.join(os.getcwd(), params.pop("ge"))
        )

    AntechamberParameters = DataFactory("amber.antechamber")
    inputs["parameters"] = AntechamberParameters(params)

    # need to search previous processes properly
    # check if inputs are outputs from prev processes
    # inputs = searchprevious.append_prev_nodes(inputs, inputs["input_list"])

    # check if a pytest test is running, if so run rather than submit aiida job
    # Note: in order to submit your calculation to the aiida daemon, do:
    # pylint: disable=unused-variable
    if "PYTEST_CURRENT_TEST" in os.environ:
        future = engine.run(CalculationFactory("amber.antechamber"), **inputs)
    else:
        future = engine.submit(CalculationFactory("amber.antechamber"), **inputs)


@click.command()
@cmdline.utils.decorators.with_dbenv()
@cmdline.params.options.CODE()
# Plugin options
@click.option(
    "--description",
    default="record antechamber data provenance via the aiida_amber plugin",
    type=str,
    help="Short metadata description",
)

# Required inputs
@click.option("-i", default="input.mol2", type=str, help="input file name")  # file
@click.option("-fi", default="mol2", type=str, help="input file format")  # parameter
# Required outputs
@click.option("-o", default="output.mol2", type=str, help="output file name")  # file
@click.option("-fo", default="mol2", type=str, help="output file format")  # parameter

# optional input files
@click.option(
    "-cf",
    type=str,
    help="charge file name",
)
@click.option(
    "-a",
    type=str,
    help="additional file name",
)
@click.option(
    "-rf",
    type=str,
    help="residue toplogy file name in prep input file, default is molecule.res",
)
@click.option(
    "-ch",
    type=str,
    help="check file name for gaussian, default is 'molecule'",
)
@click.option(
    "-ge",
    type=str,
    help="gaussian esp file generated by iop(6/50=1), default is g09.gesp",
)

# other parameters
@click.option(
    "-c",
    type=str,
    help="charge method",
)
@click.option(
    "-nc",
    type=int,
    help="net molecular charge (int)",
)
@click.option(
    "-fa",
    type=str,
    help="additional file format",
)
@click.option(
    "-ao",
    type=str,
    help="""additional file operation
                          crd   : only read in coordinate
                          crg   : only read in charge
                          radius: only read in radius
                          name  : only read in atom name
                          type  : only read in atom type
                          bond  : only read in bond type""",
)
@click.option(
    "-m",
    type=str,
    help="multiplicity (2S+1), default is 1",
)
@click.option(
    "-rn",
    type=str,
    help="residue name, overrides input file, default is MOL",
)
@click.option(
    "-ek",
    type=str,
    help="mopac or sqm keyword, inside quotes; overwrites previous ones",
)
@click.option(
    "-gk",
    type=str,
    help="gaussian job keyword, inside quotes, is ignored when both -gopt and -gsp are used",
)
@click.option(
    "-gopt",
    type=str,
    help="gaussian job keyword for optimization, inside quotes",
)
@click.option(
    "-gsp",
    type=str,
    help="gaussian job keyword for single point calculation, inside quote",
)
@click.option(
    "-gm",
    type=str,
    help='gaussian memory keyword, inside quotes, such as "%mem=1000MB"',
)
@click.option(
    "-gn",
    type=str,
    help='gaussian number of processors keyword, inside quotes, such as "%nproc=8"',
)
@click.option(
    "-gdsk",
    type=str,
    help='gaussian maximum disk usage keyword, inside quotes, such as "%maxdisk=50GB"',
)
@click.option(
    "-gv",
    type=int,
    help="""add keyword to generate gesp file (for Gaussian 09 only)
                          1    : yes
                          0    : no, the default""",
)
@click.option(
    "-tor",
    type=str,
    help="""torsional angle list, inside a pair of quotes, such as "1-2-3-4:0,5-6-7-8"
                          ':1' or ':0' indicates the torsional angle is frozen or not""",
)
@click.option(
    "-df",
    type=int,
    help="am1-bcc precharge flag, 2 - use sqm(default); 0 - use mopac",
)
@click.option(
    "-at",
    type=str,
    help="""atom type
                          gaff : the default
                          gaff2: for gaff2 (beta-version)
                          amber: for PARM94/99/99SB
                          bcc  : bcc
                          sybyl: sybyl """,
)
@click.option(
    "-du",
    type=str,
    help="fix duplicate atom names: yes(y)[default] or no(n)",
)
@click.option(
    "-bk",
    type=str,
    help="component/block Id, for ccif",
)
@click.option(
    "-an",
    type=str,
    help="""adjust atom names: yes(y) or no(n)
                          the default is 'y' for 'mol2' and 'ac' and 'n' for the other formats""",
)
@click.option(
    "-j",
    type=int,
    help="""atom type and bond type prediction index, default is 4
                          0    : no assignment
                          1    : atom type
                          2    : full  bond types
                          3    : part  bond types
                          4    : atom and full bond type
                          5    : atom and part bond type """,
)
@click.option(
    "-s",
    type=int,
    help="status information: 0(brief), 1(default) or 2(verbose)",
)
@click.option(
    "-eq",
    type=int,
    help="""equalizing atomic charge, default is 1 for '-c resp' and '-c bcc' and 0 for the other charge methods
                          0    : no use
                          1    : by atomic paths
                          2    : by atomic paths and structural information, i.e. E/Z configurations""",
)
@click.option(
    "-pf",
    type=str,
    help="remove intermediate files: yes(y) or no(n)[default]",
)
@click.option(
    "-pl",
    type=str,
    help="""maximum path length to determin equivalence of atomic charges for resp and bcc,
                          the smaller the value, the faster the algorithm, default is -1 (use full length),
                          set this parameter to 10 to 30 if your molecule is big (# atoms >= 100)""",
)
@click.option(
    "-seq",
    type=str,
    help="atomic sequence order changable: yes(y)[default] or no(n)",
)
@click.option(
    "-dr",
    type=str,
    help="acdoctor mode: yes(y)[default] or no(n)",
)
def cli(*args, **kwargs):
    # pylint: disable=unused-argument
    # pylint: disable=line-too-long
    """Run example.

    Example usage:

    $ aiida_antechamber --code antechamber@localhost -i input.mol2 -o output.mol2 -fi mol2 -fo mol2 -c bcc -pf yes -nc -2 -at gaff2 -j 5

    Alternative (automatically tried to create amber@localhost code, but requires
    amber to be installed and available in your environment path):

    $ aiida_antechamber -i input.mol2 -o output.mol2 -fi mol2 -fo mol2 -c bcc -pf yes -nc -2 -at gaff2 -j 5

    Help: $ aiida_antechamber --help
    """

    launch(kwargs)


if __name__ == "__main__":
    cli()  # pylint: disable=no-value-for-parameter
