/*
 * Copyright 2010 JBoss Inc
 *
 * 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.drools.ide.common.server.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.mvel2.MVEL;

/**
 * Use mvel to load up map/list of valid items for fields - used by the Guided rule editor.
 */
public class DataEnumLoader {

    private final List<String> errors;
    private final Map<String, String[]> data;

    /**
     * This is the source of the asset, which is an MVEL map (minus the outer "[") of course.
     */
    public DataEnumLoader(String mvelSource) {
        errors = new ArrayList<String>();
        this.data = loadEnum(mvelSource);
    }

    private Map<String, String[]> loadEnum(String mvelSource) {

        if (mvelSource == null || (mvelSource.trim().equals( "" ))) {
            return Collections.emptyMap();
        }
        if (mvelSource.startsWith("=")) {
            mvelSource = mvelSource.substring(1);
        } else {
            mvelSource = "[ " + addCommasForNewLines(mvelSource) + " ]";
        }
        final Object mvelData;
        try {
            mvelData = MVEL.eval(mvelSource, new HashMap<String, Object>());
        } catch (RuntimeException e) {
            addError("Unable to load enumeration data.");
            addError(e.getMessage());
            addError("Error type: " + e.getClass().getName());
            return Collections.emptyMap();
        }
        if (!(mvelData instanceof Map<?, ?>)) {
            addError("The expression is not a map, it is a " + mvelData.getClass().getName());
            return Collections.emptyMap();
        }
        Map<String, Object> map = (Map<String, Object>) mvelData;
        Map<String, String[]> newMap = new HashMap<String, String[]>();
        for (Map.Entry<String, Object> entry: map.entrySet()) {
            String key = entry.getKey();
            Object list = entry.getValue();
            if (!(list instanceof List<?> || list instanceof String)) {
                if (list == null) {
                    addError("The item with " + key + " is null.");
                } else {
                    addError("The item with " + key + " is not a list or a string, it is a " + list.getClass().getName());
                }
                return Collections.emptyMap();
            } else if (list instanceof String) {
                newMap.put(key, new String[] {(String)list});
            } else {
                List<?> items = (List<?>) list;
                String[] newItems = new String[items.size()];
                for (int i = 0; i < items.size(); i++) {
                    Object listItem = items.get(i);
                    if (!(listItem instanceof String)) {
                        newItems[i] = listItem.toString();
                    } else {
                        newItems[i] = (String) listItem;
                    }
                }
                newMap.put(key, newItems);
            }
        }
        return newMap;
    }

    public static String addCommasForNewLines(String mvelSource) {
        StringTokenizer st = new StringTokenizer(mvelSource, "\r\n");
        StringBuilder buf = new StringBuilder();
        while(st.hasMoreTokens()) {
            String line = st.nextToken().trim();
            if (st.hasMoreTokens() && line.endsWith( "," )) {
                buf.append( line );
            } else {
                buf.append( line );
                if (st.hasMoreTokens()) {
                    buf.append( "," );
                }
            }
            if (st.hasMoreTokens()) {
                buf.append( "\n" );
            }
        }
        return buf.toString();
    }

    private void addError(String string) {
        this.errors.add(string);
    }

    /**
     * Return a list of any errors found.
     */
    public List<String> getErrors() {
        return this.errors;
    }

    public boolean hasErrors() {
        return this.errors.size() > 0;
    }

    /**
     * Return the map of Fact.field to List (of Strings).
     */
    public Map<String, String[]> getData() {
        return this.data;
    }

}
