/*
 * Copyright 2015-present Open Networking Foundation
 *
 * 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.onosproject.cli.net;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.ImmutableList;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.net.driver.Behaviour;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.driver.DriverService;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Lists device drivers.
 */
@Command(scope = "onos", name = "drivers",
        description = "Lists device drivers")
public class DriversListCommand extends AbstractShellCommand {

    private static final String FMT = "driver=%s, extends=%s, mfr=%s, hw=%s, sw=%s";
    private static final String FMT_B = "   %s via %s";
    private static final String FMT_P = "   %s=%s";

    @Argument(index = 0, name = "driverName", description = "Driver name",
            required = false, multiValued = false)
    String driverName = null;

    @Override
    protected void execute() {
        DriverService service = get(DriverService.class);

        if (driverName != null) {
            printDriver(service.getDriver(driverName), true);
        } else {
            if (outputJson()) {
                json(service.getDrivers());
            } else {
                service.getDrivers().forEach(d -> printDriver(d, true));
            }
        }
    }

    private void json(Driver driver) {
        print("%s", jsonForEntity(driver, Driver.class));
    }

    private void json(Set<Driver> drivers) {
        ArrayNode result = mapper().createArrayNode();
        drivers.forEach(driver -> result.add(jsonForEntity(driver, Driver.class)));
        print("%s", result.toString());
    }

    private void printDriver(Driver driver, boolean first) {
        if (outputJson()) {
            json(driver);
        } else {

            List<Driver> parents = Optional.ofNullable(driver.parents())
                    .orElse(ImmutableList.of());

            List<String> parentsNames = parents.stream()
                    .map(Driver::name).collect(Collectors.toList());

            if (first) {
                print(FMT, driver.name(), parentsNames,
                      driver.manufacturer(), driver.hwVersion(), driver.swVersion());
            } else {
                print("   Inherited from %s", driver.name());
            }

            driver.behaviours().forEach(b -> printBehaviour(b, driver));
            driver.properties().forEach((k, v) -> print(FMT_P, k, v));

            //recursion call to print each parent
            parents.stream().forEach(parent -> printDriver(parent, false));
        }
    }

    private void printBehaviour(Class<? extends Behaviour> behaviour, Driver driver) {
        print(FMT_B, behaviour.getCanonicalName(),
              driver.implementation(behaviour).getCanonicalName());
    }

}

@@ -836,32 +836,79 @@
 mmands.Command;%0A
+import org.apache.karaf.shell.commands.Option;%0A
 import org.onosp
@@ -932,32 +932,32 @@
 ctShellCommand;%0A
-
 import org.onosp
@@ -1077,16 +1077,45 @@
 rvice;%0A%0A
+import java.util.Comparator;%0A
 import j
@@ -1752,16 +1752,365 @@
  null;%0A%0A
+    @Option(name = %22-s%22, aliases = %22--sort%22, description = %22Sort output by driver name%22,%0A            required = false, multiValued = false)%0A    private boolean sort = false;%0A%0A    @Option(name = %22-n%22, aliases = %22--name%22, description = %22Show driver name only%22,%0A            required = false, multiValued = false)%0A    private boolean nameOnly = false;%0A%0A
     @Ove
@@ -2447,24 +2447,139 @@
 getDrivers()
+%0A                    .stream()%0A                    .sorted(Comparator.comparing(Driver::name))%0A                    
 .forEach(d -
@@ -3084,16 +3084,87 @@
 river);%0A
+        %7D else if (nameOnly) %7B%0A            print(%22%25s%22, driver.name());%0A
         
@@ -3172,17 +3172,16 @@
  else %7B%0A
-%0A
         

Add option to print loaded driver name only