package com.jscape.util;

import java.util.Map;

/**
 * @author Alex Usun
 */
public class MessageFormat {

    public static String format(String message, char startPlaceholder, char endPlaceholder, Map<String, String> replacements) {
        return format(message, new RegularState(startPlaceholder), new MapPlaceholderState(startPlaceholder, endPlaceholder, replacements));
    }

    public static String format(String message, char startPlaceholder, char endPlaceholder, Object[] replacements) {
        return format(message, new RegularState(startPlaceholder), new ArrayPlaceholderState(startPlaceholder, endPlaceholder, replacements));
    }

    public static String format(String message, Object... replacements) {
        return format(message, '{', '}', replacements);
    }

    public static String format(String message, Map<String, String> replacements) {
        return format(message, '{', '}', replacements);
    }

    public static String format(String message, State regularState, State placeholderState) {
        regularState.setNextState(placeholderState);
        placeholderState.setNextState(regularState);

        State state = regularState;
        StringBuffer ret = new StringBuffer();
        for (int i = 0, size = message.length(); i < size; ++i) {
            state = state.onChar(message.charAt(i), ret);
        }

        return ret.toString();
    }

    public interface State {

        State onChar(char ch, StringBuffer data);

        void setNextState(State value);

    }

    private MessageFormat() {
    }

    public static class RegularState
            implements State {

        private final char startPlaceholder;
        private State nextState;

        public RegularState(char startPlaceholder) {
            this.startPlaceholder = startPlaceholder;
        }

        public State onChar(char ch, StringBuffer data) {
            if (ch == this.startPlaceholder) {
                return this.nextState;
            }

            data.append(ch);

            return this;
        }

        public void setNextState(State value) {
            this.nextState = value;
        }

    }


    public static class MapPlaceholderState
            implements State {

        private final char startPlaceholder;
        private final char endPlaceholder;
        private final Map<String, String> replacement;
        private final StringBuffer placeholder;
        private State nextState;

        public MapPlaceholderState(char startPlaceholder, char endPlaceholder, Map<String, String> replacement) {
            this.startPlaceholder = startPlaceholder;
            this.endPlaceholder = endPlaceholder;
            this.replacement = replacement;
            this.placeholder = new StringBuffer();
        }

        public State onChar(char ch, StringBuffer data) {
            if (ch == this.startPlaceholder && this.placeholder.length() == 0) {
                data.append(ch);
                return this.nextState;
            }

            if (ch == this.endPlaceholder) {
                String key = this.placeholder.toString();
                String value = getReplacementValue(key);
                data.append(value);
                this.placeholder.setLength(0);
                return this.nextState;
            }

            this.placeholder.append(ch);

            return this;
        }

        public void setNextState(State value) {
            this.nextState = value;
        }

        protected String getReplacementValue(String key) {
            String ret = (String) this.replacement.get(key);
            return ret != null ? ret : key;
        }
    }

    // faster than convert array to hashtable
    public static class ArrayPlaceholderState
            implements State {

        private final char startPlaceholder;
        private final char endPlaceholder;
        private final Object[] replacement;
        private final StringBuffer placeholder;
        private State nextState;

        public ArrayPlaceholderState(char startPlaceholder, char endPlaceholder, Object[] replacement) {
            this.startPlaceholder = startPlaceholder;
            this.endPlaceholder = endPlaceholder;
            this.replacement = replacement;
            this.placeholder = new StringBuffer();
        }

        public State onChar(char ch, StringBuffer data) {
            if (ch == this.startPlaceholder && this.placeholder.length() == 0) {
                data.append(ch);
                return this.nextState;
            }

            if (ch == this.endPlaceholder) {
                String key = this.placeholder.toString();
                String value = getReplacementValue(key);
                data.append(value);
                this.placeholder.setLength(0);
                return this.nextState;
            }

            this.placeholder.append(ch);

            return this;
        }

        public void setNextState(State value) {
            this.nextState = value;
        }

        protected String getReplacementValue(String key) {
            return this.replacement[Integer.parseInt(key)].toString();
        }
    }


}
