package it.mice.voila.runtime.web.menu;

import it.mice.voila.runtime.web.bean.MenuDefinition;
import it.mice.voila.runtime.web.bean.MenuItemDefinition;

import java.util.Iterator;
import java.util.Map;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

/**
 * Created by IntelliJ IDEA.
 * User: zzy9v4
 * Date: 5-giu-2006
 * Time: 12.01.17
 * To change this template use File | Settings | File Templates.
 */
public class MenuDefinitionFactory implements InitializingBean {
    public static final String SELECTED_MENU_ID = "jf.menuId";

    private Map<String, MenuDefinition> menus;
    private boolean autoGenerateEmptyLink = true;
    private String menuPageLink = "/menuPage";
    private boolean resetSession = true;

    public Map<String, MenuDefinition> getMenus() {
        return menus;
    }

    public void setMenus(Map<String, MenuDefinition> menus) {
        this.menus = menus;
    }

    public boolean isAutoGenerateEmptyLink() {
        return autoGenerateEmptyLink;
    }

    public void setAutoGenerateEmptyLink(boolean autoGenerateEmptyLink) {
        this.autoGenerateEmptyLink = autoGenerateEmptyLink;
    }

    public String getMenuPageLink() {
        return menuPageLink;
    }

    public void setMenuPageLink(String menuPageLink) {
        this.menuPageLink = menuPageLink;
    }

    public boolean isResetSession() {
        return resetSession;
    }

    public void setResetSession(boolean resetSession) {
        this.resetSession = resetSession;
    }

    public MenuDefinition getMenu(String menuName) {
        return (MenuDefinition) menus.get(menuName);
    }

    /**
     * Invoked by a BeanFactory after it has set all bean properties supplied
     * (and satisfied BeanFactoryAware and ApplicationContextAware).
     * <p>This method allows the bean instance to perform initialization only
     * possible when all bean properties have been set and to throw an
     * exception in the event of misconfiguration.
     *
     * @throws Exception in the event of misconfiguration (such
     *                   as failure to set an essential property) or if initialization fails.
     */
    public void afterPropertiesSet() throws Exception {
        Assert.notEmpty(menus, "At least one menu definition must be provided");
        if (isAutoGenerateEmptyLink()) {
            Iterator<MenuDefinition> i = menus.values().iterator();
            while (i.hasNext()) {
                MenuDefinition menuDefinition = (MenuDefinition) i.next();
                int[] menuLevel = {0,0,0,0,0,0,0,0,0,0};
                generateAutolinkForMenu(menuDefinition, menuLevel, 0);
            }
        }
    }

    private void generateAutolinkForMenu(MenuDefinition menuDefinition, int[] menuLevel, int currentDepth) {
        for (int i=0; i<menuDefinition.getMenuItems().length; i++) {
            MenuItemDefinition menuItemDefinition = menuDefinition.getMenuItems()[i];
            updateMenuLevel(menuLevel, currentDepth);
            //String link = prepareLink(menuItemDefinition.getLink(), menuLevel, currentDepth);
            prepareLink(menuItemDefinition, menuLevel, currentDepth);
            if (menuItemDefinition.getMenuToShow() != null) {
                generateAutolinkForMenu(menuItemDefinition.getMenuToShow(), menuLevel, (currentDepth + 1));
            }
        }
    }

    private void updateMenuLevel(int[] menuLevel, int currentDepth) {
        menuLevel[currentDepth]++;
        for (int i= (currentDepth + 1); i<10; i++) {
            menuLevel[i] = 0;
        }
    }

    private void prepareLink(MenuItemDefinition menuItemDefinition, int[] menuLevel, int currentDepth) {
    	String baseLink = menuItemDefinition.getLink();
        String stringMenuLevel = "";
        String levelDelimiter = "";
        for (int i=0; i<=currentDepth; i++) {
            stringMenuLevel+= levelDelimiter + menuLevel[i];
            levelDelimiter = ".";
        }
        String resultLink = "";
        String paramSep = "?";
        if (baseLink == null || baseLink.trim().length() == 0) {
            resultLink = getMenuPageLink();
        } else {
            resultLink = baseLink;
            if (baseLink.indexOf("?") > -1) {
                paramSep = "&amp;";
            }
        }
        if (menuItemDefinition.isTrackMenuId()) {
            resultLink += paramSep + SELECTED_MENU_ID + "=" + stringMenuLevel;
        }
        if (isResetSession() && menuItemDefinition.isResetSession()) {
            resultLink+= "&amp;resetSession=true";
        }
        menuItemDefinition.setLink(resultLink);
        menuItemDefinition.setItemId(stringMenuLevel);
    }

    /**
     * Returns a string representation of the object. In general, the
     * <code>toString</code> method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * <p/>
     * The <code>toString</code> method for class <code>Object</code>
     * returns a string consisting of the name of the class of which the
     * object is an instance, the at-sign character `<code>@</code>', and
     * the unsigned hexadecimal representation of the hash code of the
     * object. In other words, this method returns a string equal to the
     * value of:
     * <blockquote>
     * <pre>
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * </pre></blockquote>
     *
     * @return a string representation of the object.
     */
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE);
    }
}
