public class C2rccCommons {

    public static TimeCoding getTimeCoding(Product product) {
        if (product.getSceneTimeCoding() != null) {
            return product.getSceneTimeCoding();
        }
        final ProductData.UTC startTime = product.getStartTime();
        final ProductData.UTC endTime = product.getEndTime();
        if (endTime == null || startTime == null) {
            throw new OperatorException("Could not retrieve time information from source product");
        }
        return getTimeCoding(startTime, endTime);
    }

    public static TimeCoding getTimeCoding(ProductData.UTC startTime, ProductData.UTC endTime) {
        if (startTime == null || endTime == null) {
            throw new IllegalArgumentException("Arguments startTime and endTime must be given.");
        } else {
            double startTimeMJD = startTime.getMJD();
            double constantTime = (endTime.getMJD() - startTimeMJD) / 2.0 + startTimeMJD;
            return new ConstantTimeCoding(constantTime);
        }
    }

    public static void ensureTimeInformation(Product product, ProductData.UTC startTime, ProductData.UTC endTime, TimeCoding timeCoding) {
        if(product.getStartTime() == null) {
            product.setStartTime(startTime);
        }
        if(product.getEndTime() == null) {
            product.setEndTime(endTime);
        }
        if (product.getSceneTimeCoding() == null) {
            product.setSceneTimeCoding(timeCoding);
        }
    }

    public static boolean areSamplesValid(Sample[] sourceSamples, int x, int y) {
        boolean samplesValid = true;
        for (Sample sourceSample : sourceSamples) {
            // can be null because samples for ozone and atm_pressure might be missing
            RasterDataNode node = sourceSample.getNode();
            if (node != null) {
                if (!node.isPixelValid(x, y)) {
                    samplesValid = false;
                    break;
                }
            }
        }
        return samplesValid;
    }

    public static Band addBand(Product targetProduct, String name, String unit, String description) {
        Band targetBand = targetProduct.addBand(name, ProductData.TYPE_FLOAT32);
        targetBand.setUnit(unit);
        targetBand.setDescription(description);
        targetBand.setGeophysicalNoDataValue(Double.NaN);
        targetBand.setNoDataValueUsed(true);
        return targetBand;
    }

    public static Band addVirtualBand(Product targetProduct, String name, String expression, String unit, String description) {
        Band band = targetProduct.addBand(name, expression);
        band.setUnit(unit);
        band.setDescription(description);
        band.getSourceImage(); // trigger source image creation
        band.setGeophysicalNoDataValue(Double.NaN);
        band.setNoDataValueUsed(true);
        return band;
    }
} public interface C2rccConfigurable {

    void setAtmosphericAuxDataPath(String atmosphericAuxDataPath);

    void setTomsomiStartProduct(Product tomsomiStartProduct);

    void setTomsomiEndProduct(Product tomsomiEndProduct);

    void setNcepStartProduct(Product ncepStartProduct);

    void setNcepEndProduct(Product ncepEndProduct);

    void setTemperature(double temperature);

    void setSalinity(double salinity);

    void setOzone(double ozone);

    void setPress(double press);

    void setValidPixelExpression(String validPixelExpression);

    void setOutputRtosa(boolean outputRtosa);

    void setOutputAsRrs(boolean asRadianceRefl);
} public interface AtmosphericAuxdata {

    /**
     * Gets the daily interpolated ozone content value at the specified geo-location in Dobson units.
     *
     * @param mjd the date/time value as mjd to retrieve the data for
     * @param x   the pixel x-index, preferred over lat if applicable
     * @param y   the pixel x-index, preferred over lon if applicable
     * @param lat the latitude value
     * @param lon the longitude value   @return the ozone value or NaN if value could not be retrieved
     * @throws Exception in case of disk access failures
     */
    double getOzone(double mjd, int x, int y, double lat, double lon) throws Exception;

    /**
     * Gets the quarter daily interpolated surface pressure value at the specified geo-location in mBar.
     *
     * @param mjd the date/time value as mjd to retrieve the data for
     * @param x   the pixel x-index, preferred over lat if applicable
     * @param y   the pixel y-index, preferred over lon if applicable
     * @param lat the latitude value
     * @param lon the longitude value
     * @return the pressure value or NaN if value could not be retrieved
     * @throws Exception in case of disk access failures
     */
    double getSurfacePressure(double mjd, int x, int y, double lat, double lon) throws Exception;

    /**
     * Releases all resources.
     */
    void dispose();
} public class AtmosphericAuxdataBuilder {

    private RasterDataNode ozoneRaster;
    private RasterDataNode surfPressureRaster;
    private String atmosphericAuxDataPath;
    private Product tomsomiStartProduct;
    private Product tomsomiEndProduct;
    private Product ncepStartProduct;
    private Product ncepEndProduct;

    private double ozone = 330;
    private double surfacePressure = 1000;

    public void useAtmosphericAuxDataPath(String atmosphericAuxDataPath) {
        this.atmosphericAuxDataPath = atmosphericAuxDataPath;
    }

    public void useNcepProducts(Product ncepStartProduct, Product ncepEndProduct) {
        this.ncepStartProduct = ncepStartProduct;
        this.ncepEndProduct = ncepEndProduct;
    }

    public void setOzone(double ozone) {
        this.ozone = ozone;
    }

    public void setSurfacePressure(double press) {
        this.surfacePressure = press;
    }

    public void useTomsomiProducts(Product tomsomiStartProduct, Product tomsomiEndProduct) {
        this.tomsomiStartProduct = tomsomiStartProduct;
        this.tomsomiEndProduct = tomsomiEndProduct;

    }

    public void useAtmosphericRaster(RasterDataNode ozoneRaster, RasterDataNode surfPressureRaster) {
        this.ozoneRaster = ozoneRaster;
        this.surfPressureRaster = surfPressureRaster;
    }

    public AtmosphericAuxdata create() throws Exception {
        AtmosphericAuxdata auxdata;
        if (ozoneRaster != null && surfPressureRaster != null) {
            auxdata = new RasterAtmosphericAuxdata(ozoneRaster, surfPressureRaster);
        } else {
            if (StringUtils.isNullOrEmpty(atmosphericAuxDataPath)) {
                if (tomsomiStartProduct == null || tomsomiEndProduct == null || ncepStartProduct == null || ncepEndProduct == null) {
                    SystemUtils.LOG.info("Atmospheric auxdata product can't be used. At least one is not specified. " +
                                                 "Using constant values for ozone (" + ozone + ") and surface pressure (" + surfacePressure + ").");
                    auxdata = new ConstantAtmosphericAuxdata(ozone, surfacePressure);
                } else {
                    auxdata = new AtmosphericAuxdataStatic(tomsomiStartProduct, tomsomiEndProduct, "ozone", ozone,
                                                           ncepStartProduct, ncepEndProduct, "press", surfacePressure);
                }
            } else {
                final AncDownloader ancDownloader = new AncDownloader(AncillaryCommons.ANC_DATA_URI);
                final AncRepository ancRepository = new AncRepository(new File(atmosphericAuxDataPath), ancDownloader);
                AncDataFormat ozoneFormat = AncillaryCommons.createOzoneFormat(ozone);
                AncDataFormat pressureFormat = AncillaryCommons.createPressureFormat(surfacePressure);
                auxdata = new AtmosphericAuxdataDynamic(ancRepository, ozoneFormat, pressureFormat);
            }
        }
        return auxdata;
    }
} public class NNUtils {
    public static String[] getNNFilePaths(Path nnRootPath, String[] alternativeNetDirNames) throws IOException {
        final ArrayList<String> pathsList = new ArrayList<>();
        final String prefix = "The path '" + nnRootPath.toString() + "' ";

        if (!Files.isDirectory(nnRootPath)) {
            throw new OperatorException(prefix + "for alternative neuronal nets is not a valid path");
        }

        final HashSet<String> dirNames = new HashSet<>();
        Files.newDirectoryStream(nnRootPath).forEach(path -> {
            if (Files.isDirectory(path)) {
                dirNames.add(path.getFileName().toString());
            }
        });
        for (String alternativeNetDirName : alternativeNetDirNames) {
            if (!dirNames.contains(alternativeNetDirName)) {
                throw new OperatorException(prefix + "does not contain the expected sub directory '" + alternativeNetDirName + "'");
            }
            final int[] dotNetFilesCount = {0};
            final Path nnDirPath = nnRootPath.resolve(alternativeNetDirName);
            Files.newDirectoryStream(nnDirPath).forEach(path -> {
                if (path.getFileName().toString().toLowerCase().endsWith(".net")
                        && Files.isRegularFile(path)) {
                    dotNetFilesCount[0]++;
                    pathsList.add(path.toString());
                }
            });
            int count = dotNetFilesCount[0];
            if (count != 1) {
                throw new OperatorException("The path '" + nnDirPath + " must contain exact 1 file with file ending '*.net', but contains " + count);
            }
        }

        return pathsList.toArray(new String[pathsList.size()]);
    }
} public class RgbProfiles {
    private static final String[] RGB_BAND_NAMES = {
            "rhow", "rrs", "rhown", "rpath", "rtoa",
            "rtosa_gc", "rtosagc_aann", "tdown", "tup"
    };

    public static void installRgbProfiles(String productType, String redExpr, String greenExpr, String blueExpr) {
        RGBImageProfileManager profileManager = RGBImageProfileManager.getInstance();
        RGBImageProfile[] allProfiles = profileManager.getAllProfiles();
        for (String bandName : RGB_BAND_NAMES) {
            String profileName = productType + " " + bandName;
            Stream<RGBImageProfile> profileStream = Arrays.stream(allProfiles);
            boolean profileExists = profileStream.anyMatch(rgbImageProfile -> rgbImageProfile.getName().equals(profileName));
            if (!profileExists) {
                String[] rgbaExpressions = {
                        String.format(redExpr, bandName),
                        String.format(greenExpr, bandName),
                        String.format(blueExpr, bandName)};
                String[] pattern = {productType, "", ""};
                profileManager.addProfile(new RGBImageProfile(profileName, rgbaExpressions, pattern));
            }
        }
    }

    public static void installMerisRgbProfiles() {
        installRgbProfiles("C2RCC_MERIS",
                           "log(0.05 + 0.35 * %1$s_2 + 0.60 * %1$s_5 + %1$s_6 + 0.13 * %1$s_7)",
                           "log(0.05 + 0.21 * %1$s_3 + 0.50 * %1$s_4 + %1$s_5 + 0.38 * %1$s_6)",
                           "log(0.05 + 0.21 * %1$s_1 + 1.75 * %1$s_2 + 0.47 * %1$s_3 + 0.16 * %1$s_4)");
    }

    public static void installMeris4RgbProfiles() {
        installRgbProfiles("C2RCC_MERIS4",
                           "log(0.05 + 0.35 * %1$s_2 + 0.60 * %1$s_5 + %1$s_6 + 0.13 * %1$s_7)",
                           "log(0.05 + 0.21 * %1$s_3 + 0.50 * %1$s_4 + %1$s_5 + 0.38 * %1$s_6)",
                           "log(0.05 + 0.21 * %1$s_1 + 1.75 * %1$s_2 + 0.47 * %1$s_3 + 0.16 * %1$s_4)");
    }

    public static void installS2MsiRgbProfiles() {
        installRgbProfiles("C2RCC_S2-MSI",
                           "log(0.05 + 0.35 * %1$s_B2 + 0.60 * %1$s_B5 + %1$s_6 + 0.13 * %1$s_B7)",
                           "log(0.05 + 0.21 * %1$s_B3 + 0.50 * %1$s_B4 + %1$s_B5 + 0.38 * %1$s_B6)",
                           "log(0.05 + 0.21 * %1$s_B1 + 1.75 * %1$s_B2 + 0.47 * %1$s_B3 + 0.16 * %1$s_B4)");
    }

    public static void installLandsat8RgbProfiles() {
        installRgbProfiles("C2RCC_LANDSAT-8",
                           "%1$s_4",
                           "%1$s_3",
                           "%1$s_2"
        );
    }

    public static void installModisRgbProfiles() {
        installRgbProfiles("C2RCC_MODIS",
                           "%1$s_667)",
                           "%1$s_547)",
                           "%1$s_443)");
    }

    public static void installSeaWifsRgbProfiles() {
        installRgbProfiles("C2RCC_SEAWIFS",
                           "%1$s_670",
                           "%1$s_555",
                           "%1$s_443");
    }

    public static void installViirsRgbProfiles() {
        installRgbProfiles("C2RCC_VIIRS",
                           "%1$s_671",
                           "%1$s_551",
                           "%1$s_443");
    }

} public static Band addBand(Product targetProduct, String name, String unit, String description) {
    Band targetBand = targetProduct.addBand(name, ProductData.TYPE_FLOAT32);
    targetBand.setUnit(unit);
    targetBand.setDescription(description);
    targetBand.setGeophysicalNoDataValue(Double.NaN);
    targetBand.setNoDataValueUsed(true);
    return targetBand;
} public static Band addVirtualBand(Product targetProduct, String name, String expression, String unit, String description) {
    Band band = targetProduct.addBand(name, expression);
    band.setUnit(unit);
    band.setDescription(description);
    band.getSourceImage(); // trigger source image creation
    band.setGeophysicalNoDataValue(Double.NaN);
    band.setNoDataValueUsed(true);
    return band;
} public static double fetchOzone(final AtmosphericAuxdata atmosphericAuxdata, double timeMJD, int x, int y, double lat, double lon) {
    try {
        return atmosphericAuxdata.getOzone(timeMJD, x, y, lat, lon);
    } catch (Exception e) {
        throw new OperatorException("Unable to fetch ozone value from auxdata.", e);
    }
} public static double fetchSurfacePressure(AtmosphericAuxdata atmosphericAuxdata, double timeMJD, int x, int y, double lat, double lon) {
    try {
        return atmosphericAuxdata.getSurfacePressure(timeMJD, x, y, lat, lon);
    } catch (Exception e) {
        throw new OperatorException("Unable to fetch surface pressure value from auxdata.", e);
    }
} static float[] DEFAULT_WAVELENGTH = new float[]{440, 480, 560, 655, 865}; static final int FLAG_INDEX_ADET_AT_MAX = 6; static final int FLAG_INDEX_ADET_AT_MIN = 11; static final int FLAG_INDEX_AGELB_AT_MAX = 7; static final int FLAG_INDEX_AGELB_AT_MIN = 12; static final int FLAG_INDEX_APIG_AT_MAX = 5; static final int FLAG_INDEX_APIG_AT_MIN = 10; static final int FLAG_INDEX_BPART_AT_MAX = 8; static final int FLAG_INDEX_BPART_AT_MIN = 13; static final int FLAG_INDEX_BWIT_AT_MAX = 9; static final int FLAG_INDEX_BWIT_AT_MIN = 14; static final int FLAG_INDEX_CLOUD = 3; static final int FLAG_INDEX_IOP_OOR = 4; static final int FLAG_INDEX_KD489_AT_MAX = 18; static final int FLAG_INDEX_KD489_OOR = 16; static final int FLAG_INDEX_KDMIN_AT_MAX = 19; static final int FLAG_INDEX_KDMIN_OOR = 17; static final int FLAG_INDEX_RHOW_OOR = 2; static final int FLAG_INDEX_RHOW_OOS = 15; static final int FLAG_INDEX_RTOSA_OOR = 1; static final int FLAG_INDEX_RTOSA_OOS = 0; static final int FLAG_INDEX_VALID_PE = 31; static final int IDX_iop_rw = 3; static final int IDX_iop_unciop = 5; static final int IDX_iop_uncsumiop_unckd = 6; static final int IDX_rtosa_aann = 0; static final int IDX_rtosa_rpath = 9; static final int IDX_rtosa_rw = 1; static final int IDX_rtosa_trans = 8; static final int IDX_rw_iop = 2; static final int IDX_rw_kd = 4; static final int IDX_rw_rwnorm = 7; public static class Result {

    public final double[] r_toa;
    public final double[] r_tosa;
    public final double[] rtosa_aann;
    public final double rtosa_oos;
    public final double[] rpath_nn;
    public final double[] transd_nn;
    public final double[] transu_nn;
    public final double[] rwa;
    public final double rwa_oos;
    public final double[] rwn;
    public final double[] iops_nn;
    public final double kd489_nn;
    public final double kdmin_nn;
    public final double[] unc_iop_abs;
    public final double unc_abs_adg;
    public final double unc_abs_atot;
    public final double unc_abs_btot;
    public final double unc_abs_chl;
    public final double unc_abs_tsm;
    public final double unc_abs_kd489;
    public final double unc_abs_kdmin;
    public final int flags;

    public Result(double[] r_toa, double[] r_tosa, double[] rtosa_aann, double[] rpath_nn, double[] transd_nn, double[] transu_nn, double[] rwa,
                  double[] rwn, double rtosa_oos, double rwa_oos, double[] iops_nn,
                  double kd489_nn, double kdmin_nn, double[] unc_iop_abs,
                  double unc_abs_adg, double unc_abs_atot, double unc_abs_btot, double unc_abs_chl,
                  double unc_abs_tsm, double unc_abs_kd489, double unc_abs_kdmin, int flags) {

        this.r_toa = r_toa;
        this.r_tosa = r_tosa;
        this.rtosa_aann = rtosa_aann;
        this.rtosa_oos = rtosa_oos;
        this.rpath_nn = rpath_nn;
        this.transd_nn = transd_nn;
        this.transu_nn = transu_nn;
        this.rwa = rwa;
        this.rwa_oos = rwa_oos;
        this.rwn = rwn;
        this.iops_nn = iops_nn;
        this.kd489_nn = kd489_nn;
        this.kdmin_nn = kdmin_nn;
        this.unc_iop_abs = unc_iop_abs;
        this.unc_abs_adg = unc_abs_adg;
        this.unc_abs_atot = unc_abs_atot;
        this.unc_abs_btot = unc_abs_btot;
        this.unc_abs_chl = unc_abs_chl;
        this.unc_abs_tsm = unc_abs_tsm;
        this.unc_abs_kdmin = unc_abs_kdmin;
        this.unc_abs_kd489 = unc_abs_kd489;
        this.flags = flags;
    }
}
import com.bc.ceres.core.Assert;
import org.esa.s3tbx.c2rcc.C2rccCommons;
import org.esa.s3tbx.c2rcc.C2rccConfigurable;
import org.esa.s3tbx.c2rcc.ancillary.AtmosphericAuxdata;
import org.esa.s3tbx.c2rcc.ancillary.AtmosphericAuxdataBuilder;
import org.esa.s3tbx.c2rcc.util.NNUtils;
import org.esa.s3tbx.c2rcc.util.RgbProfiles;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.FlagCoding;
import org.esa.snap.core.datamodel.GeoPos;
import org.esa.snap.core.datamodel.MetadataAttribute;
import org.esa.snap.core.datamodel.MetadataElement;
import org.esa.snap.core.datamodel.PixelPos;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.core.datamodel.ProductNode;
import org.esa.snap.core.datamodel.ProductNodeEvent;
import org.esa.snap.core.datamodel.ProductNodeListener;
import org.esa.snap.core.datamodel.ProductNodeListenerAdapter;
import org.esa.snap.core.datamodel.RasterDataNode;
import org.esa.snap.core.datamodel.TimeCoding;
import org.esa.snap.core.dataop.dem.ElevationModel;
import org.esa.snap.core.dataop.dem.ElevationModelDescriptor;
import org.esa.snap.core.dataop.dem.ElevationModelRegistry;
import org.esa.snap.core.dataop.resamp.Resampling;
import org.esa.snap.core.gpf.GPF;
import org.esa.snap.core.gpf.OperatorException;
import org.esa.snap.core.gpf.OperatorSpi;
import org.esa.snap.core.gpf.annotations.OperatorMetadata;
import org.esa.snap.core.gpf.annotations.Parameter;
import org.esa.snap.core.gpf.annotations.SourceProduct;
import org.esa.snap.core.gpf.pointop.PixelOperator;
import org.esa.snap.core.gpf.pointop.ProductConfigurer;
import org.esa.snap.core.gpf.pointop.Sample;
import org.esa.snap.core.gpf.pointop.SourceSampleConfigurer;
import org.esa.snap.core.gpf.pointop.TargetSampleConfigurer;
import org.esa.snap.core.gpf.pointop.WritableSample;
import org.esa.snap.core.util.ProductUtils;
import org.esa.snap.core.util.StringUtils;
import org.esa.snap.core.util.converters.BooleanExpressionConverter;
import java.awt.*;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import static org.esa.s3tbx.c2rcc.C2rccCommons.addBand;
import static org.esa.s3tbx.c2rcc.C2rccCommons.addVirtualBand;
import static org.esa.s3tbx.c2rcc.ancillary.AncillaryCommons.fetchOzone;
import static org.esa.s3tbx.c2rcc.ancillary.AncillaryCommons.fetchSurfacePressure;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.DEFAULT_WAVELENGTH;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_ADET_AT_MAX;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_ADET_AT_MIN;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_AGELB_AT_MAX;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_AGELB_AT_MIN;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_APIG_AT_MAX;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_APIG_AT_MIN;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_BPART_AT_MAX;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_BPART_AT_MIN;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_BWIT_AT_MAX;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_BWIT_AT_MIN;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_CLOUD;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_IOP_OOR;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_KD489_AT_MAX;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_KD489_OOR;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_KDMIN_AT_MAX;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_KDMIN_OOR;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_RHOW_OOR;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_RHOW_OOS;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_RTOSA_OOR;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_RTOSA_OOS;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.FLAG_INDEX_VALID_PE;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_iop_rw;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_iop_unciop;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_iop_uncsumiop_unckd;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_rtosa_aann;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_rtosa_rpath;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_rtosa_rw;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_rtosa_trans;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_rw_iop;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_rw_kd;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.IDX_rw_rwnorm;
import static org.esa.s3tbx.c2rcc.landsat8.C2rccLandsat8Algorithm.Result;
package org.esa.s3tbx.c2rcc.landsat8;




// todo (nf) - Add Thullier solar fluxes as default values to C2RCC operator (https://github.com/bcdev/s3tbx-c2rcc/issues/1)
// todo (nf) - Add flags band and check for OOR of inputs and outputs of the NNs (https://github.com/bcdev/s3tbx-c2rcc/issues/2)
// todo (nf) - Add min/max values of NN inputs and outputs to metadata (https://github.com/bcdev/s3tbx-c2rcc/issues/3)
// todo (RD) - salinity and temperautre have to be passed to C2R ?
// todo (RD) - parameters, to control which variables to be processed, pass to C2R

/**
 * The Case 2 Regional / CoastColour Operator for MERIS.
 * <p/>
 * Computes AC-reflectances and IOPs from MERIS L1b data products using
 * an neural-network approach.
 */
@OperatorMetadata(alias = "c2rcc.landsat8", version = "0.18",
        authors = "Roland Doerffer, Marco Peters (Brockmann Consult)",
        category = "Optical/Thematic Water Processing",
        copyright = "Copyright (C) 2016 by Brockmann Consult",
        description = "Performs atmospheric correction and IOP retrieval with uncertainties on MERIS L1b data products.")
public class C2rccLandsat8Operator extends PixelOperator implements C2rccConfigurable {
    /*
        c2rcc ops have been removed from Graph Builder. In the layer xml they are disabled
        see https://senbox.atlassian.net/browse/SNAP-395
    */

    // Landsat sources
    static final String[] EXPECTED_BANDNAMES = new String[]{"coastal_aerosol", "blue", "green", "red", "near_infrared"};
    static final int L8_BAND_COUNT = EXPECTED_BANDNAMES.length;

    private static final int VALID_PIXEL_IX = L8_BAND_COUNT + 1;

    private static final int RTOA_IX = 0;
    private static final int RTOSA_IX = L8_BAND_COUNT;
    private static final int RTOSA_AANN_IX = 2 * L8_BAND_COUNT;
    private static final int RPATH_IX = 3 * L8_BAND_COUNT;
    private static final int TDOWN_IX = 4 * L8_BAND_COUNT;
    private static final int TUP_IX = 5 * L8_BAND_COUNT;
    private static final int AC_REFLEC_IX = 6 * L8_BAND_COUNT;
    private static final int RHOWN_IX = 7 * L8_BAND_COUNT;

    private static final int OOS_RTOSA_IX = 8 * L8_BAND_COUNT;
    private static final int OOS_AC_REFLEC_IX = OOS_RTOSA_IX + 1;

    private static final int IOP_APIG_IX = OOS_RTOSA_IX + 2;
    private static final int IOP_ADET_IX = OOS_RTOSA_IX + 3;
    private static final int IOP_AGELB_IX = OOS_RTOSA_IX + 4;
    private static final int IOP_BPART_IX = OOS_RTOSA_IX + 5;
    private static final int IOP_BWIT_IX = OOS_RTOSA_IX + 6;

    private static final int KD489_IX = OOS_RTOSA_IX + 7;
    private static final int KDMIN_IX = OOS_RTOSA_IX + 8;

    private static final int UNC_APIG_IX = OOS_RTOSA_IX + 9;
    private static final int UNC_ADET_IX = OOS_RTOSA_IX + 10;
    private static final int UNC_AGELB_IX = OOS_RTOSA_IX + 11;
    private static final int UNC_BPART_IX = OOS_RTOSA_IX + 12;
    private static final int UNC_BWIT_IX = OOS_RTOSA_IX + 13;
    private static final int UNC_ADG_IX = OOS_RTOSA_IX + 14;
    private static final int UNC_ATOT_IX = OOS_RTOSA_IX + 15;
    private static final int UNC_BTOT_IX = OOS_RTOSA_IX + 16;
    private static final int UNC_KD489_IX = OOS_RTOSA_IX + 17;
    private static final int UNC_KDMIN_IX = OOS_RTOSA_IX + 18;

    private static final int C2RCC_FLAGS_IX = OOS_RTOSA_IX + 19;

    private static final String PRODUCT_TYPE = "C2RCC_LANDSAT-8";

    private static final String[] alternativeNetDirNames = new String[]{
            "rtosa_aann",
            "rtosa_rw",
            "rw_iop",
            "iop_rw",
            "rw_kd",
            "iop_unciop",
            "iop_uncsumiop_unckd",
            "rw_rwnorm",
            "rtosa_trans",
            "rtosa_rpath"
    };

    private static final String STANDARD_NETS = "C2RCC-Nets";
    private static final String EXTREME_NETS = "C2X-Nets";
    private static final Map<String, String[]> c2rccNetSetMap = new HashMap<>();

    static {
        String[] standardNets = new String[10];
        standardNets[IDX_iop_rw] = "landsat8/landsat8_netze_20160818/iop_rw/17x97x47_79.9.net";
        standardNets[IDX_iop_unciop] = "landsat8/landsat8_netze_20160818/iop_unciop/17x77x37_11486.7.net";