/*
 * Copyright (C) 2020 Skywatch. https://www.skywatch.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option)
 * any later version.
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, see http://www.gnu.org/licenses/
 */
package org.esa.s1tbx.io.capella;

import org.esa.snap.core.dataio.DecodeQualification;
import org.esa.snap.core.dataio.ProductReader;
import org.esa.snap.core.dataio.ProductReaderPlugIn;
import org.esa.snap.core.util.io.SnapFileFilter;
import org.esa.snap.engine_utilities.gpf.ReaderUtils;
import org.esa.snap.engine_utilities.util.ZipUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Locale;

/**
 * The ReaderPlugIn for Capella Product Format.
 */
public class CapellaProductReaderPlugIn implements ProductReaderPlugIn {

    private final static String PLUGIN_DESCRIPTION = "Capella Product Format";
    private final Class[] VALID_INPUT_TYPES = new Class[]{Path.class, File.class, String.class, InputStream.class};

    /**
     * Checks whether the given object is an acceptable input for this product reader and if so, the method checks if it
     * is capable of decoding the input's content.
     *
     * @param input any input object
     * @return true if this product reader can decode the given input, otherwise false.
     */
    public DecodeQualification getDecodeQualification(final Object input) {

        final Path path = ReaderUtils.getPathFromInput(input);
        if (path != null) {
            try {
                String realName = path.toRealPath().getFileName().toString();
                if (ZipUtils.isZip(path)) {
                    for(String prefix : CapellaProductConstants.PRODUCT_PREFIX) {
                        if (realName.startsWith(prefix)) {
                            return DecodeQualification.INTENDED;
                        }
                    }
                }
                if (findMetadataFile(path) != null) {
                    return DecodeQualification.INTENDED;
                }
            } catch (Exception e) {
                System.out.println(e);
            }
        }
        return DecodeQualification.UNABLE;
    }

    public static File findMetadataFile(final Path folderPath) throws IOException {
        final File folder = folderPath.toFile();
        if (folder.isDirectory()) {
            final String[] fileList = folder.list();
            if (fileList != null) {
                for (String f : fileList) {
                    if (isValidProductName(f)) {
                        return new File(folder, f);
                    }
                }
            }
        } else {
            if (isValidProductName(folderPath.toRealPath().getFileName().toString())) {
                return folder;
            }
        }
        return null;
    }

    public static boolean isValidProductName(final String name) {
        final String filename = name.toUpperCase();
        if(filename.endsWith(CapellaProductConstants.PRODUCT_EXT.toUpperCase())) {
            for (String prefix : CapellaProductConstants.PRODUCT_PREFIX) {
                if (filename.startsWith(prefix)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Returns an array containing the classes that represent valid input types for this reader.
     * <p>
     * <p> Intances of the classes returned in this array are valid objects for the <code>setInput</code> method of the
     * <code>ProductReader</code> interface (the method will not throw an <code>InvalidArgumentException</code> in this
     * case).
     *
     * @return an array containing valid input types, never <code>null</code>
     */
    public Class[] getInputTypes() {
        return VALID_INPUT_TYPES;
    }

    /**
     * Creates an instance of the actual product reader class. This method should never return <code>null</code>.
     *
     * @return a new reader instance, never <code>null</code>
     */
    public ProductReader createReaderInstance() {
        return new CapellaProductReader(this);
    }

    public SnapFileFilter getProductFileFilter() {
        return new FileFilter();
    }

    /**
     * Gets the names of the product formats handled by this product I/O plug-in.
     *
     * @return the names of the product formats handled by this product I/O plug-in, never <code>null</code>
     */
    public String[] getFormatNames() {
        return new String[] {CapellaProductConstants.PRODUCT_FORMAT};
    }

    /**
     * Gets the default file extensions associated with each of the format names returned by the <code>{@link
     * #getFormatNames}</code> method. <p>The string array returned shall always have the same length as the array
     * returned by the <code>{@link #getFormatNames}</code> method. <p>The extensions returned in the string array shall
     * always include a leading colon ('.') character, e.g. <code>".hdf"</code>
     *
     * @return the default file extensions for this product I/O plug-in, never <code>null</code>
     */
    public String[] getDefaultFileExtensions() {
        return  new String[] {CapellaProductConstants.PRODUCT_EXT};
    }

    /**
     * Gets a short description of this plug-in. If the given locale is set to <code>null</code> the default locale is
     * used.
     * <p>
     * <p> In a GUI, the description returned could be used as tool-tip text.
     *
     * @param locale the local for the given decription string, if <code>null</code> the default locale is used
     * @return a textual description of this product reader/writer
     */
    public String getDescription(final Locale locale) {
        return PLUGIN_DESCRIPTION;
    }

    public static class FileFilter extends SnapFileFilter {

        public FileFilter() {
            super();
            setFormatName(CapellaProductConstants.PRODUCT_FORMAT);
            setExtensions(new String[] {CapellaProductConstants.PRODUCT_EXT});
            setDescription(PLUGIN_DESCRIPTION);
        }

        /**
         * Tests whether or not the given file is accepted by this filter. The default implementation returns
         * <code>true</code> if the given file is a directory or the path string ends with one of the registered extensions.
         * if no extension are defined, the method always returns <code>true</code>
         *
         * @param file the file to be or not be accepted.
         * @return <code>true</code> if given file is accepted by this filter
         */
        public boolean accept(final File file) {
            if (super.accept(file)) {
                final String filename = file.getName().toUpperCase();
                return file.isDirectory() || isValidProductName(filename);
            }
            return false;
        }
    }
}