#!/usr/bin/env python

"""
API for cam's configure
"""
#pylint: disable=multiple-imports, wrong-import-position, wildcard-import
#pylint: disable=invalid-name, unused-wildcard-import, too-many-locals
#pylint: disable=too-many-branches, too-many-statements
import os, sys, re

CIMEROOT = os.environ.get("CIMEROOT")
if CIMEROOT is None:
    raise SystemExit("ERROR: must set CIMEROOT environment variable")
sys.path.append(os.path.join(CIMEROOT, "scripts", "Tools"))

from standard_script_setup import *

from CIME.utils import run_cmd_no_fail
from CIME.case import Case
from CIME.buildnml import parse_input


logger = logging.getLogger(__name__)

###############################################################################
def buildcpp(case):
###############################################################################
    """
    Invoke cam configure - output goes in `caseroot`/Buildconf/camconf
    """
    caseroot = case.get_value("CASEROOT")
    srcroot = case.get_value("SRCROOT")
    exeroot = case.get_value("EXEROOT")
    atm_grid = case.get_value("ATM_GRID")
    pts_mode = case.get_value("PTS_MODE")
    cam_dycore = case.get_value("CAM_DYCORE")
    comp_ocn = case.get_value("COMP_OCN")
    docn_mode = case.get_value("DOCN_MODE")
    mpilib = case.get_value("MPILIB")
    compiler = case.get_value("COMPILER") # for chem preprocessor
    nthrds_atm = case.get_value("NTHRDS_ATM")
    cam_config_opts = case.get_value("CAM_CONFIG_OPTS")
    comp_interface=case.get_value("COMP_INTERFACE")

    # level information for CAM is part of the atm grid name - and must be stripped out
    nlev = ''
    match = re.match(r'(.+)z(\d+)', atm_grid)
    if match:
        atm_grid = match.groups()[0]
        nlev = match.groups()[1]

    # The following translations are hard-wired to support the differences
    # between how the CESM scripts specify the grid and how it is specified
    # by CAM's build and run system.

    if atm_grid == 'T5':
        atm_grid = '8x16'
    if atm_grid == 'T31':
        atm_grid = '48x96'
    if atm_grid == 'T42':
        atm_grid = '64x128'
    if atm_grid == 'T85':
        atm_grid = '128x256'
    if atm_grid == 'T341':
        atm_grid = '512x1024'

    # Need to relax this error tolerance for the SE variable resolution grids
    if atm_grid[0:3] == 'ne0':
        case.set_value("EPS_AAREA", "1.0e-04")

    # Need to relax this error tolerance for the T42 grids
    if atm_grid[0:6] == '64x128':
        case.set_value("EPS_AAREA", "1.0e-04")
        case.set_value("EPS_AGRID", "1.0e-05")

    # The vector mapping (in the mediator) needs to be 'cart3d' for SE
    # NB: This is currently the default, is it working by conincidence for
    #     other unstructured dycores?
    match = re.match(r'ne[0-9]', atm_grid)
    if match:
        case.set_value('VECT_MAP', 'cart3d')

    # if need to build - then construct configure command
    config_opts = ["-s", "-fc_type", compiler, "-dyn", cam_dycore,
                   "-hgrid", atm_grid, "-cpl", comp_interface,
                   "-usr_src", os.path.join(caseroot, "SourceMods", "src.cam")]

    if nlev:
        config_opts += ["-nlev", nlev]

    # Some settings for single column mode.
    if pts_mode:
        config_opts.append("-scam")

    if mpilib == 'mpi-serial':
        config_opts.append("-nospmd")
    else:
        config_opts.append("-spmd")

    if int(nthrds_atm) == 1:
        config_opts.append("-nosmp")
    else:
        config_opts.append("-smp")

    if cam_dycore == 'fv3':
         config_opts += ["-fv3core_libdir", os.path.join(exeroot,"atm","obj","atmos_cubed_sphere")]

    # The ocean component setting is only used by CAM to do attribute matching for
    # setting default tuning parameter values.  In SOM mode we want to use the same
    # tunings as the fully coupled B compset, so set the ocean component to pop in
    # that case.

    if docn_mode == 'som':
        config_opts += ["-ocn", "pop"]
    else:
        config_opts += ["-ocn", comp_ocn]

    # Add user options.
    config_opts += cam_config_opts.split(" ")

    if "-cosp" in config_opts:
        config_opts += ["-cosp_libdir", os.path.join(exeroot, "atm", "obj", "cosp")]

    camconf = os.path.join(caseroot, "Buildconf", "camconf")
    if not os.path.isdir(camconf):
        os.makedirs(camconf)

    # Construct the command itself.
    testpath = os.path.join(srcroot, "components", "cam")
    if os.path.exists(testpath):
        srcroot = testpath
    cmd = os.path.join(srcroot, "bld", "configure") + \
          " " + " ".join(config_opts)
    run_cmd_no_fail(cmd, from_dir=camconf)

    # determine cppdefs - caseroot/camconf/CESM_cppdefs is created by the call to configure
    with open(os.path.join(camconf, "CESM_cppdefs"), 'r') as f:
        user_cppdefs = f.readline().rstrip()
    if user_cppdefs:
        case.set_value("CAM_CPPDEFS", user_cppdefs)
        case.flush()

    return user_cppdefs

###############################################################################
def _main_func():

    caseroot = parse_input(sys.argv)
    with Case(caseroot) as case:
        cam_cppdefs = buildcpp(case)
    logger.info("CAM_CPPDEFS: %s", cam_cppdefs)

if __name__ == "__main__":
    _main_func()
