/*
 * citygml-tools - Collection of tools for processing CityGML files
 * https://github.com/citygml4j/citygml-tools
 *
 * citygml-tools is part of the citygml4j project
 *
 * Copyright 2018-2023 Claus Nagel <claus.nagel@gmail.com>
 *
 * Licensed 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.citygml4j.tools.option;

import org.citygml4j.xml.CityGMLContext;
import org.citygml4j.xml.module.Module;
import org.citygml4j.xml.module.citygml.CityGMLModules;
import picocli.CommandLine;

import javax.xml.namespace.QName;
import java.util.*;

public class TypeNamesOption implements Option {
    @CommandLine.Option(names = {"-t", "--type-name"}, split = ",", paramLabel = "<[prefix:]name>", required = true,
            description = "Names of the top-level city objects to process.")
    private String[] typeNames;

    @CommandLine.Option(names = "--namespace", split = ",", paramLabel = "<prefix=name>",
            description = "Prefix-to-namespace mappings.")
    private Map<String, String> namespaces;

    public Set<QName> getTypeNames(CityGMLContext context) {
        Set<QName> names = new HashSet<>();
        if (typeNames != null) {
            Map<String, List<String>> defaultNamespaces = getDefaultNamespaces();
            for (String typeName : typeNames) {
                String[] parts = typeName.split(":");
                if (parts.length == 2) {
                    String candidate = namespaces != null ? namespaces.get(parts[0]) : null;
                    if (candidate != null) {
                        names.add(new QName(candidate, parts[1]));
                    } else {
                        defaultNamespaces.getOrDefault(parts[0], Collections.emptyList()).stream()
                                .map(namespace -> new QName(namespace, parts[1]))
                                .forEach(names::add);
                    }
                } else {
                    defaultNamespaces.values().stream()
                            .flatMap(Collection::stream)
                            .filter(namespace -> context.getXMLObjects().getBuilder(namespace, parts[0]) != null)
                            .map(namespace -> new QName(namespace, parts[0]))
                            .forEach(names::add);
                }
            }
        }

        return names;
    }

    private Map<String, List<String>> getDefaultNamespaces() {
        Map<String, List<String>> namespaces = new HashMap<>();
        for (CityGMLModules context : CityGMLModules.all()) {
            for (Module module : context.getModules()) {
                namespaces.computeIfAbsent(module.getNamespacePrefix(), v -> new ArrayList<>())
                        .add(module.getNamespaceURI());
            }
        }

        return namespaces;
    }

    @Override
    public void preprocess(CommandLine commandLine) throws Exception {
        if (typeNames != null) {
            for (String typeName : typeNames) {
                String[] parts = typeName.split(":");
                if (parts.length == 0 || parts.length > 2) {
                    throw new CommandLine.ParameterException(commandLine,
                            "A type name must be in [PREFIX:]NAME format but was '" + typeName + "'");
                }
            }
        }
    }
}
