/*
 * #%L
 * gitools-ui-platform
 * %%
 * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
package org.gitools.ui.platform.help;

import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class
        Help {

    private static Help instance;
    private final PropertiesExpansion properties;
    private final List<UrlMap> urlMap;

    Help() {
        this(new Properties(), new ArrayList<UrlMap>());
    }

    private Help(Properties properties, List<UrlMap> urlMap) {
        this.properties = new PropertiesExpansion(properties);
        this.urlMap = urlMap;
    }

    public static Help get() {
        if (instance == null) {
            instance = new Help();
        }

        return instance;
    }

    public void loadProperties(InputStream in) throws IOException {
        properties.load(in);
        in.close();
    }

    public void loadUrlMap(InputStream in) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String line;
        int lineNum = 1;
        while ((line = br.readLine()) != null) {
            line = line.trim();
            if (!line.isEmpty()) {
                String[] fields = line.split("\t");

                if (fields.length == 2) {
                    urlMap.add(new UrlMap(Pattern.compile(fields[0]), fields[1]));
                } else {
                    throw new Exception("Error reading help url mappings:" +
                            " Two columns expected at line " + lineNum);
                }
            }
            lineNum++;
        }
        br.close();
    }

    URL getHelpUrl(HelpContext context) throws MalformedURLException {
        String id = context.getId();
        String urlStr = null; // FIXME Use a default url
        for (UrlMap map : urlMap) {
            Matcher matcher = map.getPattern().matcher(id);
            if (matcher.matches()) {
                Properties p = new Properties(properties);
                for (int i = 0; i < matcher.groupCount(); i++)
                    p.setProperty("" + i, matcher.group(i));
                urlStr = expandPattern(properties, map.getUrl());
                break;
            }
        }

        return new URL(urlStr);
    }

    public void showHelp(HelpContext context) throws HelpException {
        try {
            URL url = getHelpUrl(context);
            Desktop.getDesktop().browse(url.toURI());
        } catch (Exception ex) {
            throw new HelpException(ex);
        }
    }

    private String expandPattern(Properties properties, String pattern) {

        final StringBuilder output = new StringBuilder();
        final StringBuilder var = new StringBuilder();

        char state = 'C';

        int pos = 0;

        while (pos < pattern.length()) {

            char ch = pattern.charAt(pos++);

            switch (state) {
                case 'C': // copying normal characters
                    if (ch == '$') {
                        state = '$';
                    } else {
                        output.append(ch);
                    }
                    break;

                case '$': // start of variable
                    if (ch == '{') {
                        state = 'V';
                    } else {
                        output.append('$').append(ch);
                        state = 'C';
                    }
                    break;

                case 'V': // reading name of variable
                    if (ch == '}') {
                        state = 'X';
                    } else {
                        var.append(ch);
                    }
                    break;

                case 'X': // expand variable
                    output.append(properties.getProperty(var.toString()));
                    var.setLength(0);
                    pos--;
                    state = 'C';
                    break;
            }
        }

        switch (state) {
            case '$':
                output.append('$');
                break;
            case 'V':
                output.append("${").append(var);
                break;
            case 'X':
                output.append(properties.getProperty(var.toString()));
                break;
        }

        return output.toString();
    }

    public static class UrlMap {
        private final Pattern pattern;
        private final String url;

        public UrlMap(Pattern pattern, String url) {
            this.pattern = pattern;
            this.url = url;
        }

        public Pattern getPattern() {
            return pattern;
        }

        public String getUrl() {
            return url;
        }
    }
}
