/*
 * 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.
 *
 */

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

/**
 * Utility class to parse Nexus staging repo and optionally download all the Maven artifacts for a given RC.
 * <p>
 * $ java NexusGet nexusId [download folder] [.type]
 * <p>
 * The nexusId is either a 4 digit id, in which case it is assumed to be under orgapachecommons,
 * <br>
 * or it is the name of a /content/ folder parent as in {project}-nnnn below
 * <br>
 * https://repository.apache.org/service/local/repositories/{project}-nnnn/content/
 * <br>
 * or it is a full URL as above (can also be a subdirectory of content)
 * <p>
 * Note that Nexus does not redirect if the trailing "/" is missing
 */
public class NexusGet {

    private static final String COMMONS_BASE = "https://repository.apache.org/service/local/repositories/orgapachecommons-%s/content/";
    private static final String NEXUS_BASE = "https://repository.apache.org/service/local/repositories/%s/content/";

    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            System.err.println("Syntax: java NexusGet nexusId [download folder] [.type]");
            System.err.println("Need nexusId, e.g.");
            System.err.println("\t1234 (orgapachecommons is assumed) or");
            System.err.println("\torgapachecommons-1234 or");
            System.err.println("\tURL to https://repository.apache.org/service/local/repositories/{project}-nnnn/content/ or below");
            System.err.println("\tIf the download folder name starts with '.' it is assumed to be the file type, and the contents are displayed");
            return;
        }
        
        final String param = args[0];
        final String start;
        if (param.length() == 4) { // we assume commons Nexus id
            start = String.format(COMMONS_BASE, param);            
        } else if (!param.contains("/")){ // assume it is orgapachecommons-1234
            start = String.format(NEXUS_BASE, param);            
        } else { // Must be a full URL
            start = param;
        }
        final String folder; 
        if (args.length > 1) {
            folder = args[1];
        } else {
            folder = null;
        }
        XMLInputFactory fact = XMLInputFactory.newFactory();
        parseNexus(start, folder, fact);
    }

    private static void download(URL url, File localFilename) throws IOException {
        byte[] buffer = new byte[4096];
        int len;
        URLConnection urlConn = url.openConnection();
        try (FileOutputStream fos = new FileOutputStream(localFilename);InputStream is = urlConn.getInputStream();){
            while ((len = is.read(buffer)) > 0) {  
                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            System.err.println("Error writing file: " + e);
            System.exit(1);
        }
    }

    /*
     * We assume that the XML contains an entry with the tag <resourceURI>
     * whose value is either a file name or a directory with trailing "/".
     * So we don't need to bother to check the <leaf> tag.
     */
    private static void parseNexus(String  name, String folder, XMLInputFactory fact)
            throws XMLStreamException, IOException {
        URLConnection urlConn = new URL(name).openConnection();
        try (InputStream is = urlConn.getInputStream()) {
            XMLStreamReader xr = fact.createXMLStreamReader(is);
            while(xr.hasNext()) {
                if (xr.isStartElement()) {
                    if (xr.getName().toString().equals("resourceURI")) {
                        xr.next();
                        final String text = xr.getText();
                        if (text.endsWith("/")) {
                            try {
                                parseNexus(text, folder, fact);
                            } catch (IOException ioe){
                                System.out.println("Retrying once; Nexus sometimes objects " + ioe);
                                ioe.printStackTrace();
                                parseNexus(text, folder, fact);                                
                            }
                        } else {
                            getFile(text, folder);
                        }
                        continue;
                    }
                }
                xr.next();
            }
            xr.close();
        }
    }

    private static void getFile(final String nexusUrl, String folder) throws IOException {
        final URL url = new  URL(nexusUrl);
        final String name = new File(url.getPath()).getName();
        if (name.startsWith("maven-metadata.xml") || name.equals("archetype-catalog.xml")) {
            return; // not wanted
        }
        if (folder != null) {
            final File path = new File(folder, name);
            if (path.exists()) {
                System.out.println(path + " exists locally");
            } else {
                System.out.println(nexusUrl + " => " + path);
                download(url, path);
            }
        } else {
            System.out.println(nexusUrl);
        }
    }

}
