#!/usr/bin/env python

"""
API for cam's configure
"""

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, expect
from CIME.utils import run_cmd
from CIME.case import Case
from CIME.buildnml import parse_input

import glob, shutil
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")

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

    # The following translation is hard-wired for backwards compatibility
    # to support the differences between how the scripts specify the land grid
    # and how it is specified internally

    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'

    # if need to build - then construct configure command
    config_opts = ["-s", "-fc_type", compiler, "-ccsm_seq",
                   "-dyn", cam_dycore, "-res", atm_grid,
                   "-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 or pts_mode:
        config_opts.append("-nosmp")
    else:
        config_opts.append("-smp")

    # 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.
    
    ocn = comp_ocn
    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.
    cmd = os.path.join(srcroot,"components","cam","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()
