/**
 * Copyright (c) 2010-2016 by the respective copyright holders.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.binding.ebus.tools;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.openhab.binding.ebus.internal.parser.EBusConfigurationProvider;

/**
 * eBUS Helper Script to check configurations and create markdown files
 *
 * @author Christian Sowada
 * @since 1.7.1
 */
public class EBusJsonConfTool {

    static String ALLOWED_LEVEL_1_PARAMS[] = { "comment", "device", "id", "class", "command", "data", "values",
            "computed_values", "debug" };

    /**
     * @param args
     */
    public static void main(String[] args) {

        EBusJsonConfTool m = new EBusJsonConfTool();
        try {
            m.run();

        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @SuppressWarnings("unchecked")
    private List<List<String>> createTelegramIdTable(List<Map<String, ?>> configurationMap) {

        List<List<String>> m = new ArrayList<List<String>>();

        for (Map<String, ?> entry : configurationMap) {

            Map<String, Map<String, ?>> valueEntries = new HashMap<String, Map<String, ?>>();

            Map<String, Map<String, ?>> x = (Map<String, Map<String, ?>>) entry.get("values");
            if (x != null && !x.isEmpty()) {
                valueEntries.putAll(x);
            }

            boolean singleValue = x != null ? x.size() == 1 : false;

            x = (Map<String, Map<String, ?>>) entry.get("computed_values");
            if (x != null && !x.isEmpty()) {
                valueEntries.putAll(x);
            }

            for (String valueKey : valueEntries.keySet()) {
                Map<String, ?> map = valueEntries.get(valueKey);

                if (!map.containsKey("hide") && !StringUtils.startsWith(valueKey, "_")) {
                    List<String> line = new ArrayList<String>();
                    m.add(line);

                    if (entry.containsKey("class") && entry.containsKey("id")) {
                        if (singleValue && !((String) entry.get("id")).startsWith("set_")) {
                            line.add("**" + (String) entry.get("class") + "." + (String) entry.get("id") + "**");
                        } else {
                            line.add("**" + (String) entry.get("class") + "." + (String) entry.get("id") + "**."
                                    + valueKey);
                        }
                    } else if (entry.containsKey("class")) {
                        line.add((String) entry.get("class") + "." + valueKey);

                    } else {
                        line.add(valueKey);
                    }

                    // if(!StringUtils.isEmpty((String) entry.get("id"))) {
                    // line.add((String) entry.get("class") + "." + (String) entry.get("id"));
                    // } else {
                    // line.add("---");
                    // }
                    // line.add("---");
                    // line.add((String) entry.get("class"));
                    // line.add(StringUtils.defaultIfEmpty((String) entry.get("id"), "-"));

                    String t = (String) map.get("type");
                    if (map.containsKey("type_hint")) {
                        t = (String) map.get("type_hint");
                    }

                    t = t.equalsIgnoreCase("bit") ? "Switch"
                            : t.equalsIgnoreCase("script") ? "???" : t.equalsIgnoreCase("string") ? "Text" : "Number";
                    t = "``" + t + "``";
                    line.add(t);

                    t = (String) map.get("label");
                    t = t == null || t.equals("") ? "" : t;

                    if (map.containsKey("mapping")) {
                        t += " - " + map.get("mapping").toString();
                    }

                    line.add(t);
                }
            }
        }

        return m;
    }

    private void writeMarkdownIdTable(List<Map<String, ?>> configurationMap, PrintStream out) {

        if (configurationMap == null || configurationMap.isEmpty()) {
            return;
        }

        List<List<String>> table = createTelegramIdTable(configurationMap);

        // set sort order by column number
        final int sortColumns[] = new int[] { 1, 0 };
        Collections.sort(table, new Comparator<List<String>>() {
            @Override
            public int compare(List<String> line1, List<String> line2) {
                int compareTo = 0;
                for (int i : sortColumns) {
                    compareTo = ObjectUtils.compare(line1.get(i), line2.get(i));
                    if (compareTo != 0) {
                        return compareTo;
                    }
                }
                return compareTo;
            }
        });

        // Add headers
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("ID");
        // arrayList.add("Class");
        // arrayList.add("Command");
        arrayList.add("Item type");
        arrayList.add("Description");
        table.add(0, arrayList);

        // Add Markdown header delimiters
        arrayList = new ArrayList<String>();
        for (int i = 0; i < table.get(0).size(); i++) {
            arrayList.add("---");
        }
        table.add(1, arrayList);

        // compute max colum width
        int columnWidth[] = new int[arrayList.size()];
        for (List<String> column : table) {
            for (int i = 0; i < column.size(); i++) {
                String elm = column.get(i);
                if (elm != null && elm.length() > columnWidth[i]) {
                    columnWidth[i] = elm.length();
                }
            }
        }

        for (List<String> column : table) {
            for (int i = 0; i < column.size(); i++) {
                String elm = column.get(i);
                out.print(String.format("%-" + columnWidth[i] + "s", elm));
                if (i < column.size() - 1) {
                    out.print(" | ");
                }
            }

            // line end
            out.print("\n");
        }
    }

    private void checkConfiguration(List<Map<String, ?>> configurationMap) {

        Map<String, String> doubleEntries = new HashMap<String, String>();

        for (Map<String, ?> entryMap : configurationMap) {

            StringBuilder sb = new StringBuilder();
            String unid = (String) (entryMap.containsKey("command")
                    ? entryMap.get("command") + " XX " + entryMap.get("data") : entryMap.get("filter"));

            if (doubleEntries.containsKey(unid)) {
                sb.append("Combination of command and data already existing [" + doubleEntries.get(unid) + "]... "
                        + unid + "\n");
            } else {
                doubleEntries.put(unid, (String) entryMap.get("comment"));
            }

            if (!entryMap.containsKey("comment") || entryMap.get("comment").equals("")) {
                sb.append("No comment item ...\n");
            }

            if (entryMap.containsKey("debug")) {
                sb.append("Remove debug switch ...\n");
            }

            if (sb.length() > 0) {
                System.err.println("Error/Warning on item " + entryMap.get("comment"));
                System.err.println(sb.toString());
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void run() throws JsonParseException, JsonMappingException, IOException {

        final File currentDir = new File("").getAbsoluteFile();
        final ObjectMapper mapper = new ObjectMapper();

        List<Map<String, ?>> configurationMap = new ArrayList<Map<String, ?>>();

        File folder = new File(currentDir, "src/main/resources/");
        File[] listOfFiles = folder.listFiles();
        PrintStream out = null;

        PrintStream mainOut = new PrintStream(new File(currentDir, "docs/json-configs.md"));
        mainOut.print("# JSON configuration files\n");
        mainOut.print("\n");
        mainOut.print("This is an automatic created list of all included configuration files.\n");
        mainOut.print("\n");

        EBusConfigurationProvider ebuscfg = new EBusConfigurationProvider();

        for (File file : listOfFiles) {
            if (file.getName().endsWith("configuration.json")) {
                String string = StringUtils.substringBefore(file.getName(), "-configuration.json");
                out = new PrintStream(new File(currentDir, "docs/json-files/" + string + ".md"));

                out.print("# JSON configuration for _" + string + "_\n");
                out.print("\n");

                ebuscfg.loadConfigurationFile(file.toURI().toURL());

                List<Map<String, ?>> readValue = mapper.readValue(file, List.class);
                writeMarkdownIdTable(readValue, out);

                out.print("\n");
                out.print("_bold part is the command-id part_\n");
                out.print("\n");

                configurationMap.addAll(readValue);
                out.flush();
                out.close();

                mainOut.print("* [" + string + "](./json-files/" + string + ".md)\n");
            }

        }

        mainOut.println();
        mainOut.flush();
        mainOut.close();

        checkConfiguration(configurationMap);
    }

}