"""
`AstroDrizzle` - Python implementation of `MultiDrizzle`

`AstroDrizzle` automates the process of aligning images in an output frame,
identifying cosmic-rays, removing distortion, and then combining the images
after removing the identified cosmic-rays.

This process involves a number of steps, namely:
  1.  Processing the input images and input parameters
  2.  Creating a static mask
  3.  Performing sky subtraction
  4.  Drizzling onto separate output images
  5.  Creating the median image
  6.  Blotting the median image
  7.  Identifying and flagging cosmic-rays
  8.  Final combination

A full description of this process can be found
in the `DrizzlePac Handbook <http://drizzlepac.stsci.edu>`_\ .

The primary output from this task is the distortion-corrected,
cosmic-ray cleaned, and combined image as a FITS file.

This task requires numerous user-settable parameters to control the primary
aspects of each of the processing steps.

:Authors: Warren Hack

:License: :doc:`LICENSE`

"""
from __future__ import absolute_import, division, print_function  # confidence high


import os
import sys

from six import string_types

from stsci.tools import teal, logutil, textutil

from . import adrizzle
from . import ablot
from . import createMedian
from . import drizCR
from . import processInput
from . import sky
from . import staticMask
from . import util
from . import wcs_functions
from .version import *


__taskname__ = "astrodrizzle"


# Pointer to the included Python class for WCS-based coordinate transformations
PYTHON_WCSMAP = wcs_functions.WCSMap

log = logutil.create_logger(__name__, level=logutil.logging.NOTSET)


def AstroDrizzle(input=None, mdriztab=False, editpars=False, configobj=None,
                 wcsmap=None, **input_dict):
    """ AstroDrizzle command-line interface """
    # Support input of filenames from command-line without a parameter name
    # then copy this into input_dict for merging with TEAL ConfigObj
    # parameters.

    # Load any user-specified configobj
    if isinstance(configobj, string_types):
        if configobj == 'defaults':
            # load "TEAL"-defaults (from ~/.teal/):
            configobj = teal.load(__taskname__)
        else:
            if not os.path.exists(configobj):
                raise RuntimeError('Cannot find .cfg file: '+configobj)
            configobj = teal.load(configobj, strict=False)
    elif configobj is None:
        # load 'astrodrizzle' parameter defaults as described in the docs:
        configobj = teal.load(__taskname__, defaults=True)

    if input and not util.is_blank(input):
        input_dict['input'] = input
    elif configobj is None:
        raise TypeError("AstroDrizzle() needs either 'input' or "
                        "'configobj' arguments")

    if 'updatewcs' in input_dict: # user trying to explicitly turn on updatewcs
        configobj['updatewcs'] = input_dict['updatewcs']
        del input_dict['updatewcs']

    # If called from interactive user-interface, configObj will not be
    # defined yet, so get defaults using EPAR/TEAL.
    #
    # Also insure that the input_dict (user-specified values) are folded in
    # with a fully populated configObj instance.
    try:
        configObj = util.getDefaultConfigObj(__taskname__, configobj,
                                             input_dict,
                                             loadOnly=(not editpars))
        log.debug('')
        log.debug("INPUT_DICT:")
        util.print_cfg(input_dict, log.debug)
        log.debug('')
        # If user specifies optional parameter for final_wcs specification in input_dict,
        #    insure that the final_wcs step gets turned on
        util.applyUserPars_steps(configObj, input_dict, step='3a')
        util.applyUserPars_steps(configObj, input_dict, step='7a')

    except ValueError:
        print("Problem with input parameters. Quitting...", file=sys.stderr)
        return

    if not configObj:
        return

    configObj['mdriztab'] = mdriztab
    # If 'editpars' was set to True, util.getDefaultConfigObj() will have
    # already called 'run()'.
    if not editpars:
        run(configObj, wcsmap=wcsmap)

##############################
##  Interfaces used by TEAL ##
##############################
@util.with_logging
def run(configobj, wcsmap=None):
    """
    Initial example by Nadia ran MD with configobj EPAR using:
    It can be run in one of two ways:

        from stsci.tools import teal

        1. Passing a config object to teal

        teal.teal('drizzlepac/pars/astrodrizzle.cfg')


        2. Passing a task  name:

        teal.teal('astrodrizzle')

        The example config files are in drizzlepac/pars

    """
    # turn on logging, redirecting stdout/stderr messages to a log file
    # while also printing them out to stdout as well
    # also, initialize timing of processing steps
    #
    # We need to define a default logfile name from the user's parameters
    input_list, output, ivmlist, odict = \
            processInput.processFilenames(configobj['input'])

    if output is not None:
        def_logname = output
    elif len(input_list) > 0:
        def_logname = input_list[0]
    else:
        print(textutil.textbox(
            "ERROR:\nNo valid input files found!   Please restart the task "
            "and check the value for the 'input' parameter."), file=sys.stderr)
        def_logname = None
        return

    clean = configobj['STATE OF INPUT FILES']['clean']
    procSteps = util.ProcSteps()

    print("AstroDrizzle Version {:s} ({:s}) started at: {:s}\n"
          .format(__version__, __version_date__, util._ptime()[0]))
    util.print_pkg_versions(log=log)

    log.debug('')
    log.debug(
        "==== AstroDrizzle was invoked with the following parameters: ===="
    )
    log.debug('')
    util.print_cfg(configobj, log.debug)

    try:
        # Define list of imageObject instances and output WCSObject instance
        # based on input paramters
        imgObjList = None
        procSteps.addStep('Initialization')
        imgObjList, outwcs = processInput.setCommonInput(configobj)
        procSteps.endStep('Initialization')

        if imgObjList is None or not imgObjList:
            errmsg = "No valid images found for processing!\n"
            errmsg += "Check log file for full details.\n"
            errmsg += "Exiting AstroDrizzle now..."
            print(textutil.textbox(errmsg, width=65))
            print(textutil.textbox(
                'ERROR:\nAstroDrizzle Version {:s} encountered a problem!  '
                'Processing terminated at {:s}.'
                .format(__version__, util._ptime()[0])), file=sys.stderr)
            return

        log.info("USER INPUT PARAMETERS common to all Processing Steps:")
        util.printParams(configobj, log=log)

        # Call rest of MD steps...
        #create static masks for each image
        staticMask.createStaticMask(imgObjList, configobj,
                                    procSteps=procSteps)

        #subtract the sky
        sky.subtractSky(imgObjList, configobj, procSteps=procSteps)

#       _dbg_dump_virtual_outputs(imgObjList)

        #drizzle to separate images
        adrizzle.drizSeparate(imgObjList, outwcs, configobj, wcsmap=wcsmap,
                              procSteps=procSteps)

#       _dbg_dump_virtual_outputs(imgObjList)

        #create the median images from the driz sep images
        createMedian.createMedian(imgObjList, configobj, procSteps=procSteps)

        #blot the images back to the original reference frame
        ablot.runBlot(imgObjList, outwcs, configobj, wcsmap=wcsmap,
                      procSteps=procSteps)

        #look for cosmic rays
        drizCR.rundrizCR(imgObjList, configobj, procSteps=procSteps)

        #Make your final drizzled image
        adrizzle.drizFinal(imgObjList, outwcs, configobj, wcsmap=wcsmap,
                           procSteps=procSteps)

        print()
        print("AstroDrizzle Version {:s} is finished processing at {:s}.\n"
              .format(__version__, util._ptime()[0]))

    except:
        clean = False
        print(textutil.textbox(
            "ERROR:\nAstroDrizzle Version {:s} encountered a problem!  "
            "Processing terminated at {:s}."
            .format(__version__, util._ptime()[0])), file=sys.stderr)
        raise

    finally:
        procSteps.reportTimes()
        if imgObjList:
            for image in imgObjList:
                if clean:
                    image.clean()
                image.close()
            del imgObjList
            del outwcs


def help(file=None):
    """
    Print out syntax help for running astrodrizzle

    Parameters
    ----------
    file : str (Default = None)
        If given, write out help to the filename specified by this parameter
        Any previously existing file with this name will be deleted before
        writing out the help.

    """
    helpstr = getHelpAsString(docstring=True, show_ver = True)
    if file is None:
        print(helpstr)
    else:
        if os.path.exists(file): os.remove(file)
        f = open(file, mode = 'w')
        f.write(helpstr)
        f.close()


def getHelpAsString(docstring = False, show_ver = True):
    """
    return useful help from a file in the script directory called
    __taskname__.help

    """
    install_dir = os.path.dirname(__file__)
    taskname = util.base_taskname(__taskname__, '')
    htmlfile = os.path.join(install_dir, 'htmlhelp', taskname + '.html')
    helpfile = os.path.join(install_dir, taskname + '.help')

    if docstring or (not docstring and not os.path.exists(htmlfile)):
        if show_ver:
            helpString = os.linesep + \
                ' '.join([__taskname__, 'Version', __version__,
                ' updated on ', __version_date__]) + 2*os.linesep
        else:
            helpString = ''
        if os.path.exists(helpfile):
            helpString += teal.getHelpFileAsString(taskname, __file__)
        else:
            if __doc__ is not None:
                helpString += __doc__ + os.linesep
    else:
        helpString = 'file://' + htmlfile

    return helpString


_fidx = 0
def _dbg_dump_virtual_outputs(imgObjList):
    """ dump some helpful information.  strictly for debugging """
    global _fidx
    tag = 'virtual'
    log.info((tag+'  ')*7)
    for iii in imgObjList:
        log.info('-'*80)
        log.info(tag+'  orig nm: '+iii._original_file_name)
        log.info(tag+'  names.data: '+str(iii.outputNames["data"]))
        log.info(tag+'  names.orig: '+str(iii.outputNames["origFilename"]))
        log.info(tag+'  id: '+str(id(iii)))
        log.info(tag+'  in.mem: '+str(iii.inmemory))
        log.info(tag+'  vo items...')
        for vok in sorted(iii.virtualOutputs.keys()):
            FITSOBJ = iii.virtualOutputs[vok]
            log.info(tag+': '+str(vok)+' = '+str(FITSOBJ))
            if vok.endswith('.fits'):
                if not hasattr(FITSOBJ, 'data'):
                    FITSOBJ = FITSOBJ[0] # list of PrimaryHDU ?
                if not hasattr(FITSOBJ, 'data'):
                    FITSOBJ = FITSOBJ[0] # was list of HDUList ?
                dbgname = 'DEBUG_%02d_'%(_fidx,)
                dbgname+=os.path.basename(vok)
                _fidx+=1
                FITSOBJ.writeto(dbgname)
                log.info(tag+'  wrote: '+dbgname)
                log.info('\n'+vok)
                if hasattr(FITSOBJ, 'data'):
                    log.info(str(FITSOBJ._summary()))
                    log.info('min and max are: '+str( (FITSOBJ.data.min(),
                                                       FITSOBJ.data.max()) ))
                    log.info('avg and sum are: '+str( (FITSOBJ.data.mean(),
                                                       FITSOBJ.data.sum()) ))
#                    log.info(str(FITSOBJ.data)[:75])
                else:
                    log.info(vok+' has no .data attr')
                    log.info(str(type(FITSOBJ)))
                log.info(vok+'\n')
    log.info('-'*80)

AstroDrizzle.__doc__ = getHelpAsString(docstring = True, show_ver = False)
