/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

package org.netbeans.nbbuild;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;

/**
 * Creates summary of .hgmail coverage.
 */
public class SummarizeHgmail extends Task {

    private File output;
    /** Output file. */
    public void setOutput(File output) {
        this.output = output;
    }

    // DirSet of repo roots might be more natural, but . will not match at root
    private FileSet hgmails;
    /** Hgmail files to summarize. */
    public void addConfiguredHgmails(FileSet hgmails) {
        this.hgmails = hgmails;
    }

    public @Override void execute() throws BuildException {
        try {
            try (PrintWriter w = new PrintWriter(output)) {
                DirectoryScanner scanner = hgmails.getDirectoryScanner(getProject());
                scanner.scan();
                for (String hgmailS : new TreeSet<String>(Arrays.asList(scanner.getIncludedFiles()))) {
                    w.println("SUMMARY OF " + hgmailS);
                    File hgmail = new File(scanner.getBasedir(), hgmailS);
                    File dir = hgmail.getParentFile();
                    File[] kids = dir.listFiles(new FileFilter() {
                        public boolean accept(File f) {
                            return /* contrib/contrib is a self-link */f.exists() && !f.getName().equals(".hg") && !new File(f, ".hg").isDirectory();
                        }
                    });
                    if (kids == null) {
                        throw new BuildException(dir.getPath(), getLocation());
                    }
                    // module dir such as java.source or contrib/autosave or file such as .hgtags, to list of addressees such as core-commits@platform.netbeans.org
                    Map<String,List<String>> notifications = new TreeMap<>();
                    try (Reader r = new FileReader(hgmail)) {
                        BufferedReader br = new BufferedReader(r);
                        String line;
                        while ((line = br.readLine()) != null) {
                            if (line.matches("^[\\[#].+|\\s*")) {
                                continue;
                            }
                            String[] split = line.split("=", 2);
                            if (split.length < 2) {
                                throw new BuildException(line, getLocation());
                            }
                            for (String patt : split[1].trim().split(",")) {
                                boolean isdir = false;
                                if (patt.endsWith("/*")) {
                                    patt = patt.substring(0, patt.length() - 2);
                                    isdir = true;
                                }
                                if (patt.contains("/")) {
                                    throw new BuildException("Notifications of subdirs not yet supported: " + line, getLocation());
                                }
                                List<String> modules = new ArrayList<>();
                                if (patt.contains("*")) {
                                    // Pattern, have to search for matches.
                                    Pattern wild = Pattern.compile("\\Q" + patt.replace("*", "\\E.*\\Q") + "\\E");
                                    for (File kid : kids) {
                                        String name = kid.getName();
                                        if (wild.matcher(name).matches()) {
                                            if (kid.isDirectory() ^ isdir) {
                                                throw new BuildException("Dir pattern matched non-dir or vice-versa: " + kid + " vs. " + line, getLocation());
                                            }
                                            modules.add(name);
                                        }
                                    }
                                }
                                if (modules.isEmpty()) {
                                    // Single file (may or may not exist), or unmatched pattern; so record it.
                                    modules.add(patt);
                                }
                                String addr = split[0].trim();
                                for (String module : modules) {
                                    List<String> addressees = notifications.get(module);
                                    if (addressees == null) {
                                        addressees = new ArrayList<>();
                                        notifications.put(module, addressees);
                                    }
                                    addressees.add(addr);
                                }
                            }
                        }
                    }
                    // Now find unmentioned modules:
                    for (File kid : kids) {
                        String name = kid.getName();
                        if (!notifications.containsKey(name)) {
                            notifications.put(name, new ArrayList<>());
                        }
                    }
                    // Report:
                    for (Map.Entry<String,List<String>> entry : notifications.entrySet()) {
                        String path = entry.getKey();
                        w.println("PATH " + path);
                        if (!new File(dir, path).exists()) {
                            w.println("  NONEXISTENT");
                        }
                        List<String> addressees = entry.getValue();
                        if (addressees.isEmpty()) {
                            w.println("  UNNOTIFIED");
                        } else {
                            if (new HashSet<>(addressees).size() < addressees.size()) {
                                w.println("  DUPLICATES");
                            }
                            Collections.sort(addressees);
                            for (String addressee : addressees) {
                                w.println("  NOTIFIES " + addressee);
                            }
                        }
                    }
                    w.println();
                }
                w.flush();
            }
            log(output + ": hgmail summary written");
        } catch (IOException x) {
            throw new BuildException(x, getLocation());
        }
    }

}
