from __future__ import absolute_import
import os
import time
import numpy as np

# get is_CASA6 and is_python3
from casatasks.private.casa_transition import *
if is_CASA6:
    from .. import casalog
    from .callibrary import *
    from . import flaghelper as fh
    from .parallel.parallel_data_helper import ParallelDataHelper
    from .parallel.parallel_task_helper import ParallelTaskHelper
    from .mstools import write_history
    from casatools import ms, calibrater
else:
    from taskinit import *
    from mstools import write_history
    from callibrary import *
    import flaghelper as fh
    from parallel.parallel_data_helper import ParallelDataHelper
    from parallel.parallel_task_helper import ParallelTaskHelper

    calibrater = cbtool
    ms = mstool

def applycal(
    vis=None,
    field=None,
    spw=None,
    intent=None,
    selectdata=None,
    timerange=None,
    uvrange=None,
    antenna=None,
    scan=None,
    observation=None,
    msselect=None,
    docallib=None,
    callib=None,
    gaintable=None,
    gainfield=None,
    interp=None,
    spwmap=None,
    calwt=None,
    parang=None,
    applymode=None,
    flagbackup=None,
    ):

    # Python script
    casalog.origin('applycal')

    # Take care of the trivial parallelization
    if ParallelDataHelper.isMMSAndNotServer(vis):
        
        # Back up the flags, if requested (and if necessary)
        if flagbackup and applymode != 'calonly' and applymode != 'trial':
            fh.backupFlags(aflocal=None, msfile=vis, prename='applycal')
            flagbackup = False
        
        # To be safe convert file names to absolute paths.
        gaintable = ParallelTaskHelper.findAbsPath(gaintable)
        helper = ParallelTaskHelper('applycal', locals())
        ret = helper.go()
        if ParallelTaskHelper.getAsyncMode():
            return ret
        else:
            return

    try:
        mycb = calibrater( )
        makecorr = True
        if (type(vis) == str) & os.path.exists(vis):
            # if applymode is flagonly don't add CORRECTED_DATA column
            # otherwise add CORRECTED_DATA column
            if applymode in ['flagonly', 'flagonlystrict', 'trial']:
                makecorr = False
            mycb.open(filename=vis, compress=False, addcorr=makecorr,
                      addmodel=False)
        else:
            raise ValueError( 'Visibility data set not found - please verify the name' )

        # enforce default if unspecified
        if applymode == '':
            applymode = 'calflag'

        # Back up the flags, if requested (and if necessary)
        if flagbackup and applymode != 'calonly' and applymode \
            != 'trial':
            fh.backupFlags(aflocal=None, msfile=vis, prename='applycal')

        # Do data selection according to selectdata
        if selectdata:
            # pass all data selection parameters in as specified
            mycb.selectvis(
                time=timerange,
                spw=spw,
                scan=scan,
                field=field,
                intent=intent,
                observation=str(observation),
                baseline=antenna,
                uvrange=uvrange,
                chanmode='none',
                msselect=msselect,
                )
        else:
            # selectdata=F, so time,scan,baseline,uvrange,msselect=''
            # using spw and field specifications only
            mycb.selectvis(
                time='',
                spw=spw,
                scan='',
                field=field,
                intent=intent,
                observation='',
                baseline='',
                uvrange='',
                chanmode='none',
                msselect='',
                )

        # Arrange applies....

        if docallib:
            # by cal library from file
            # parsing using c++ parser
            thiscallib=mycb.parsecallibfile(callib)
            mycb.setcallib(thiscallib)

        else:

            # by traditional parameters

            ngaintab = 0
            if gaintable != ['']:
                ngaintab = len(gaintable)

            ncalwt = len(calwt)
            if ncalwt == 1:
                calwt = [calwt[0] for i in range(ngaintab)]

            ngainfld = len(gainfield)
            nspwmap = len(spwmap)
            ninterp = len(interp)

            # handle list of list issues with spwmap
            if nspwmap > 0:
                if type(spwmap[0]) != list:
                    # first element not a list, only one spwmap specified
                    # make it a list of list
                    spwmap = [spwmap]
                    nspwmap = 1

            for igt in range(ngaintab):
                if gaintable[igt] != '':

                    # field selection is null unless specified
                    thisgainfield = ''
                    if igt < ngainfld:
                        thisgainfield = gainfield[igt]

                    # spwmap is null unless specifed
                    thisspwmap = [-1]
                    if igt < nspwmap:
                        thisspwmap = spwmap[igt]

                    # interp is 'linear' unless specified
                    thisinterp = 'linear'
                    if igt < ninterp:
                        if interp[igt] == '':
                            interp[igt] = thisinterp
                        thisinterp = interp[igt]

                    mycb.setapply(
                        t=0.0,
                        table=gaintable[igt],
                        field=thisgainfield,
                        calwt=calwt[igt],
                        spwmap=thisspwmap,
                        interp=thisinterp,
                        )

        # ...and now the specialized terms

        # Apply parallactic angle, if requested
        if parang:
            mycb.setapply(type='P')

        mycb.correct(applymode)

        # report what the flags did
        reportflags(mycb.activityrec())


        # write history
        try:
            param_names = \
                          applycal.__code__.co_varnames[:applycal.__code__.co_argcount]
            if is_python3:
                vars = locals( )
                param_vals = [vars[p] for p in param_names]
            else:
                param_vals = [eval(p) for p in param_names]
            write_history(
                ms(),
                vis,
                'applycal',
                param_names,
                param_vals,
                casalog,
                )
        except Exception as instance:
            casalog.post("*** Error \'%s\' updating HISTORY"
                         % instance, 'WARN')
    finally:
        mycb.close()

def reportflags(rec):
    try:
        if 'origin' in rec and rec['origin'] \
            == 'Calibrater::correct' and 'VisEquation' in rec:
            casalog.post('Calibration apply flagging statistics (among calibrateable spws):'
                         )
            VE = rec['VisEquation']
            nterm = len(VE)
            if nterm > 0:
                nVisTotal=VE['nVisTotal']

                casalog.post('  Total visibilities selected for correction (ncorr x nchan x nrow summed over spws) = '
                              + str(nVisTotal))
                casalog.post('  Flags:')
                partlog=False
                for iterm in range(nterm-1):    # one of the keys is nVisTotal; the rest are caltable indices
                    VEi = VE['*' + str(iterm + 1)]

                    nVisThis=VEi['ndata']
                    partial='  '
                    if nVisThis<nVisTotal:
                        partlog=True
                        partial='**'
                    flstr = '   ' + VEi['type']
                    flstr += ': '
                    flstr += 'In: ' + str(VEi['nflagIn'])
                    flstr += ' / '+str(nVisThis)+partial
                    flstr += ' (' + str(100. * VEi['nflagIn']/nVisThis) + '%) --> '
                    flstr += 'Out: ' + str(VEi['nflagOut'])
                    flstr += ' / '+str(nVisThis)+partial
                    flstr += ' (' + str(100. * VEi['nflagOut']/nVisThis) + '%)'

                    if 'table' in VEi:
                        flstr += ' (' + VEi['table'] + ')'
                    casalog.post(flstr)
                if partlog:
                    casalog.post('     ** = Denotes caltable that only corrected a subset of total selected visibilities')

    except Exception as instance:
        # complain mildly, but don't alarm
        casalog.post('Error formatting some or all of the applycal flagging log info: '
                      + str(instance), 'SEVERE')