#!/usr/bin/env python3
#

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright by California Institute of Technology. ALL RIGHTS RESERVED.
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
# http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 
# United States Government Sponsorship acknowledged. This software is subject to
# U.S. export control laws and regulations and has been classified as 'EAR99 NLR'
# (No [Export] License Required except when exporting to an embargoed country,
# end user, or in support of a prohibited end use). By downloading this software,
# the user agrees to comply with all applicable U.S. export laws and regulations.
# The user has the responsibility to obtain export licenses, or other export
# authority as may be required before exporting this software to any 'EAR99'
# embargoed foreign country or citizen of those countries.
#
# Author: Heresh Fattahi
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~






from __future__ import print_function
import time
import sys
from isce import logging

import isce
import isceobj
import iscesys
from iscesys.Component.Application import Application
from iscesys.Compatibility import Compatibility
from iscesys.Component.Configurable import SELF
import isceobj.StripmapProc as StripmapProc
from isceobj.Scene.Frame import FrameMixin
from isceobj.Util.decorators import use_api

logger = logging.getLogger('isce.insar')


SENSOR_NAME = Application.Parameter(
        'sensorName',
        public_name='sensor name',
        default = None,
        type = str,
        mandatory = False,
        doc = 'Sensor name for both reference and secondary')


REFERENCE_SENSOR_NAME = Application.Parameter(
        'referenceSensorName',
        public_name='reference sensor name',
        default = None,
        type=str,
        mandatory = True,
        doc = "Reference sensor name if mixing sensors")

SECONDARY_SENSOR_NAME = Application.Parameter(
        'secondarySensorName',
        public_name='secondary sensor name',
        default = None,
        type=str,
        mandatory = True,
        doc = "Secondary sensor name if mixing sensors")


CORRELATION_METHOD = Application.Parameter(
   'correlation_method',
   public_name='correlation_method',
   default='cchz_wave',
   type=str,
   mandatory=False,
   doc=(
   """Select coherence estimation method:
      cchz=cchz_wave
      phase_gradient=phase gradient"""
        )
                                           )
REFERENCE_DOPPLER_METHOD = Application.Parameter(
    'referenceDopplerMethod',
    public_name='reference doppler method',
    default=None,
    type=str, mandatory=False,
    doc= "Doppler calculation method.Choices: 'useDOPIQ', 'useDefault'."
)

SECONDARY_DOPPLER_METHOD = Application.Parameter(
    'secondaryDopplerMethod',
    public_name='secondary doppler method',
    default=None,
    type=str, mandatory=False,
    doc="Doppler calculation method. Choices: 'useDOPIQ','useDefault'.")


UNWRAPPER_NAME = Application.Parameter(
    'unwrapper_name',
    public_name='unwrapper name',
    default='grass',
    type=str,
    mandatory=False,
    doc="Unwrapping method to use. To be used in  combination with UNWRAP."
)

DO_UNWRAP = Application.Parameter(
    'do_unwrap',
    public_name='do unwrap',
    default=True,
    type=bool,
    mandatory=False,
    doc="True if unwrapping is desired. To be unsed in combination with UNWRAPPER_NAME."
)

DO_UNWRAP_2STAGE = Application.Parameter(
    'do_unwrap_2stage',
    public_name='do unwrap 2 stage',
    default=False,
    type=bool,
    mandatory=False,
    doc="True if unwrapping is desired. To be unsed in combination with UNWRAPPER_NAME."
)

UNWRAPPER_2STAGE_NAME = Application.Parameter(
    'unwrapper_2stage_name',
    public_name='unwrapper 2stage name',
    default='REDARC0',
    type=str,
    mandatory=False,
    doc="2 Stage Unwrapping method to use. Available: MCF, REDARC0, REDARC1, REDARC2"
)

SOLVER_2STAGE = Application.Parameter(
    'solver_2stage',
    public_name='SOLVER_2STAGE',
    default='pulp',
    type=str,
    mandatory=False,
    doc='Linear Programming Solver for 2Stage; Options: pulp, gurobi, glpk; Used only for Redundant Arcs'
)

USE_HIGH_RESOLUTION_DEM_ONLY = Application.Parameter(
    'useHighResolutionDemOnly',
    public_name='useHighResolutionDemOnly',
    default=False,
    type=int,
    mandatory=False,
    doc=(
    """If True and a dem is not specified in input, it will only
    download the SRTM highest resolution dem if it is available
    and fill the missing portion with null values (typically -32767)."""
    )
)

DEM_FILENAME = Application.Parameter(
     'demFilename',
     public_name='demFilename',
     default='',
     type=str,
     mandatory=False,
     doc="Filename of the DEM init file"
)

REGION_OF_INTEREST = Application.Parameter(
        'regionOfInterest',
        public_name = 'regionOfInterest',
        default = None,
        container = list,
        type = float,
        doc = 'Region of interest - South, North, West, East in degrees')


GEOCODE_BOX = Application.Parameter(
    'geocode_bbox',
    public_name='geocode bounding box',
    default = None,
    container=list,
    type=float,
    doc='Bounding box for geocoding - South, North, West, East in degrees'
                                    )

GEO_POSTING = Application.Parameter(
    'geoPosting',
    public_name='geoPosting',
    default=None,
    type=float,
    mandatory=False,
    doc=(
    "Output posting for geocoded images in degrees (latitude = longitude)"
    )
                                    )

POSTING = Application.Parameter(
    'posting',
    public_name='posting',
    default=30,
    type=int,
    mandatory=False,
    doc="posting for interferogram")


NUMBER_RANGE_LOOKS = Application.Parameter(
    'numberRangeLooks',
    public_name='range looks',
    default=None,
    type=int,
    mandatory=False,
    doc='Number of range looks'
                                    )

NUMBER_AZIMUTH_LOOKS = Application.Parameter(
    'numberAzimuthLooks',
    public_name='azimuth looks',
    default=None,
    type=int,
    mandatory=False,
    doc='Number of azimuth looks'
                                 )

FILTER_STRENGTH = Application.Parameter('filterStrength',
                                      public_name='filter strength',
                                      default=0.5,
                                      type=float,
                                      mandatory=False,
                                      doc='')

############################################## Modified by V.Brancato 10.07.2019
DO_RUBBERSHEETINGAZIMUTH = Application.Parameter('doRubbersheetingAzimuth', 
                                      public_name='do rubbersheetingAzimuth',
                                      default=False,
                                      type=bool,
                                      mandatory=False,
                                      doc='')
DO_RUBBERSHEETINGRANGE = Application.Parameter('doRubbersheetingRange', 
                                      public_name='do rubbersheetingRange',
                                      default=False,
                                      type=bool,
                                      mandatory=False,
                                      doc='')
#################################################################################
RUBBERSHEET_SNR_THRESHOLD = Application.Parameter('rubberSheetSNRThreshold',
                                      public_name='rubber sheet SNR Threshold',
                                      default = 5.0,
                                      type = float,
                                      mandatory = False,
                                      doc='')

RUBBERSHEET_FILTER_SIZE = Application.Parameter('rubberSheetFilterSize',
                                      public_name='rubber sheet filter size',
                                      default = 9,
                                      type = int,
                                      mandatory = False,
                                      doc = '')

DO_DENSEOFFSETS  = Application.Parameter('doDenseOffsets',
                                      public_name='do denseoffsets',
                                      default=False,
                                      type=bool,
                                      mandatory=False,
                                      doc='')

DENSE_WINDOW_WIDTH = Application.Parameter('denseWindowWidth',
                                      public_name='dense window width',
                                      default=64,
                                      type = int,
                                      mandatory = False,
                                      doc = '')

DENSE_WINDOW_HEIGHT = Application.Parameter('denseWindowHeight',
                                      public_name='dense window height',
                                      default=64,
                                      type = int,
                                      mandatory = False,
                                      doc = '')


DENSE_SEARCH_WIDTH = Application.Parameter('denseSearchWidth',
                                      public_name='dense search width',
                                      default=20,
                                      type = int,
                                      mandatory = False,
                                      doc = '')

DENSE_SEARCH_HEIGHT = Application.Parameter('denseSearchHeight',
                                      public_name='dense search height',
                                      default=20,
                                      type = int,
                                      mandatory = False,
                                      doc = '')

DENSE_SKIP_WIDTH = Application.Parameter('denseSkipWidth',
                                      public_name='dense skip width',
                                      default=32,
                                      type = int,
                                      mandatory = False,
                                      doc = '')

DENSE_SKIP_HEIGHT = Application.Parameter('denseSkipHeight',
                                      public_name='dense skip height',
                                      default=32,
                                      type = int,
                                      mandatory = False,
                                      doc = '')

DO_SPLIT_SPECTRUM = Application.Parameter('doSplitSpectrum',
                                      public_name='do split spectrum',
                                      default = False,
                                      type = bool,
                                      mandatory = False,
                                      doc = '')

DO_DISPERSIVE = Application.Parameter('doDispersive',
                                      public_name='do dispersive',
                                      default=False,
                                      type=bool,
                                      mandatory=False,
                                      doc='')

GEOCODE_LIST = Application.Parameter(
    'geocode_list',
     public_name='geocode list',
     default = None,
     container=list,
     type=str,
     doc = "List of products to geocode."
                                      )

OFFSET_GEOCODE_LIST = Application.Parameter(
        'off_geocode_list',
        public_name='offset geocode list',
        default=None,
        container=list,
        mandatory=False,
        doc='List of offset-specific files to geocode')

HEIGHT_RANGE = Application.Parameter(
        'heightRange',
        public_name = 'height range',
        default = None,
        container = list,
        type = float,
        doc = 'Altitude range in scene for cropping')

PICKLE_DUMPER_DIR = Application.Parameter(
    'pickleDumpDir',
    public_name='pickle dump directory',
    default='PICKLE',
    type=str,
    mandatory=False,
    doc=(
    "If steps is used, the directory in which to store pickle objects."
    )
                                          )
PICKLE_LOAD_DIR = Application.Parameter(
    'pickleLoadDir',
    public_name='pickle load directory',
    default='PICKLE',
    type=str,
    mandatory=False,
    doc=(
    "If steps is used, the directory from which to retrieve pickle objects."
    )
                                        )

RENDERER = Application.Parameter(
    'renderer',
    public_name='renderer',
    default='xml',
    type=str,
    mandatory=True,
    doc=(
    "Format in which the data is serialized when using steps. Options are xml (default) or pickle."
    )
                                        )

DISPERSIVE_FILTER_FILLING_METHOD = Application.Parameter('dispersive_filling_method',
                                            public_name = 'dispersive filter filling method',
                                            default='nearest_neighbour',
                                            type=str,
                                            mandatory=False,
                                            doc='method to fill the holes left by masking the ionospheric phase estimate')
					    
DISPERSIVE_FILTER_KERNEL_XSIZE = Application.Parameter('kernel_x_size',
                                      public_name='dispersive filter kernel x-size',
                                      default=800,
                                      type=float,
                                      mandatory=False,
                                      doc='kernel x-size for the Gaussian low-pass filtering of the dispersive and non-disperive phase')

DISPERSIVE_FILTER_KERNEL_YSIZE = Application.Parameter('kernel_y_size',
                                      public_name='dispersive filter kernel y-size',
                                      default=800,
                                      type=float,
                                      mandatory=False,
                                      doc='kernel y-size for the Gaussian low-pass filtering of the dispersive and non-disperive phase')

DISPERSIVE_FILTER_KERNEL_SIGMA_X = Application.Parameter('kernel_sigma_x',
                                      public_name='dispersive filter kernel sigma_x',
                                      default=100,
                                      type=float,
                                      mandatory=False,
                                      doc='kernel sigma_x for the Gaussian low-pass filtering of the dispersive and non-disperive phase')

DISPERSIVE_FILTER_KERNEL_SIGMA_Y = Application.Parameter('kernel_sigma_y',
                                      public_name='dispersive filter kernel sigma_y',
                                      default=100,
                                      type=float,
                                      mandatory=False,
                                      doc='kernel sigma_y for the Gaussian low-pass filtering of the dispersive and non-disperive phase')

DISPERSIVE_FILTER_KERNEL_ROTATION = Application.Parameter('kernel_rotation',
                                      public_name='dispersive filter kernel rotation',
                                      default=0.0,
                                      type=float,
                                      mandatory=False,
                                      doc='kernel rotation angle for the Gaussian low-pass filtering of the dispersive and non-disperive phase')

DISPERSIVE_FILTER_ITERATION_NUMBER = Application.Parameter('dispersive_filter_iterations',
                                      public_name='dispersive filter number of iterations',
                                      default=5,
                                      type=int,
                                      mandatory=False,
                                      doc='number of iterations for the iterative low-pass filtering of the dispersive and non-disperive phase')

DISPERSIVE_FILTER_MASK_TYPE = Application.Parameter('dispersive_filter_mask_type',
                                      public_name='dispersive filter mask type',
                                      default="connected_components",
                                      type=str,
                                      mandatory=False,
                                      doc='The type of mask for the iterative low-pass filtering of the estimated dispersive phase. If method is coherence, then a mask based on coherence files of low-band and sub-band interferograms is generated using the mask coherence thresold which can be also setup. If method is connected_components, then mask is formed based on connected component files with non zero values. If method is phase, then pixels with zero phase values in unwrapped sub-band interferograms are masked out.')

DISPERSIVE_FILTER_COHERENCE_THRESHOLD = Application.Parameter('dispersive_filter_coherence_threshold',
                                      public_name='dispersive filter coherence threshold',
                                      default=0.5,
                                      type=float,
                                      mandatory=False,
                                      doc='Coherence threshold to generate a mask file which gets used in the iterative filtering of the dispersive and non-disperive phase')
#Facility declarations

REFERENCE = Application.Facility(
    'reference',
    public_name='Reference',
    module='isceobj.StripmapProc.Sensor',
    factory='createSensor',
    args=(SENSOR_NAME, REFERENCE_SENSOR_NAME, 'reference'),
    mandatory=False,
    doc="Reference raw data component"
                              )

SECONDARY = Application.Facility(
    'secondary',
    public_name='Secondary',
    module='isceobj.StripmapProc.Sensor',
    factory='createSensor',
    args=(SENSOR_NAME, SECONDARY_SENSOR_NAME,'secondary'),
    mandatory=False,
    doc="Secondary raw data component"
                             )

DEM_STITCHER = Application.Facility(
    'demStitcher',
    public_name='demStitcher',
    module='iscesys.DataManager',
    factory='createManager',
    args=('dem1','iscestitcher',),
    mandatory=False,
    doc="Object that based on the frame bounding boxes creates a DEM"
)

RUN_UNWRAPPER = Application.Facility(
    'runUnwrapper',
    public_name='Run unwrapper',
    module='isceobj.StripmapProc',
    factory='createUnwrapper',
    args=(SELF(), DO_UNWRAP, UNWRAPPER_NAME),
    mandatory=False,
    doc="Unwrapping module"
)

RUN_UNWRAP_2STAGE = Application.Facility(
    'runUnwrap2Stage',
    public_name='Run unwrapper 2 Stage',
    module='isceobj.TopsProc',
    factory='createUnwrap2Stage',
    args=(SELF(), DO_UNWRAP_2STAGE, UNWRAPPER_NAME),
    mandatory=False,
    doc="Unwrapping module"
)

_INSAR = Application.Facility(
    '_insar',
    public_name='insar',
    module='isceobj.StripmapProc',
    factory='createStripmapProc',
    args = ('stripmapAppContext',isceobj.createCatalog('stripmapProc')),
    mandatory=False,
    doc="InsarProc object"
)



## Common interface for stripmap insar applications.
class _RoiBase(Application, FrameMixin):

    family = 'insar'
    ## Define Class parameters in this list
    parameter_list = (SENSOR_NAME,
                      REFERENCE_SENSOR_NAME,
                      SECONDARY_SENSOR_NAME,
                      FILTER_STRENGTH,
                      CORRELATION_METHOD,
                      REFERENCE_DOPPLER_METHOD,
                      SECONDARY_DOPPLER_METHOD,
                      UNWRAPPER_NAME,
                      DO_UNWRAP,
                      DO_UNWRAP_2STAGE,
                      UNWRAPPER_2STAGE_NAME,
                      SOLVER_2STAGE,
                      USE_HIGH_RESOLUTION_DEM_ONLY,
                      DEM_FILENAME,
                      GEO_POSTING,
                      POSTING,
                      NUMBER_RANGE_LOOKS,
                      NUMBER_AZIMUTH_LOOKS,
                      GEOCODE_LIST,
                      OFFSET_GEOCODE_LIST,
                      GEOCODE_BOX,
                      REGION_OF_INTEREST,
                      HEIGHT_RANGE,
                      DO_RUBBERSHEETINGRANGE, #Modified by V. Brancato 10.07.2019
                      DO_RUBBERSHEETINGAZIMUTH,  #Modified by V. Brancato 10.07.2019
                      RUBBERSHEET_SNR_THRESHOLD,
                      RUBBERSHEET_FILTER_SIZE,
                      DO_DENSEOFFSETS,
                      DENSE_WINDOW_WIDTH,
                      DENSE_WINDOW_HEIGHT,
                      DENSE_SEARCH_WIDTH,
                      DENSE_SEARCH_HEIGHT,
                      DENSE_SKIP_WIDTH,
                      DENSE_SKIP_HEIGHT,
                      DO_SPLIT_SPECTRUM,
                      PICKLE_DUMPER_DIR,
                      PICKLE_LOAD_DIR,
                      RENDERER,
                      DO_DISPERSIVE,
                      DISPERSIVE_FILTER_FILLING_METHOD,
                      DISPERSIVE_FILTER_KERNEL_XSIZE,
                      DISPERSIVE_FILTER_KERNEL_YSIZE,
                      DISPERSIVE_FILTER_KERNEL_SIGMA_X,
                      DISPERSIVE_FILTER_KERNEL_SIGMA_Y,
                      DISPERSIVE_FILTER_KERNEL_ROTATION,
                      DISPERSIVE_FILTER_ITERATION_NUMBER,
                      DISPERSIVE_FILTER_MASK_TYPE,
                      DISPERSIVE_FILTER_COHERENCE_THRESHOLD)

    facility_list = (REFERENCE,
                     SECONDARY,
                     DEM_STITCHER,
                     RUN_UNWRAPPER,
                     RUN_UNWRAP_2STAGE,
                     _INSAR)


    _pickleObj = "_insar"

    def __init__(self, family='', name='',cmdline=None):
        import isceobj
        super().__init__(family=family, name=name,
            cmdline=cmdline)

        from isceobj.StripmapProc import StripmapProc
        from iscesys.StdOEL.StdOELPy import create_writer
        self._stdWriter = create_writer("log", "", True, filename="roi.log")
        self._add_methods()
        self._insarProcFact = StripmapProc
        self.timeStart = None
        return None

    def Usage(self):
        print("Usages: ")
        print("stripmapApp.py <input-file.xml>")
        print("stripmapApp.py --steps")
        print("stripmapApp.py --help")
        print("stripmapApp.py --help --steps")

    def _init(self):

        message =  (
            ("ISCE VERSION = %s, RELEASE_SVN_REVISION = %s,"+
             "RELEASE_DATE = %s, CURRENT_SVN_REVISION = %s") %
            (isce.__version__,
             isce.release_svn_revision,
             isce.release_date,
             isce.svn_revision)
            )
        logger.info(message)

        print(message)
        return None

    ## You need this to use the FrameMixin
    @property
    def frame(self):
        return self.insar.frame


    def _configure(self):

        self.insar.procDoc._addItem("ISCE_VERSION",
            "Release: %s, svn-%s, %s. Current svn-%s" %
            (isce.release_version, isce.release_svn_revision,
             isce.release_date, isce.svn_revision
            ),
            ["stripmapProc"]
            )

        #Ensure consistency in geocode_list maintained by insarApp and
        #InsarProc. If it is configured in both places, the one in insarApp
        #will be used. It is complicated to try to merge the two lists
        #because InsarProc permits the user to change the name of the files
        #and the linkage between filename and filetype is lost by the time
        #geocode_list is fully configured.  In order to safely change file
        #names and also specify the geocode_list, then insarApp should not
        #be given a geocode_list from the user.
        if(self.geocode_list is not None):
            #if geocode_list defined here, then give it to InsarProc
            #for consistency between insarApp and InsarProc and warn the user

            #check if the two geocode_lists differ in content
            g_count = 0
            for g in self.geocode_list:
                if g not in self.insar.geocode_list:
                    g_count += 1

            #warn if there are any differences in content
            if g_count > 0:
                print()
                logger.warning((
                    "Some filenames in stripmapApp.geocode_list configuration "+
                    "are different from those in StripmapProc. Using names given"+
                    " to stripmapApp."))
                print("stripmapApp.geocode_list = {}".format(self.geocode_list))
        else:
            self.geocode_list = self.insar.geocode_list


        if (self.off_geocode_list is None):
            self.off_geocode_list = self.insar.off_geocode_list
        else:
            g_count = 0
            for g in self.off_geocode_list:
                if g not in self.insar.off_geocode_list:
                    g_count += 1

            if g_count > 0:
                self.off_geocode_list = self.insar.off_geocode_list


        return None

    @property
    def insar(self):
        return self._insar
    @insar.setter
    def insar(self, value):
        self._insar = value
        return None

    @property
    def procDoc(self):
        return self.insar.procDoc
    @procDoc.setter
    def procDoc(self):
        raise AttributeError(
            "Can not assign to .insar.procDoc-- but you hit all its other stuff"
            )

    def _finalize(self):
        pass

    def help(self):
        from isceobj.Sensor import SENSORS
        print(self.__doc__)
        lsensors = list(SENSORS.keys())
        lsensors.sort()
        print("The currently supported sensors are: ", lsensors)
        return None

    def help_steps(self):
        print(self.__doc__)
        print("A description of the individual steps can be found in the README file")
        print("and also in the ISCE.pdf document")
        return

    def renderProcDoc(self):
        self.procDoc.renderXml()


    def startup(self):
        self.help()
        self._insar.timeStart = time.time()

    def endup(self):
        self.renderProcDoc()
        self._insar.timeEnd = time.time()
        if hasattr(self._insar, 'timeStart'):
            logger.info("Total Time: %i seconds" %
                        (self._insar.timeEnd-self._insar.timeStart))
        return None


    ## Add instance attribute RunWrapper functions, which emulate methods.
    def _add_methods(self):
        self.runPreprocessor = StripmapProc.createPreprocessor(self)
        self.runFormSLC = StripmapProc.createFormSLC(self)
        self.runCrop = StripmapProc.createCrop(self)
        self.runSplitSpectrum = StripmapProc.createSplitSpectrum(self)
        self.runTopo = StripmapProc.createTopo(self)
        self.runGeo2rdr = StripmapProc.createGeo2rdr(self)
        self.runResampleSlc = StripmapProc.createResampleSlc(self)
        self.runRefineSecondaryTiming = StripmapProc.createRefineSecondaryTiming(self)
        self.runDenseOffsets = StripmapProc.createDenseOffsets(self)
        self.runRubbersheetRange = StripmapProc.createRubbersheetRange(self) #Modified by V. Brancato 10.07.2019
        self.runRubbersheetAzimuth =StripmapProc.createRubbersheetAzimuth(self) #Modified by V. Brancato 10.07.2019
        self.runResampleSubbandSlc = StripmapProc.createResampleSubbandSlc(self)
        self.runInterferogram = StripmapProc.createInterferogram(self)
        self.runFilter = StripmapProc.createFilter(self)
        self.runDispersive = StripmapProc.createDispersive(self)
        self.verifyDEM = StripmapProc.createVerifyDEM(self)
        self.runGeocode = StripmapProc.createGeocode(self)
        return None

    def _steps(self):

        self.step('startup', func=self.startup,
                     doc=("Print a helpful message and "+
                          "set the startTime of processing")
                  )

        # Run a preprocessor for the two sets of frames
        self.step('preprocess',
                  func=self.runPreprocessor,
                  doc=(
                """Preprocess the reference and secondary sensor data to raw images"""
                )
                  )

        self.step('cropraw',
                func = self.runCrop,
                args=(True,))

        self.step('formslc', func=self.runFormSLC)

        self.step('cropslc', func=self.runCrop,
                args=(False,))

        # Verify whether the DEM was initialized properly.  If not, download
        # a DEM
        self.step('verifyDEM', func=self.verifyDEM)

        self.step('topo', func=self.runTopo)

        self.step('geo2rdr', func=self.runGeo2rdr)

        self.step('coarse_resample', func=self.runResampleSlc,
                    args=('coarse',))

        self.step('misregistration', func=self.runRefineSecondaryTiming)

        self.step('refined_resample', func=self.runResampleSlc,
                    args=('refined',))

        self.step('dense_offsets', func=self.runDenseOffsets)
######################################################################## Modified by V. Brancato 10.07.2019
        self.step('rubber_sheet_range', func=self.runRubbersheetRange)
	
        self.step('rubber_sheet_azimuth',func=self.runRubbersheetAzimuth)
#########################################################################

        self.step('fine_resample', func=self.runResampleSlc,
                    args=('fine',))

        self.step('split_range_spectrum', func=self.runSplitSpectrum)

        self.step('sub_band_resample', func=self.runResampleSubbandSlc,
                    args=(True,))

        self.step('interferogram', func=self.runInterferogram)

        self.step('sub_band_interferogram', func=self.runInterferogram,
                args=("sub",))

        self.step('filter', func=self.runFilter,
                  args=(self.filterStrength,))

        self.step('filter_low_band', func=self.runFilter,
                  args=(self.filterStrength,"low",))

        self.step('filter_high_band', func=self.runFilter,
                  args=(self.filterStrength,"high",))

        self.step('unwrap', func=self.runUnwrapper)

        self.step('unwrap_low_band', func=self.runUnwrapper, args=("low",))

        self.step('unwrap_high_band', func=self.runUnwrapper, args=("high",))

        self.step('ionosphere', func=self.runDispersive)

        self.step('geocode', func=self.runGeocode,
                args=(self.geocode_list, self.geocode_bbox))

        self.step('geocodeoffsets', func=self.runGeocode,
                args=(self.off_geocode_list, self.geocode_bbox, True))

        return None

    ## Main has the common start to both insarApp and dpmApp.
    #@use_api
    def main(self):
        self.timeStart = time.time()
        self.help()

        # Run a preprocessor for the two sets of frames
        self.runPreprocessor()

        #Crop raw data if desired
        self.runCrop(True)

        self.runFormSLC()

        self.runCrop(False)

        #Verify whether user defined  a dem component.  If not, then download
        # SRTM DEM.
        self.verifyDEM()

        # run topo (mapping from radar to geo coordinates)
        self.runTopo()

        # run geo2rdr (mapping from geo to radar coordinates)
        self.runGeo2rdr()

        # resampling using only geometry offsets
        self.runResampleSlc('coarse')

        # refine geometry offsets using offsets computed by cross correlation
        self.runRefineSecondaryTiming()

        # resampling using refined offsets
        self.runResampleSlc('refined')

        # run dense offsets
        self.runDenseOffsets()
	
############ Modified by V. Brancato 10.07.2019
        # adding the azimuth offsets computed from cross correlation to geometry offsets 
        self.runRubbersheetAzimuth()
       
        # adding the range offsets computed from cross correlation to geometry offsets 
        self.runRubbersheetRange()
####################################################################################
        # resampling using rubbersheeted offsets
        # which include geometry + constant range + constant azimuth
        # + dense azimuth offsets
        self.runResampleSlc('fine')

        #run split range spectrum
        self.runSplitSpectrum()

        self.runResampleSubbandSlc(misreg=True)
        # forming the interferogram
        self.runInterferogram()

        self.runInterferogram(igramSpectrum = "sub")

        # Filtering and estimating coherence
        self.runFilter(self.filterStrength)

        self.runFilter(self.filterStrength, igramSpectrum = "low")

        self.runFilter(self.filterStrength, igramSpectrum = "high")

        # unwrapping
        self.runUnwrapper()

        self.runUnwrapper(igramSpectrum = "low")

        self.runUnwrapper(igramSpectrum = "high")

        self.runDispersive()

        self.runGeocode(self.geocode_list, self.geocode_bbox)

        self.runGeocode(self.geocode_list, self.geocode_bbox, True)


        self.timeEnd = time.time()
        logger.info("Total Time: %i seconds" %(self.timeEnd - self.timeStart))

        self.renderProcDoc()

        return None

class Insar(_RoiBase):
    """
    Insar Application:
    Implements InSAR processing flow for a pair of scenes from
    sensor raw data to geocoded, flattened interferograms.
    """

    family = "insar"

    def __init__(self, family='',name='',cmdline=None):
        #to allow inheritance with different family name use the locally
        #defined only if the subclass (if any) does not specify one

        super().__init__(
            family=family if family else  self.__class__.family, name=name,
            cmdline=cmdline)

    def Usage(self):
        print("Usages: ")
        print("stripmapApp.py <input-file.xml>")
        print("stripmapApp.py --steps")
        print("stripmapApp.py --help")
        print("stripmapApp.py --help --steps")


    ## extends _InsarBase_steps, but not in the same was as main
    def _steps(self):
        super()._steps()

        # Geocode
        #self.step('geocode', func=self.runGeocode,
        #        args=(self.geocode_list, self.unwrap, self.geocode_bbox))

        self.step('endup', func=self.endup)

        return None

    ## main() extends _InsarBase.main()
    def main(self):

        super().main()
        print("self.timeStart = {}".format(self.timeStart))

        # self.runCorrect()

        #self.runRgoffset()

        # Cull offoutliers
        #self.iterate_runOffoutliers()

        self.runResampleSlc()
        #self.runResamp_only()

        self.runRefineSecondaryTiming()

        #self.insar.topoIntImage=self.insar.resampOnlyImage
        #self.runTopo()
#        self.runCorrect()

        # Coherence ?
        #self.runCoherence(method=self.correlation_method)


        # Filter ?
        self.runFilter(self.filterStrength)

        # Unwrap ?
        self.runUnwrapper()

        # Geocode
        #self.runGeocode(self.geocode_list, self.unwrap, self.geocode_bbox)

        timeEnd = time.time()
        logger.info("Total Time: %i seconds" %(timeEnd - self.timeStart))

        self.renderProcDoc()

        return None


if __name__ == "__main__":
    #make an instance of Insar class named 'stripmapApp'
    insar = Insar(name="stripmapApp")
    #configure the insar application
    insar.configure()
    #invoke the base class run method, which returns status
    status = insar.run()
    #inform Python of the status of the run to return to the shell
    raise SystemExit(status)
