/*
 * Copyright 2013, 2014 Deutsche Nationalbibliothek
 *
 * 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.metafacture.flux;

import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.metafacture.commons.ResourceUtil;
import org.metafacture.commons.reflection.ObjectFactory;
import org.metafacture.framework.MetafactureException;
import org.metafacture.framework.annotations.Description;
import org.metafacture.framework.annotations.In;
import org.metafacture.framework.annotations.Out;
import org.metafacture.framework.annotations.ReturnsAvailableArguments;

/**
 * prints Flux help for a given {@link ObjectFactory}
 *
 * @author Markus Michael Geipel
 */
public final class HelpPrinter {
    private HelpPrinter() {
        // no instances
    }

    public static void print(final ObjectFactory<?> factory,
            final PrintStream out) {
        out.println("WELCOME TO METAFACTURE");
        out.println(getVersionInfo());

        out.println("\nUsage:\tflux FLOW_FILE [VARNAME=VALUE ...]\n");
        out.println("Available pipe elements:\n");

        final List<String> keyWords = new ArrayList<String>();
        keyWords.addAll(factory.keySet());
        Collections.sort(keyWords);
        for (String name : keyWords) {
            describe(name, factory, out);
        }
    }

    private static String getVersionInfo() {
        try {
            return ResourceUtil.loadProperties("build.properties").toString();
        } catch (IOException e) {
            throw new MetafactureException("Failed to load build infos", e);
        }
    }

    private static <T> void describe(String name, ObjectFactory<T> factory,
            PrintStream out) {
        final Class<? extends T> moduleClass = factory.get(name).getPlainClass();
        final Description desc = moduleClass.getAnnotation(Description.class);

        out.println(name);

        if (desc != null) {
            out.println("description:\t" + desc.value());
        }
        final Collection<String> arguments = getAvailableArguments(moduleClass);
        if (!arguments.isEmpty()) {
            out.println("argument in\t" + arguments);
        }

        final Map<String, Class<?>> attributes = factory.get(name).getSetterTypes();

        if (!attributes.isEmpty()) {
            out.print("options:\t");
            final StringBuilder builder = new StringBuilder();
            for (Entry<String, Class<?>> entry : attributes.entrySet()) {
                if (entry.getValue().isEnum()) {
                    builder.append(entry.getKey())
                            .append(" ")
                            .append(Arrays.toString(entry.getValue().getEnumConstants()))
                            .append(", ");
                } else {
                    builder.append(entry.getKey())
                            .append(" (")
                            .append(entry.getValue().getName())
                            .append("), ");
                }

            }
            out.println(builder.substring(0, builder.length() - 2));
        }

        out.println("implementation:\t" + moduleClass.getCanonicalName());
        String inString = "<unknown>";
        String outString = "";
        final In inClass = moduleClass.getAnnotation(In.class);
        if (inClass != null) {
            inString = inClass.value().getCanonicalName();
        }
        final Out outClass = moduleClass.getAnnotation(Out.class);
        if (outClass != null) {
            outString = outClass.value().getCanonicalName();
        }
        out.println("signature:\t" + inString + " -> " + outString);
        out.println();
    }

    @SuppressWarnings("unchecked")
    private static Collection<String> getAvailableArguments(
            Class<?> moduleClass) {
        for (Method method : moduleClass.getMethods()) {
            if (method.getAnnotation(ReturnsAvailableArguments.class) != null) {
                try {
                    return (Collection<String>) method.invoke(moduleClass);
                } catch (IllegalAccessException | InvocationTargetException |
                        IllegalArgumentException e) {
                    // silently ignore
                }
            }
        }
        return Collections.emptyList();
    }

}

@@ -1667,30 +1667,100 @@
 n(%22W
-ELCOME TO METAFACTURE%22
+elcome to Metafacture%22);%0A        out.println(%22======================%22);%0A        out.println(
 );%0A 
@@ -1904,20 +1904,20 @@
 ble 
-pipe element
+flux command
 s:%5Cn
@@ -2722,16 +2722,114 @@
 n(name);
+%0A        name.chars().forEach(c-%3E %7B%0A            out.print(%22-%22);%0A        %7D);%0A        out.println();
 %0A%0A      
@@ -2875,16 +2875,18 @@
 rintln(%22
+- 
 descript
@@ -3062,24 +3062,26 @@
 rintln(%22
+- 
 argument
  in%5Ct%22 +
@@ -3072,19 +3072,18 @@
 argument
- in
+s:
 %5Ct%22 + ar
@@ -3249,16 +3249,18 @@
 .print(%22
+- 
 options:
@@ -3586,24 +3586,22 @@
 (Arrays.
-toString
+asList
 (entry.g
@@ -3848,16 +3848,22 @@
 ue().get
+Simple
 Name())%0A
@@ -4023,83 +4023,8 @@
  %7D%0A%0A
-        out.println(%22implementation:%5Ct%22 + moduleClass.getCanonicalName());%0A
     
@@ -4222,33 +4222,30 @@
 .value().get
-Canonical
+Simple
 Name();%0A    
@@ -4397,17 +4397,14 @@
 .get
-Canonical
+Simple
 Name
@@ -4438,16 +4438,18 @@
 rintln(%22
+- 
 signatur
@@ -4488,16 +4488,89 @@
 tring);%0A
+        out.println(%22- java class:%5Ct%22 + moduleClass.getCanonicalName());%0A
         

Tweak help output for Markdown formatting