""" `TweakReg` - A replacement for IRAF-based `tweakshifts`

:Authors: Warren Hack, Mihai Cara

:License: `<http://www.stsci.edu/resources/software_hardware/pyraf/LICENSE>`_

"""
from __future__ import absolute_import, division, print_function
import os
import sys
import numpy as np
from copy import copy

from six import string_types

from stsci.tools import parseinput, teal
from stsci.tools import logutil, textutil
from stsci.tools.cfgpars import DuplicateKeyError
from stwcs import updatewcs

from . import util

# __version__ and __version_date__ are defined here, prior to the importing
# of the modules below, so that those modules can use the values
# from these variable definitions, allowing the values to be designated
# in one location only.
#
# This is specifically NOT intended to match the package-wide version information.
__version__ = '1.4.6'
__vdate__ = '19-March-2018'

from . import tweakutils
from . import imgclasses
from . import catalogs
from . import imagefindpars
from . import refimagefindpars

__taskname__ = 'tweakreg' # unless someone comes up with anything better

PSET_SECTION        = '_SOURCE FINDING PARS_'
PSET_SECTION_REFIMG = '_REF IMAGE SOURCE FINDING PARS_'
# !! Use pre and post underscores to hide this added section in TEAL, so that
# TEAL allows its data to stay alongside the expected data during a call to
# TweakReg().  All of this needs to be revisited.

log = logutil.create_logger(__name__)


def _managePsets(configobj, section_name, task_name, iparsobj=None, input_dict=None):
    """ Read in parameter values from PSET-like configobj tasks defined for
        source-finding algorithms, and any other PSET-like tasks under this task,
        and merge those values into the input configobj dictionary.
    """
    # Merge all configobj instances into a single object
    configobj[section_name] = {}

    # Load the default full set of configuration parameters for the PSET:
    iparsobj_cfg = teal.load(task_name)

    # Identify optional parameters in input_dicts that are from this
    # PSET and add it to iparsobj:
    if input_dict is not None:
        for key in list(input_dict.keys()):
            if key in iparsobj_cfg:
                if iparsobj is not None and key in iparsobj:
                    raise DuplicateKeyError("Duplicate parameter '{:s}' "
                        "provided for task {:s}".format(key, task_name))
                iparsobj_cfg[key] = input_dict[key]
                del input_dict[key]

    if iparsobj is not None:
        iparsobj_cfg.update(iparsobj)

    del iparsobj_cfg['_task_name_']

    # merge these parameters into full set
    configobj[section_name].merge(iparsobj_cfg)

    # clean up configobj a little to make it easier for later...
#   if '_RULES_' in configobj:
#       del configobj['_RULES_']


def edit_imagefindpars():
    """ Allows the user to edit the imagefindpars configObj in a TEAL GUI
        """
    teal.teal(imagefindpars.__taskname__, returnAs=None,
              autoClose=True, loadOnly=False, canExecute=False)

def edit_refimagefindpars():
    """ Allows the user to edit the refimagefindpars configObj in a TEAL GUI
        """
    teal.teal(refimagefindpars.__taskname__, returnAs=None,
              autoClose=True, loadOnly=False, canExecute=False)


@util.with_logging
def run(configobj):
    """ Primary Python interface for image registration code
        This task replaces 'tweakshifts'
    """
    print('TweakReg Version %s(%s) started at: %s \n'%(
                    __version__,__version_date__,util._ptime()[0]))
    util.print_pkg_versions()

    # make sure 'updatewcs' is set to False when running from GUI or if missing
    # from configObj:
    if 'updatewcs' not in configobj:
        configobj['updatewcs'] = False

    # Check to see whether or not the imagefindpars parameters have
    # already been loaded, as done through the python interface.
    # Repeat for refimagefindpars
    if PSET_SECTION not in configobj:
        # Manage PSETs for source finding algorithms
        _managePsets(configobj, PSET_SECTION, imagefindpars.__taskname__)
    #print configobj[PSET_SECTION]
    if PSET_SECTION_REFIMG not in configobj:
        # Manage PSETs for source finding algorithms in reference image
        _managePsets(configobj, PSET_SECTION_REFIMG,
                     refimagefindpars.__taskname__)

    # print out user set input parameter values for running this task
    log.info("USER INPUT PARAMETERS common to all Processing Steps:")
    util.printParams(configobj, log=log)

    # start interpretation of input parameters
    input = configobj['input']
    # Start by interpreting the inputs
    use_catfile = True
    expand_refcat = configobj['expand_refcat']
    enforce_user_order = configobj['enforce_user_order']

    filenames,catnames = tweakutils.parse_input(
        input, sort_wildcards=not enforce_user_order
    )

    catdict = {}
    for indx,f in enumerate(filenames):
        if catnames is not None and len(catnames) > 0:
            catdict[f] = catnames[indx]
        else:
            catdict[f] = None

    if not filenames:
        print('No filenames matching input %r were found.' % input)
        raise IOError

    # Verify that files are writable (based on file permissions) so that
    #    they can be updated if either 'updatewcs' or 'updatehdr' have
    #    been turned on (2 cases which require updating the input files)
    if configobj['updatewcs'] or configobj['UPDATE HEADER']['updatehdr']:
        filenames = util.verifyFilePermissions(filenames)
        if filenames is None or len(filenames) == 0:
            raise IOError

    if configobj['UPDATE HEADER']['updatehdr']:
        wname = configobj['UPDATE HEADER']['wcsname']
        # verify that a unique WCSNAME has been specified by the user
        if not configobj['UPDATE HEADER']['reusename']:
            for fname in filenames:
                uniq = util.verifyUniqueWcsname(fname,wname)
                if not uniq:
                    errstr = 'WCSNAME "%s" already present in "%s".  '%(wname,fname)+\
                    'A unique value for the "wcsname" parameter needs to be ' + \
                    'specified. \n\nQuitting!'
                    print(textutil.textbox(errstr,width=60))
                    raise IOError

    if configobj['updatewcs']:
        print('\nRestoring WCS solutions to original state using updatewcs...\n')
        updatewcs.updatewcs(filenames)

    if catnames in [None,'',' ','INDEF'] or len(catnames) == 0:
        catfile_par = configobj['COORDINATE FILE DESCRIPTION']['catfile']
        # check to see whether the user specified input catalogs through other parameters
        if catfile_par not in [None,'',' ','INDEF']:
            # read in catalog file list provided by user
            catnames,catdict = tweakutils.parse_atfile_cat('@'+catfile_par)
        else:
            use_catfile = False

        if 'exclusions' in configobj and \
            configobj['exclusions'] not in [None,'',' ','INDEF']:
            if os.path.exists(configobj['exclusions']):
                excl_files, excl_dict = tweakutils.parse_atfile_cat(
                    '@'+configobj['exclusions'])

                # make sure the dictionary is well formed and that keys are base
                # file names and that exclusion files have been expanded:
                exclusion_files = []
                exclusion_dict = {}
                rootpath = os.path.abspath(
                    os.path.split(configobj['exclusions'])[0]
                )

                for f in excl_dict.keys():
                    print(f)
                    bf = os.path.basename(f)
                    exclusion_files.append(bf)
                    reglist = excl_dict[f]

                    if reglist is None:
                        exclusion_dict[bf] = None
                        continue

                    new_reglist = []
                    for regfile in reglist:
                        if regfile in [ None, 'None', '', ' ', 'INDEF' ]:
                            new_reglist.append(None)
                        else:
                            abs_regfile = os.path.normpath(
                                os.path.join(rootpath, regfile)
                            )
                            new_reglist.append(abs_regfile)

                    exclusion_dict[bf] = new_reglist

            else:
                raise IOError('Could not find specified exclusions file "{:s}"'
                      .format(configobj['exclusions']))

        else:
            exclusion_files = [None]*len(filenames)
            exclusion_dict = {}

            for f in filenames:
                exclusion_dict[os.path.basename(f)] = None

    # Verify that we have the same number of catalog files as input images
    if catnames is not None and (len(catnames) > 0):
        missed_files = []

        for f in filenames:
            if f not in catdict:
                missed_files.append(f)
            if len(missed_files) > 0:
                print('The input catalogs does not contain entries for the following images:')
                print(missed_files)
                raise IOError
    else:
        # setup array of None values as input to catalog parameter for Image class
        catnames = [None]*len(filenames)
        use_catfile = False

    # convert input images and any provided catalog file names into Image objects
    input_images = []
    # copy out only those parameters needed for Image class
    catfile_kwargs = tweakutils.get_configobj_root(configobj)
    # define default value for 'xyunits' assuming sources to be derived from image directly
    catfile_kwargs['xyunits'] = 'pixels' # initialized here, required by Image class
    del catfile_kwargs['exclusions']

    if use_catfile:
        # reset parameters based on parameter settings in this section
        catfile_kwargs.update(configobj['COORDINATE FILE DESCRIPTION'])
        for sort_par in imgclasses.sortKeys:
            catfile_kwargs['sort_'+sort_par] = catfile_kwargs[sort_par]
    # Update parameter set with 'SOURCE FINDING PARS' now
    catfile_kwargs.update(configobj[PSET_SECTION])
    uphdr_par = configobj['UPDATE HEADER']
    hdrlet_par = configobj['HEADERLET CREATION']
    objmatch_par = configobj['OBJECT MATCHING PARAMETERS']
    catfit_pars = configobj['CATALOG FITTING PARAMETERS']

    catfit_pars['minobj'] = objmatch_par['minobj']
    objmatch_par['residplot'] = catfit_pars['residplot']

    hdrlet_par.update(uphdr_par) # default hdrlet name
    catfile_kwargs['updatehdr'] = uphdr_par['updatehdr']

    shiftpars = configobj['OPTIONAL SHIFTFILE OUTPUT']

    # verify a valid hdrname was provided, if headerlet was set to True
    imgclasses.verify_hdrname(**hdrlet_par)

    print('\nFinding shifts for: ')
    for f in filenames:
        print('    {}'.format(f))

    log.info("USER INPUT PARAMETERS for finding sources for each input image:")
    util.printParams(catfile_kwargs, log=log)

    try:
        minsources = max(1, catfit_pars['minobj'])
        omitted_images = []
        all_input_images = []
        for imgnum in range(len(filenames)):
            # Create Image instances for all input images
            try:
                regexcl = exclusion_dict[os.path.basename(filenames[imgnum])]
            except KeyError:
                regexcl = None
                pass

            img = imgclasses.Image(filenames[imgnum],
                                   input_catalogs=catdict[filenames[imgnum]],
                                   exclusions=regexcl,
                                   **catfile_kwargs)

            all_input_images.append(img)
            if img.num_sources < minsources:
                warn_str = "Image '{}' will not be aligned " \
                           "since it contains fewer than {} sources." \
                           .format(img.name, minsources)
                print('\nWARNING: {}\n'.format(warn_str))
                log.warning(warn_str)
                omitted_images.append(img)
                continue
            input_images.append(img)

    except KeyboardInterrupt:
        for img in input_images:
            img.close()
        print('Quitting as a result of user request (Ctrl-C)...')
        return
    # create set of parameters to pass to RefImage class
    kwargs = tweakutils.get_configobj_root(configobj)
    # Determine a reference image or catalog and
    #    return the full list of RA/Dec positions
    # Determine what WCS needs to be used for reference tangent plane
    refcat_par = configobj['REFERENCE CATALOG DESCRIPTION']
    if refcat_par['refcat'] not in [None,'',' ','INDEF']: # User specified a catalog to use
        # Update kwargs with reference catalog parameters
        kwargs.update(refcat_par)

    # input_images list can be modified below.
    # So, make a copy of the original:
    input_images_orig_copy = copy(input_images)
    do_match_refimg = False

    # otherwise, extract the catalog from the first input image source list
    if configobj['refimage'] not in [None, '',' ','INDEF']: # User specified an image to use
        # A hack to allow different source finding parameters for
        # the reference image:
        ref_sourcefind_pars = \
            tweakutils.get_configobj_root(configobj[PSET_SECTION_REFIMG])
        ref_catfile_kwargs = catfile_kwargs.copy()
        ref_catfile_kwargs.update(ref_sourcefind_pars)
        ref_catfile_kwargs['updatehdr'] = False

        log.info("USER INPUT PARAMETERS for finding sources for "
                 "the reference image:")
        util.printParams(ref_catfile_kwargs, log=log)

        #refimg = imgclasses.Image(configobj['refimage'],**catfile_kwargs)
        # Check to see whether the user specified a separate catalog
        #    of reference source positions and replace default source list with it
        if refcat_par['refcat'] not in [None,'',' ','INDEF']: # User specified a catalog to use
            ref_source = refcat_par['refcat']
            cat_src = ref_source
            xycat = None
            cat_src_type = 'catalog'
        else:
            try:
                regexcl = exclusion_dict[configobj['refimage']]
            except KeyError:
                regexcl = None
                pass

            refimg = imgclasses.Image(configobj['refimage'],
                                      exclusions=regexcl,
                                      **ref_catfile_kwargs)
            ref_source = refimg.all_radec
            cat_src = None
            xycat = refimg.xy_catalog
            cat_src_type = 'image'

        try:
            if 'use_sharp_round' in ref_catfile_kwargs:
                kwargs['use_sharp_round'] = ref_catfile_kwargs['use_sharp_round']
            refimage = imgclasses.RefImage(configobj['refimage'],
                            ref_source, xycatalog=xycat,
                            cat_origin=cat_src, **kwargs)
            refwcs_fname = refimage.wcs.filename
            if cat_src is not None:
                refimage.name = cat_src

        except KeyboardInterrupt:
            refimage.close()
            for img in input_images:
                img.close()
            print('Quitting as a result of user request (Ctrl-C)...')
            return

        if len(input_images) < 1:
            warn_str = "Fewer than two images are available for alignment. " \
                       "Quitting..."
            print('\nWARNING: {}\n'.format(warn_str))
            log.warning(warn_str)
            for img in input_images:
                img.close()
            return

        image = _max_overlap_image(refimage, input_images, expand_refcat,
                                   enforce_user_order)

    elif refcat_par['refcat'] not in [None,'',' ','INDEF']:
        # a reference catalog is provided but not the reference image/wcs
        if len(input_images) < 1:
            warn_str = "No images available for alignment. Quitting..."
            print('\nWARNING: {}\n'.format(warn_str))
            log.warning(warn_str)
            for img in input_images:
                img.close()
            return

        if len(input_images) == 1:
            image = input_images.pop(0)
        else:
            image, image2 = _max_overlap_pair(input_images, expand_refcat,
                                              enforce_user_order)
            input_images.insert(0, image2)

        # Workaround the defect described in ticket:
        # http://redink.stsci.edu/trac/ssb/stsci_python/ticket/1151
        refwcs = []
        for i in all_input_images:
            refwcs.extend(i.get_wcs())
        kwargs['ref_wcs_name'] = image.get_wcs()[0].filename

        # A hack to allow different source finding parameters for
        # the reference image:
        ref_sourcefind_pars = \
            tweakutils.get_configobj_root(configobj[PSET_SECTION_REFIMG])
        ref_catfile_kwargs = catfile_kwargs.copy()
        ref_catfile_kwargs.update(ref_sourcefind_pars)
        ref_catfile_kwargs['updatehdr'] = False

        log.info("USER INPUT PARAMETERS for finding sources for "
                 "the reference image (not used):")
        util.printParams(ref_catfile_kwargs, log=log)

        ref_source = refcat_par['refcat']
        cat_src = ref_source
        xycat = None

        try:
            if 'use_sharp_round' in ref_catfile_kwargs:
                kwargs['use_sharp_round'] = ref_catfile_kwargs['use_sharp_round']
            kwargs['find_bounding_polygon'] = True
            refimage = imgclasses.RefImage(refwcs,
                                           ref_source, xycatalog=xycat,
                                           cat_origin=cat_src, **kwargs)
            refwcs_fname = refimage.wcs.filename
            refimage.name = cat_src
            cat_src_type = 'catalog'
        except KeyboardInterrupt:
            refimage.close()
            for img in input_images:
                img.close()
            print('Quitting as a result of user request (Ctrl-C)...')
            return

    else:
        if len(input_images) < 2:
            warn_str = "Fewer than two images available for alignment. " \
                "Quitting..."
            print('\nWARNING: {}\n'.format(warn_str))
            log.warning(warn_str)
            for img in input_images:
                img.close()
            return

        kwargs['use_sharp_round'] = catfile_kwargs['use_sharp_round']

        cat_src = None

        refimg, image = _max_overlap_pair(input_images, expand_refcat,
                                          enforce_user_order)

        refwcs = []
        #refwcs.extend(refimg.get_wcs())
        #refwcs.extend(image.get_wcs())
        #for i in input_images:
            #refwcs.extend(i.get_wcs())
        # Workaround the defect described in ticket:
        # http://redink.stsci.edu/trac/ssb/stsci_python/ticket/1151
        for i in all_input_images:
            refwcs.extend(i.get_wcs())
        kwargs['ref_wcs_name'] = refimg.get_wcs()[0].filename

        try:
            ref_source = refimg.all_radec
            refimage = imgclasses.RefImage(refwcs, ref_source,
                                        xycatalog=refimg.xy_catalog, **kwargs)
            refwcs_fname = refimg.name
            cat_src_type = 'image'
        except KeyboardInterrupt:
            refimage.close()
            for img in input_images:
                img.close()
            print('Quitting as a result of user request (Ctrl-C)...')
            return

        omitted_images.insert(0, refimg) # refimage *must* be first
        do_match_refimg = True

    print("\n{0}\nPerforming alignment in the projection plane defined by the "
          "WCS\nderived from '{1}'\n{0}\n".format('='*63, refwcs_fname))

    if refimage.outxy is not None:
        if cat_src is None:
            cat_src = refimage.name

        try:
            log.info("USER INPUT PARAMETERS for matching sources:")
            util.printParams(objmatch_par, log=log)

            log.info("USER INPUT PARAMETERS for fitting source lists:")
            util.printParams(configobj['CATALOG FITTING PARAMETERS'], log=log)

            if hdrlet_par['headerlet']:
                log.info("USER INPUT PARAMETERS for creating headerlets:")
                util.printParams(hdrlet_par, log=log)

            if shiftpars['shiftfile']:
                log.info("USER INPUT PARAMETERS for creating a shiftfile:")
                util.printParams(shiftpars, log=log)

            # Now, apply reference WCS to each image's sky positions as well as the
            # reference catalog sky positions,
            # then perform the fit between the reference catalog positions and
            #    each image's positions
            quit_immediately = False
            xycat_lines = ''
            xycat_filename = None

            for img in input_images_orig_copy:
                if xycat_filename is None:
                    xycat_filename = img.rootname+'_xy_catfile.list'
                # Keep a record of all the generated input_xy catalogs
                xycat_lines += img.get_xy_catnames()

            retry_flags = len(input_images)*[0]
            objmatch_par['cat_src_type'] = cat_src_type
            while image is not None:
                print ('\n'+'='*20)
                print ('Performing fit for: {}\n'.format(image.name))
                image.match(refimage, quiet_identity=False, **objmatch_par)
                assert(len(retry_flags) == len(input_images))

                if not image.goodmatch:
                    # we will try to match it again once reference catalog
                    # has expanded with new sources:
                    #if expand_refcat:
                    input_images.append(image)
                    retry_flags.append(1)
                    if len(retry_flags) > 0 and retry_flags[0] == 0:
                        retry_flags.pop(0)
                        image = input_images.pop(0)
                        # try to match next image in the list
                        continue
                    else:
                        # no new sources have been added to the reference
                        # catalog and we have already tried to match
                        # images to the existing reference catalog

                        #input_images.append(image) # <- add it back for later reporting
                        #retry_flags.append(1)
                        break

                image.performFit(**catfit_pars)
                if image.quit_immediately:
                    quit_immediately = True
                    image.close()
                    break

                # add unmatched sources to the reference catalog
                # (to expand it):
                if expand_refcat:
                    refimage.append_not_matched_sources(image)

                image.updateHeader(wcsname=uphdr_par['wcsname'],
                                    reusename=uphdr_par['reusename'])
                if hdrlet_par['headerlet']:
                    image.writeHeaderlet(**hdrlet_par)
                if configobj['clean']:
                    image.clean()
                image.close()

                if refimage.dirty and len(input_images) > 0:
                    # The reference catalog has been updated with new sources.
                    # Clear retry flags and get next image:
                    image = _max_overlap_image(
                        refimage, input_images, expand_refcat,
                        enforce_user_order
                    )
                    retry_flags = len(input_images)*[0]
                    refimage.clear_dirty_flag()
                elif len(input_images) > 0 and retry_flags[0] == 0:
                    retry_flags.pop(0)
                    image = input_images.pop(0)
                else:
                    break

            assert(len(retry_flags) == len(input_images))

            if not quit_immediately:
                # process images that have not been matched in order to
                # update their headers:
                si = 0
                if do_match_refimg:
                    image = omitted_images[0]
                    image.match(refimage, quiet_identity=True, **objmatch_par)
                    si = 1

                # process omitted (from start) images separately:
                for image in omitted_images[si:]:
                    image.match(refimage, quiet_identity=False, **objmatch_par)

                # add to the list of omitted images, images that could not
                # be matched:
                omitted_images.extend(input_images)

                if len(input_images) > 0:
                    print("\nUnable to match the following images:")
                    print("-------------------------------------")
                    for image in input_images:
                        print(image.name)
                    print("")

                # update headers:
                for image in omitted_images:
                    image.performFit(**catfit_pars)
                    if image.quit_immediately:
                        quit_immediately = True
                        image.close()
                        break
                    image.updateHeader(wcsname=uphdr_par['wcsname'],
                                    reusename=uphdr_par['reusename'])
                    if hdrlet_par['headerlet']:
                        image.writeHeaderlet(**hdrlet_par)
                    if configobj['clean']:
                        image.clean()
                    image.close()

                if configobj['writecat'] and not configobj['clean']:
                    # Write out catalog file recording input XY catalogs used
                    # This file will be suitable for use as input to 'tweakreg'
                    # as the 'catfile' parameter
                    if os.path.exists(xycat_filename):
                        os.remove(xycat_filename)
                    f = open(xycat_filename, mode='w')
                    f.writelines(xycat_lines)
                    f.close()

                    if expand_refcat:
                        base_reg_name = os.path.splitext(
                            os.path.basename(cat_src))[0]
                        refimage.write_skycatalog(
                            'cumulative_sky_refcat_{:s}.coo' \
                            .format(base_reg_name),
                            show_flux=True, show_id=True
                        )

                # write out shiftfile (if specified)
                if shiftpars['shiftfile']:
                    tweakutils.write_shiftfile(input_images_orig_copy,
                                               shiftpars['outshifts'],
                                               outwcs=shiftpars['outwcs'])

        except KeyboardInterrupt:
            refimage.close()
            for img in input_images_orig_copy:
                img.close()
                del img
            print ('Quitting as a result of user request (Ctrl-C)...')
            return
    else:
        print ('No valid sources in reference frame. Quitting...')
        return


def _overlap_matrix(images):
    nimg = len(images)
    m = np.zeros((nimg,nimg), dtype=np.float)
    for i in range(nimg):
        for j in range(i+1,nimg):
            p = images[i].skyline.intersection(images[j].skyline)
            area = np.fabs(p.area())
            m[j,i] = area
            m[i,j] = area
    return m


def _max_overlap_pair(images, expand_refcat, enforce_user_order):
    assert(len(images) > 1)
    if len(images) == 2 or not expand_refcat or enforce_user_order:
        # for the special case when only two images are provided
        # return (refimage, image) in the same order as provided in 'images'.
        # Also, when ref. catalog is static - revert to old tweakreg behavior
        im1 = images.pop(0) # reference image
        im2 = images.pop(0)
        return (im1, im2)

    m = _overlap_matrix(images)
    imgs = [f.name for f in images]
    n = m.shape[0]
    index = m.argmax()
    i = index // n
    j = index % n
    si = np.sum(m[i])
    sj = np.sum(m[:,j])

    if si < sj:
        c = j
        j = i
        i = c

    if i < j:
        j -= 1

    im1 = images.pop(i) # reference image
    im2 = images.pop(j)

    # Sort the remaining of the input list of images by overlap area
    # with the reference image (in decreasing order):
    row = m[i]
    row = np.delete(row, i)
    row = np.delete(row, j)
    sorting_indices = np.argsort(row)[::-1]
    images_arr = np.asarray(images)[sorting_indices]
    while len(images) > 0:
        del images[0]
    for k in range(images_arr.shape[0]):
        images.append(images_arr[k])

    return (im1, im2)


def _max_overlap_image(refimage, images, expand_refcat, enforce_user_order):
    nimg = len(images)
    assert(nimg > 0)
    if not expand_refcat or enforce_user_order:
        # revert to old tweakreg behavior
        return images.pop(0)

    area = np.zeros(nimg, dtype=np.float)
    for i in range(nimg):
        area[i] = np.fabs(
            refimage.skyline.intersection(images[i].skyline).area()
        )

    # Sort the remaining of the input list of images by overlap area
    # with the reference image (in decreasing order):
    sorting_indices = np.argsort(area)[::-1]
    images_arr = np.asarray(images)[sorting_indices]
    while len(images) > 0:
        del images[0]
    for k in range(images_arr.shape[0]):
        images.append(images_arr[k])

    return images.pop(0)


#
# Primary interface for running this task from Python
#
def TweakReg(files=None, editpars=False, configobj=None, imagefindcfg=None,
             refimagefindcfg=None, **input_dict):
    """
    """
    # support input of filenames from command-line without a parameter name
    # then copy this into input_dict for merging with TEAL ConfigObj parameters

    # Get default or user-specified configobj for primary task
    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("TweakReg() needs either 'files' or "
                        "'configobj' arguments")

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

    # Merge PSET configobj with full task configobj
    _managePsets(configobj, PSET_SECTION, imagefindpars.__taskname__,
                 iparsobj=imagefindcfg, input_dict=input_dict)
    _managePsets(configobj, PSET_SECTION_REFIMG,
                 refimagefindpars.__taskname__, iparsobj=refimagefindcfg)
    # !! NOTE - the above line needs to be done so that getDefaultConfigObj()
    # can merge in input_dict, however the TEAL GUI is not going to understand
    # the extra section, (or use it), so work needs to be done here - some
    # more thinking about how to accomplish what we want to with PSETS.
    # !! For now, warn them that imagefindpars args will be ignored in GUI.
    if editpars and input_dict:
        idkeys = input_dict.keys()
        for i in idkeys:
            if i in configobj[PSET_SECTION]:
                print('WARNING: ignoring imagefindpars setting "'+i+
                     '='+str(input_dict[i])+'", for now please enter directly into TEAL.')
                input_dict.pop(i)
        del configobj[PSET_SECTION] # force run() to pull it again after GUI use
        del configobj[PSET_SECTION_REFIMG] # force run() to pull it again after GUI use

    # 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))
    except ValueError:
        print("Problem with input parameters. Quitting...")
        return

    if configObj is None:
        return
    # If 'editpars' was set to True, util.getDefaultConfigObj() will have
    # already called 'run()'.
    if not editpars:
        # Pass full set of parameters on to the task
        run(configObj)


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


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