package org.jboss.windup.rules.apps.java.scan.provider;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.metadata.RuleMetadata;
import org.jboss.windup.config.phase.ArchiveMetadataExtractionPhase;
import org.jboss.windup.config.query.Query;
import org.jboss.windup.config.ruleprovider.IteratingRuleProvider;
import org.jboss.windup.graph.model.ArchiveModel;
import org.jboss.windup.graph.model.LicenseModel;
import org.jboss.windup.graph.model.resource.FileModel;
import org.jboss.windup.graph.service.GraphService;
import org.jboss.windup.reporting.model.TechnologyTagLevel;
import org.jboss.windup.reporting.service.TechnologyTagService;
import org.jboss.windup.util.Logging;
import org.ocpsoft.rewrite.config.ConditionBuilder;
import org.ocpsoft.rewrite.config.Rule;
import org.ocpsoft.rewrite.context.EvaluationContext;

/**
 * Discovers Licenses files within archives.
 *
 * @author <a href="mailto:bradsdavis@gmail.com">Brad Davis</a>
 * @author <a href="mailto:hotmana76@gmail.com">Marek Novotny</a>
 */
@RuleMetadata(phase = ArchiveMetadataExtractionPhase.class, perform = "DiscoverArchiveLicenseFiles")
public class DiscoverArchiveLicenseFilesRuleProvider extends IteratingRuleProvider<ArchiveModel>
{
    private static final Logger LOG = Logging.get(DiscoverArchiveLicenseFilesRuleProvider.class);

    private static final TechnologyTagLevel TECH_TAG_LEVEL = TechnologyTagLevel.INFORMATIONAL;

    @Override
    public ConditionBuilder when()
    {
        return Query.fromType(ArchiveModel.class);
    }

    @Override
    public void perform(GraphRewrite event, EvaluationContext context,
                ArchiveModel payload)
    {
        Rule rule = (Rule) context.get(Rule.class);
        Set<FileModel> licenseFiles = findLicense(payload);
        if (licenseFiles.isEmpty())
        {
            // no licenses found, skip this one
            return;
        }

        TechnologyTagService technologyTagService = new TechnologyTagService(
                    event.getGraphContext());
        GraphService<LicenseModel> licenseService = new GraphService<>(
                    event.getGraphContext(), LicenseModel.class);

        for (FileModel license : licenseFiles)
        {
            LOG.info("Classifying: " + license.getFileName()
                        + " as License within archive: " + payload.getArchiveName());

            // http://opensource.org/licenses/
            try (InputStream stream = license.asInputStream())
            {
                String content = IOUtils.toString(stream);

                if (StringUtils.containsIgnoreCase(content, "Apache License, Version 2.0"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "Apache License 2.0",
                                "Apache License 2.0 File",
                                "http://www.apache.org/licenses/LICENSE-2.0");
                }
                else if (StringUtils.containsIgnoreCase(content, "Apache Software License, Version 1.1"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "Apache License 1.1",
                                "Apache License 1.1 File",
                                "http://www.apache.org/licenses/LICENSE-1.1");
                }
                else if (StringUtils.containsIgnoreCase(content, "Copyright (c) 1995-1999 The Apache Group.  All rights reserved."))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "Apache License 1.0",
                                "Apache License 1.0 File",
                                "http://www.apache.org/licenses/LICENSE-1.0");
                }
                else if (StringUtils.containsIgnoreCase(content, "GNU General Public License"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "GNU GPL",
                                "GNU General Public License File",
                                "http://opensource.org/licenses/gpl-license");
                }
                else if (StringUtils.containsIgnoreCase(content, "The MIT License (MIT)"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "MIT License",
                                "GNU General Public License File",
                                "http://opensource.org/licenses/MIT");
                }
                else if (StringUtils.containsIgnoreCase(content, "Mozilla Public License, version 2.0"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "Mozilla Public License 2.0",
                                "Mozilla Public License 2.0 File",
                                "http://opensource.org/licenses/MPL-2.0");
                }
                else if (StringUtils.containsIgnoreCase(content, "GNU Lesser General Public License"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "GNU LGPL",
                                "GNU LGPL File",
                                "http://opensource.org/licenses/lgpl-license");
                }
                else if (StringUtils.contains(content, "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "CDDL",
                                "CDDL License File",
                                "http://opensource.org/licenses/CDDL-1.0");
                }
                else if (StringUtils.containsIgnoreCase(content, "Eclipse Public License"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "Eclipse Public License 1.0",
                                "Eclipse Public License 1.0 File",
                                "http://opensource.org/licenses/EPL-1.0");
                }
                else if (StringUtils.containsIgnoreCase(content, "Redistribution and use in source and binary forms"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "BSD License",
                                "BSD License File",
                                "http://opensource.org/licenses/");
                }
                else if (StringUtils.containsIgnoreCase(content, "the work of authorship identified is in the public domain of the country"))
                {
                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "Public Domain License",
                                "Creative Commons Public Domain License File",
                                "http://creativecommons.org/licenses/publicdomain/");
                }

                else
                {
                    LOG.warning("Must be unknown license type: " + license.getFileName());

                    tagLicenseByTechnologyTag(rule, licenseService,
                                technologyTagService,
                                license,
                                "Unknown License", "Unknown License File", "Unknown License File");
                }
            }
            catch (IOException e)
            {
                LOG.fine("Error while opening License file: " + license.getFileName()
                + " with error: " + e.getLocalizedMessage());
            }
        }

    }

    private void tagLicenseByTechnologyTag(Rule rule, GraphService<LicenseModel> licenseService,
                TechnologyTagService technologyTagService, FileModel license,
                String name, String description, String url)
    {
        LOG.info("Identified: " + license.getFileName() + " as: " + name);

        // create license model for future reporting.
        LicenseModel model = licenseService.addTypeToModel(license);
        model.setName(name);
        model.setURL(url);
        // even it is not classified or marked as a hint, do include it in reports
        model.setGenerateSourceReport(true);

        // also add it as a tag for reporting on main screen.
        technologyTagService.addTagToFileModel(license, name, TECH_TAG_LEVEL);
    }

    private Set<FileModel> findLicense(ArchiveModel archive)
    {
        Set<FileModel> licenses = new HashSet<>();

        Iterable<FileModel> files = archive.getAllFiles();
        for (FileModel model : files)
        {
            if (model.isDirectory())
            {
                continue;
            }

            String fileName = model.getFileName();
            fileName = StringUtils.lowerCase(fileName);

            if (fileName.endsWith("license.txt")
                        || fileName.endsWith("license")
                        || fileName.endsWith("gpl.txt")
                        || fileName.endsWith("lgpl.txt")
                        || fileName.endsWith("notice.txt")
                        || fileName.endsWith("notice"))
            {
                licenses.add(model);
            }
        }

        return licenses;
    }

}
