/* ----------------------------------------
 * Origin  : GFI INFORMATIQUE 
 * Project : CDPP Propagation Tool 
 * ----------------------------------------
 * History 
 * Version : 01/05/2015 : SCA : V2.1
 * Version : 15/09/2016 : SCA : V2.2 (PSWS-H2020)
 *  Create a default J-map image
 * Version : 06/11/2017 : SCA : V2.2.2 
 *  Redmine #5403 - DMPT3 
 * Version : 12/09/2023 : LBE : V2.8
 * DM-ID : Redmine#8799 : 12/09/2023 : new carrington maps
 * ----------------------------------------
 */
package fr.tlse.irap.server.serviceDAO.utils;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;

import nom.tam.fits.BasicHDU;
import nom.tam.fits.Fits;
import nom.tam.fits.FitsException;

import org.apache.log4j.Logger;

import fr.tlse.irap.map.CarringtonMap;
import fr.tlse.irap.map.CarringtonMapHeader;
import fr.tlse.irap.map.CarringtonMapType;
import fr.tlse.irap.map.JMap;
import fr.tlse.irap.map.JMapHeader;

/**
 * Utility Class for Fits File Manipulation. Two public static methods are available: loadImage(Date d, File file) and
 * calculateFileName(Date d, File dirname)
 *
 * @author frup03304
 */
public class FitsHelper {

    private static final Logger logger = Logger.getLogger(FitsHelper.class);
    private static final Integer NB_DAY = new Integer(10);
    /* Fits keywords */
    private static final String AXIS_X_KEYWORD = "NAXIS1";
    private static final String AXIS_Y_KEYWORD = "NAXIS2";
    /* J-Map fits keywords */
    private static final String START_TIME_KEYWORD = "STARTTIM";
    private static final String END_TIME_KEYWORD = "ENDTIM";
    private static final String MAX_ELONGATION_KEYWORD = "MAXEL";
    private static final String MIN_ELONGATION_KEYWORD = "MINEL";
    private static final String PA_KEYWORD = "PA";
    /* Carrington-Map fits keywords */
    private static final String CARROT_KEYWORD = "CARROT";

    /**
     * Retrieve START TIME card from current Fits header
     */
    private static Date getStartDate(Fits fits) {
        Date startDate = null;
        try {
            String dateInString = fits.getHDU(0).getHeader().getStringValue(START_TIME_KEYWORD);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
            startDate = simpleDateFormat.parse(dateInString);

        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return startDate;
    }

    /**
     * Retrieve END TIME card from current Fits header
     */
    private static Date getEndDate(Fits fits) {
        Date endDate = new Date();
        try {
            String dateInString = fits.getHDU(0).getHeader().getStringValue(END_TIME_KEYWORD);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
            endDate = simpleDateFormat.parse(dateInString);

        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return endDate;
    }

    /**
     * Retrieve MIN ELONGATION card from current Fits header
     */
    private static Double getMinElongation(Fits fits) {
        Double minel = 0d;
        try {
            minel = fits.getHDU(0).getHeader().getDoubleValue(MIN_ELONGATION_KEYWORD);
        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return minel;
    }

    /**
     * Retrieve MAX ELONGATION card from current Fits header
     */
    private static Double getMaxElongation(Fits fits) {
        Double maxel = 0d;
        try {
            maxel = fits.getHDU(0).getHeader().getDoubleValue(MAX_ELONGATION_KEYWORD);
        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return maxel;
    }

    /**
     * Retrieve NAXIS 1 card from current Fits header
     */
    private static int getWidth(Fits fits) {
        int width = 0;
        try {
            width = fits.getHDU(0).getHeader().getIntValue(AXIS_X_KEYWORD);
        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return width;
    }

    /**
     * Retrieve NAXIS 2 card from current Fits header
     */
    private static int getHeight(Fits fits) {
        int height = 0;
        try {
            height = fits.getHDU(0).getHeader().getIntValue(AXIS_Y_KEYWORD);
        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return height;
    }

    /**
     * Retrieve PA card from current Fits header
     */
    private static Double getPA(Fits fits) {
        Double pa = null;
        try {
            pa = fits.getHDU(0).getHeader().getDoubleValue(PA_KEYWORD);
        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return pa;
    }

    /**
     * Retrieve CARROT card from current Fits header
     */
    private static int getCRN(Fits fits) {
        int crn = 0;
        try {
            crn = fits.getHDU(0).getHeader().getIntValue(CARROT_KEYWORD);
        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return crn;
    }

    /**
     * LoadImage Method Public Method to create a JMap instance with all data inside: WIDTH AXIS 1 HEIGHT AXIS 2 MAX
     * ELONGATION MAXEL MIN ELONGATION MINEL PA STARTTIM START TIME ENDTIM END TIME DataCompressed a byte Array of
     * pixels At the end of the method the currentFits is suppressed.
     */
    public static JMap loadImage(Date d1, Date d2, File file, String type) {
        logger.info("Start loading image from FITS File");
        logger.debug("Create a JMap Object");
        JMap jmap = new JMap();
        try {
            Fits fits = new Fits(file);
            float contrast = 0.1f;

            int i = fits.getNumberOfHDUs();
            logger.debug("Number of HDU " + i + " of " + file.getName());
            if (i == 0) {
                BasicHDU img = fits.getHDU(0);

                logger.debug("Set JMap Attributs");
                JMapHeader jmapHeader = new JMapHeader();
                jmapHeader.setHeight(getHeight(fits));
                jmapHeader.setWidth(getWidth(fits));
                jmapHeader.setPositionAngle(getPA(fits));
                jmapHeader.setMinElongation(getMinElongation(fits));
                jmapHeader.setMaxElongation(getMaxElongation(fits));
                jmapHeader.setStartDate(getStartDate(fits));
                jmapHeader.setEndDate(getEndDate(fits));
                jmap.setHeader(jmapHeader);
                int[] range = computePixelRange(d1, d2, jmapHeader);
                if (type.equals("ENLIL")) {
                    contrast = 0.1f;
                    jmap.dataCompressed = convert(jmapHeader.getHeight(), range, (float[][]) img.getKernel(), contrast);
                } else {
                    // code without image contrast increased
                    jmap.dataCompressed = convert(jmapHeader.getHeight(), range, (byte[][]) img.getKernel());
                    // code with image contrast increased
                    //contrast = 0.5f;
                    //jmap.dataCompressed = convert(jmapHeader.getHeight(), range, (byte[][]) img.getKernel(), contrast);
                }
            }

        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

        logger.info("End loading image from FITS File");
        return jmap;
    }

    /**
     * Get default JMap image
     */
    public static JMap getDefaultImage(Date d1, Date d2, String type) {
        logger.debug("Create a default JMap Object");
        JMap jmap = new JMap();
        JMapHeader jmapHeader = new JMapHeader();
        jmapHeader.setHeight(100);
        jmapHeader.setWidth(100);
        jmapHeader.setPositionAngle(0d);
        jmapHeader.setMinElongation(0d);
        jmapHeader.setMaxElongation(90d);
        jmapHeader.setStartDate(d1);
        jmapHeader.setEndDate(d2);
        jmap.setHeader(jmapHeader);
        int[] range = computePixelRange(d1, d2, jmapHeader);
        jmap.dataCompressed = createDefault(jmapHeader.getHeight(), range, (byte) 240);

        return jmap;
    }

    /**
     * LoadImage Public Method to create a CarringonMap instance with all data inside: WIDTH AXIS 1 HEIGHT AXIS 2
     * CARRINGTON ROTATION NUMBER, DataCompressed a byte Array of pixels At the end of the method the currentFits is
     * suppressed.
     */
    public static CarringtonMap loadImage(File file) {
        logger.info("Start loading image from FITS File");
        logger.debug("Create a CarringonMap Object");
        CarringtonMap carringtonMap = new CarringtonMap();
        try {
            Fits fits = new Fits(file);

            int i = fits.getNumberOfHDUs();
            logger.debug("Number of HDU " + i + " of " + file.getName());
            if (i == 0) {
                BasicHDU img = fits.getHDU(0);

                logger.debug("Set CarringtonMap Attributs");
                CarringtonMapHeader carringtonMapHeader = new CarringtonMapHeader();
                int h = getHeight(fits);
                int w = getWidth(fits);
                carringtonMapHeader.setHeight(h);
                carringtonMapHeader.setWidth(w);
                carringtonMapHeader.setRotationNumber(getCRN(fits));
                carringtonMap.setHeader(carringtonMapHeader);
                int fComp = computeCompressFactor(h * w);
                try{
                    carringtonMap.fDataCompressed = convert(h, w, (double[][]) img.getKernel(), fComp);
                }catch(Exception ed) {
                    try {
                        logger.debug("Failed reading double, trying float");
                        carringtonMap.fDataCompressed = convert(h, w, (float[][]) img.getKernel(), fComp);
                    } catch (Exception ef) {
                        logger.debug("Failed reading float, trying short");
                        carringtonMap.fDataCompressed = convert(h, w, (short[][]) img.getKernel(), fComp);
                    }
                }
            }

        } catch (FitsException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

        logger.info("End loading image from FITS File");
        return carringtonMap;
    }

    /**
     * Compute compression factor of the generated map image from the size of original fits
     *
     * @param original map size
     * @return
     */
    private static int computeCompressFactor(int size) {
        int compressFactor;
        if (size > 4000000) {
            compressFactor = 8;
        } else if (size > 3000000) {
            compressFactor = 7;
        } else if (size > 2000000) {
            compressFactor = 6;
        } else if (size > 1500000) {
            compressFactor = 5;
        } else if (size > 1000000) {
            compressFactor = 4;
        } else if (size > 750000) {
            compressFactor = 3;
        } else if (size > 500000) {
            compressFactor = 2;
        } else {
            compressFactor = 1; // default value
        }
        logger.info("Size = " + size + ", compression factor = " + compressFactor);
        return compressFactor;
    }

    /**
     * Define a range of indexes corresponding to the indexes of the byte array (pixels) of JMap int[0] = d1 int[1] = d2
     */
    private static int[] computePixelRange(Date d1, Date d2, JMapHeader jmapHeader) {

        long tStart = jmapHeader.getStartDate().getTime();
        long tEnd = jmapHeader.getEndDate().getTime();
        long t1Target = d1.getTime();
        long t2Target = d2.getTime();
        long period = (tEnd - tStart) / jmapHeader.getWidth();

        int indexStart = (int) ((t1Target - tStart) / period);
        int indexEnd = (int) ((t2Target - tStart) / period);
        logger.debug("Pixel Range between  : " + indexStart + "_" + indexEnd);
        int[] range = {indexStart, indexEnd};

        jmapHeader.setWidth(indexEnd - indexStart);
        jmapHeader.setStartDate(d1);
        jmapHeader.setEndDate(d2);

        return range;
    }

    private static byte[] createDefault(int height, int[] range, byte data) {
        byte[] pixelValues = new byte[(range[1] - range[0]) * height];
        for (int i = 0; i < pixelValues.length; i++) {
            pixelValues[i] = data;
        }
        return pixelValues;
    }

    /**
     * Convert a byte[][] in byte[] Only data between range[0] and range[1] are returned
     */
    private static byte[] convert(int height, int[] range, byte[][] data) {
        byte[] pixelValues = new byte[(range[1] - range[0]) * height];
        int count = 0;
        for (int row = data.length - 1; row > -1; row--) {
            for (int col = range[0]; col < range[1]; col++) {
                pixelValues[count++] = data[row][col];
            }
        }
        return pixelValues;
    }

    /**
     * Convert a float[][] in float[].
     *
     * @param fComp : aliasing of the original fits => compression factor from 1 to 8 applied to the data
     */
    private static float[] convert(int height, int width, float[][] data, int fComp) {
        int[] range = {0, width};
        if (fComp > 1) {
            for (int row = 0; row < height; row += fComp) {
                for (int col = 0; col < width; col += fComp) {
                    for (int i = 0; i < fComp && row + i < height; i++) {
                        for (int j = 0; j < fComp && col + j < width; j++) {
                            data[row + i][col + j] = data[row][col];
                        }
                    }
                }
            }
        }
        return convert(height, range, data);
    }

    /**
     * Convert a short[][] in float[].
     *
     * @param fComp : aliasing of the original fits => compression factor from 1 to 8 applied to the data
     */
    private static float[] convert(int height, int width, short[][] data, int fComp) {
        int[] range = {0, width};
        if (fComp > 1) {
            for (int row = 0; row < height; row += fComp) {
                for (int col = 0; col < width; col += fComp) {
                    for (int i = 0; i < fComp && row + i < height; i++) {
                        for (int j = 0; j < fComp && col + j < width; j++) {
                            data[row + i][col + j] = data[row][col];
                        }
                    }
                }
            }
        }
        return convert(height, range, data);
    }

    /**
     * Convert a double[][] in float[].
     *
     * @param fComp : aliasing of the original fits => compression factor from 1 to 8 applied to the data
     */
    private static float[] convert(int height, int width, double[][] data, int fComp) {
        int[] range = {0, width};
        if (fComp > 1) {
            for (int row = 0; row < height; row += fComp) {
                for (int col = 0; col < width; col += fComp) {
                    for (int i = 0; i < fComp && row + i < height; i++) {
                        for (int j = 0; j < fComp && col + j < width; j++) {
                            data[row + i][col + j] = data[row][col];
                        }
                    }
                }
            }
        }
        return convert(height, range, data);
    }

    /**
     * Convert a float[][] in byte[].
     *
     * @param height
     * @param range of width
     * @param contrast : rate limit of bound values[0, 1] => modify the contrast
     * @param data : Float data are translated and scaled to [0, 254] before casted to byte
     * @return Complement à 2 : [0, 127] => [0, 127], [128, 254] => [-127, -1]
     */
    private static byte[] convert(int height, int[] range, float[][] data, float contrast) {
        byte[] pixelValues = new byte[(range[1] - range[0]) * height];
        float max = 0;
        float min = 0;
        for (int row = data.length - 1; row > -1; row--) {
            for (int col = range[0]; col < range[1]; col++) {
                if (data[row][col] < min) {
                    min = data[row][col];
                }
                if (data[row][col] > max) {
                    max = data[row][col];
                }
            }
        }
        // limit min/max to x% of real bound values
        max = max * contrast;
        min = min * contrast;

        int count = 0;
        for (int row = data.length - 1; row > -1; row--) {
            for (int col = range[0]; col < range[1]; col++) {
                // compute byte value
                pixelValues[count++] = convertToByte(data[row][col], min, max);
            }
        }
        return pixelValues;
    }

    /**
     * Convert a float[][] in float[].
     */
    private static float[] convert(int height, int[] range, float[][] data) {
        float[] pixelValues = new float[(range[1] - range[0]) * height];
        int count = 0;
        for (int row = data.length - 1; row > -1; row--) {
            for (int col = range[0]; col < range[1]; col++) {
                pixelValues[count++] = data[row][col];
            }
        }
        return pixelValues;
    }

    private static float[] convert(int height, int[] range, short[][] data) {
        float[] pixelValues = new float[(range[1] - range[0]) * height];
        int count = 0;
        for (int row = data.length - 1; row > -1; row--) {
            for (int col = range[0]; col < range[1]; col++) {
                pixelValues[count++] = data[row][col];
            }
        }
        return pixelValues;
    }

    private static float[] convert(int height, int[] range, double[][] data) {
        float[] pixelValues = new float[(range[1] - range[0]) * height];
        int count = 0;
        for (int row = data.length - 1; row > -1; row--) {
            for (int col = range[0]; col < range[1]; col++) {
                pixelValues[count++] = (float) data[row][col];
            }
        }
        return pixelValues;
    }

    /**
     * Convert float data between min and max values to a byte
     */
    private static byte convertToByte(float fData, float min, float max) {
        // compute data between [0, 255[
        Float f = ((fData - min) / (max - min)) * 254;
        f = f > 254 ? 254 : f < 0 ? 0 : f;
        // convert to byte
        return f.byteValue();
    }

    /**
     * calculateFileName public method to return the name of the existing file which contains corresponding data. To
     * match it the filename have to be composed by two dates (START Date / END Date). FILENAME START Date <
     * TargetedDate - 10 days && TargetedDate + 10 days < FILENAME END Date return null if not found
     */
    public static String calculateFileName(Date d, File dirname) {
        return filterExistingFileName(d, dirname);
    }

    /**
     * calculateFileName public method to return the name of the existing file which contains corresponding data. To
     * match it the filename have to be composed by two dates (START Date / END Date). FILENAME START Date < T1Date &&
     * T2Date < FILENAME END Date return null if not found
     */
    public static String calculateFileName(Date d1, Date d2, File dirname) {
        return filterExistingFileName(d1, d2, dirname);
    }

    /**
     * findCarringtonFile public method to return the existing Carrington fits file which corresponding to the date and
     * to the radial distance. To match it the filename have to be composed by this date, FILENAME with corresponding
     * CRN is returned, null if not found
     */
    public static File findCarringtonFile(CarringtonMapType pCmType, Date pDate, File pDirname) {
        File[] files = null;

        //check for files with date instead of carrington rotation number
        if (pCmType.getProbe().equals("SDO") && pCmType.getInstrument().equals("EUV")) {
            SimpleDateFormat lSdf = new SimpleDateFormat("yyyyMMdd'T'");
            lSdf.setTimeZone(TimeZone.getTimeZone("UTC"));
            String lStrDate = lSdf.format(pDate);
            GregorianCalendar lGc = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
            lGc.setTime(pDate);
            int lHour = lGc.get(GregorianCalendar.HOUR_OF_DAY);
            if(lHour<6) lStrDate+="000000";
            else if(lHour<12) lStrDate+="060000";
            else if(lHour<18) lStrDate+="120000";
            else lStrDate+="180000";
            String model = "aia";
            String m0 = pCmType.getMeasurement().substring(0, 3);

            String fName = model + m0 + "_" + lStrDate;
            logger.debug("Searching files like " + fName + "*.fits");
            files = ScanDirectory.fetchMatchingFiles(pDirname, fName, ".fits");
        } else {

            Integer CRN = (int) getCarringtonRotationNumber(pDate);
            if (CRN > 0) {
                String crn = CRN.toString();
                if (pCmType.getProbe().equals("MODEL")) {
                    String model = pCmType.getInstrument().replace("-", "");
                    String m0 = pCmType.getMeasurement().substring(0, 1);
                    String measure = (m0.equals("S") ? "V" : (m0.equals("D") ? "N" : m0));
                    String rd0 = pCmType.getRadialDistances().get(0);

                    String fName = model + "_" + measure + "_" + rd0 + "Rs_CR" + crn + "_";
                    logger.debug("Searching files like " + fName + "*.fits");
                    files = ScanDirectory.fetchMatchingFiles(pDirname, fName, ".fits");
                } else {
                    // searching STEREO type files
                    logger.debug("Searching files like " + "_" + crn + ".fts");
                    files = ScanDirectory.fetchMatchingFiles(pDirname, "_" + crn + ".", ".fts");
                    if (files.length == 0) {
                        // searching SOHO type files
                        logger.debug("Searching files like " + "_" + crn + "_" + "*.fts");
                        files = ScanDirectory.fetchMatchingFiles(pDirname, "_" + crn + "_", ".fts");
                        if (files.length == 0) {
                            // searching GONG MAG type files
                            logger.debug("Searching files like " + "_" + crn + ".fits");
                            files = ScanDirectory.fetchMatchingFiles(pDirname, "_" + crn + ".", ".fits");
                            if (files.length == 0) {
                                // searching SDO MAG type files
                                logger.debug("Searching files like " + "." + crn + ".fits");
                                files = ScanDirectory.fetchMatchingFiles(pDirname, "." + crn + ".", ".fits");
                            }
                        }
                    }
                }
            }
        }
        if (files != null && files.length > 0) {
            return files[0];
        } else {
            return null;
        }
    }

    /**
     * Retrieve the first filename which is between the right period
     */
    private static String filterExistingFileName(Date d, File dirname) {
        List<String> potentialFiles = findFilesAroundthisDate(d, dirname);

        for (String currentFileName : potentialFiles) {
            if (isBetweenRightPeriod(d, currentFileName)) {
                return currentFileName;
            }
        }
        return null;
    }

    /**
     * Retrieve the first filename which is between the right period
     */
    private static String filterExistingFileName(Date d1, Date d2, File dirname) {
        List<String> potentialFiles = findFilesAroundthisDate(d1, dirname);

        for (String currentFileName : potentialFiles) {
            if (isBetweenRightPeriod(d1, d2, currentFileName)) {
                return currentFileName;
            }
        }
        return null;
    }

    /**
     * Retrieve All potential files names. To do this date are between d + d.MONTH-1 and d + d.MONTH+1
     */
    private static List<String> findFilesAroundthisDate(Date targetedDate, File dirname) {
        List<String> potentialFiles = new ArrayList<String>();
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(targetedDate);

        for (File file : findFilesForthisDate(gc, dirname)) {
            potentialFiles.add(file.getName());
        }

        gc.add(Calendar.MONTH, -1);
        for (File file : findFilesForthisDate(gc, dirname)) {
            potentialFiles.add(file.getName());
        }

        gc.add(Calendar.MONTH, +2);
        for (File file : findFilesForthisDate(gc, dirname)) {
            potentialFiles.add(file.getName());
        }

        return potentialFiles;

    }

    /**
     * Retrieve the existing file corresponding to the file name
     */
    private static File[] findFilesForthisDate(Calendar cal, File dirname) {
        String currentMonth;
        if (cal.get(Calendar.MONTH) > 8) {
            currentMonth = "" + (cal.get(Calendar.MONTH) + 1);
        } else {
            currentMonth = "0" + (cal.get(Calendar.MONTH) + 1);
        }
        logger.debug("Search files with pattern YYYY_MM : " + cal.get(Calendar.YEAR) + "_" + currentMonth);
        return ScanDirectory.fetchMatchingFiles(dirname, cal.get(Calendar.YEAR) + "_" + currentMonth, ".fits");
    }

    /**
     * Test if the filename is in right period FILENAME START Date < TargetedDate - 10 days && TargetedDate + 10 days <
     * FILENAME END Date
     */
    private static boolean isBetweenRightPeriod(Date targetedDate, String fileName) {
        Date startDate = null;
        Date endDate = null;

        List<String> dateCollection;
        if ((dateCollection = decomposeFileName(fileName)) != null && dateCollection.size() == 2) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
            try {
                startDate = simpleDateFormat.parse(dateCollection.get(0));
                endDate = simpleDateFormat.parse(dateCollection.get(1));
            } catch (ParseException e) {
                e.printStackTrace();
            }

            GregorianCalendar gc = new GregorianCalendar();
            gc.setTime(targetedDate);

            gc.add(Calendar.DAY_OF_YEAR, -NB_DAY.intValue());
            Date maxStartDate = gc.getTime();

            gc.add(Calendar.DAY_OF_YEAR, +2 * NB_DAY.intValue());
            Date maxEndDate = gc.getTime();

            logger.debug("Test if file contains the right period between : " + maxEndDate + " and " + maxStartDate);
            if (startDate.before(maxStartDate) && endDate.after(maxEndDate)) {
                logger.debug("True for file " + fileName);
                return true;
            }
        }
        return false;
    }

    /**
     * Test if the filename is in right period FILENAME START Date < d1 && d2 < FILENAME END Date
     */
    private static boolean isBetweenRightPeriod(Date d1, Date d2, String fileName) {
        Date startDate = null;
        Date endDate = null;

        List<String> dateCollection;
        if ((dateCollection = decomposeFileName(fileName)) != null && dateCollection.size() == 2) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
            try {
                startDate = simpleDateFormat.parse(dateCollection.get(0));
                endDate = simpleDateFormat.parse(dateCollection.get(1));
            } catch (ParseException e) {
                e.printStackTrace();
            }

            logger.debug("Test if " + fileName + " contains the right period between : " + d1 + " and " + d2);
            if (startDate.compareTo(d1) <= 0 && endDate.compareTo(d2) >= 0) {
                logger.debug("True for file " + fileName);
                return true;
            }
        }
        return false;
    }

    /**
     * Decompose the file name with two string representing start date end date
     */
    private static List<String> decomposeFileName(String fileName) {
        List<String> dateCollection = new ArrayList<String>();
        dateCollection.add(fileName.substring(0, 19));
        dateCollection.add(fileName.substring(20, fileName.lastIndexOf('.')));
        return dateCollection;
    }

    /**
     * Compute Carrington Rotation Number (CRN) from a given date
     */
    public static float getCarringtonRotationNumber(Date time) {
        // First day on November 9, 1853.
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

        try {
            final Date FIRST_CARRINGTON_DAY = sdf.parse("1853-11-09T12:00:00.000");
            final float CARRINGTON_STEP = 27.2753f;
            final int FIRST_CARRINGTON_NUMBER = 1;

            float count = ((time.getTime() - FIRST_CARRINGTON_DAY.getTime()) / 1000)
                    / (86400 * CARRINGTON_STEP);

            return (FIRST_CARRINGTON_NUMBER + count);
        } catch (ParseException e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * get Carrington values unit according to measurement
     */
    public static String getCarringtonUnit(String measurement) {
        String unit = "";
        if (measurement.startsWith("S")) {
            unit = "km/s";
        } else if (measurement.startsWith("B")) {
            unit = "G";
        } else if (measurement.startsWith("T")) {
            unit = "MK";
        } else if (measurement.startsWith("N")) {
            unit = "cm\u207B\u00B3";

        }
        return unit;
    }

    /**
     * get Carrington number from file name
     */
    public static int getCarringtonNumber(String fName) {
        String name = fName.substring(0, fName.lastIndexOf("."));
        String s = name.split("_")[1];
        Integer crn = 0;
        try {
            crn = Integer.parseInt(s.substring(s.length() - 4));
        } catch (Exception e) {
            s = name.split("_")[2];
            try {
                crn = Integer.parseInt(s.substring(s.length() - 4));
            } catch (Exception e2) {
                s = name.split("_")[3];
                crn = Integer.parseInt(s.substring(s.length() - 4));
            }
        }
        return crn;
    }
}
