package org.springframework.richclient.components;

import org.springframework.richclient.core.Message;
import org.springframework.richclient.core.Severity;
import org.springframework.richclient.image.IconSource;
import org.springframework.richclient.application.ApplicationServicesLocator;

import javax.swing.*;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
import java.util.Map;
import java.awt.*;

/**
 * @author Jan Hoskens
 * @author Geoffrey De Smet
 */
public class MessagableTabbedPane extends JTabbedPane implements MessagableTab {

    private List<MessagableTab> messagableTabs = new ArrayList<MessagableTab>();

    private IconSource iconSource;

    public MessagableTabbedPane() {
        super();
    }

    public MessagableTabbedPane(int tabPlacement) {
        super(tabPlacement);
    }

    public MessagableTabbedPane(int tabPlacement, int tabPolicy) {
        super(tabPlacement, tabPolicy);
    }

    @Override
    public void insertTab(String title, Icon icon, Component component, String tip, int index) {
        super.insertTab(title, icon, component, tip, index);
        messagableTabs.add(index, new MessagableTab());
    }

    @Override
    public void setIconAt(int index, Icon icon) {
        // Hack to allow the error icon to overwrite the real icon
        MessagableTab messagableTab = messagableTabs.get(index);
        messagableTab.setIcon(icon);
        if (!messagableTab.hasErrors()) {
            super.setIconAt(index, icon);
        }
    }

    @Override
    public void setToolTipTextAt(int index, String toolTipText) {
        // Hack to allow the error toolTipText to overwrite the real toolTipText
        MessagableTab messagableTab = messagableTabs.get(index);
        messagableTab.setToolTipText(toolTipText);
        if (!messagableTab.hasErrors()) {
            super.setToolTipTextAt(index, toolTipText);
        }
    }

    public void setMessage(Object source, Message message, int index) {
        MessagableTab messagableTab = messagableTabs.get(index);
        messagableTab.put(source, message);
        if (messagableTab.hasErrors()) {
            // Calling super to avoid the error icon/toolTipText overwrite hack
            super.setIconAt(index, loadIcon(Severity.ERROR.getLabel()));
            super.setToolTipTextAt(index, messagableTab.getFirstErrorMessage());
        } else {
            // Calling super to avoid the error icon/toolTipText overwrite hack
            super.setIconAt(index, messagableTab.getIcon());
            super.setToolTipTextAt(index, messagableTab.getToolTipText());
        }
    }

    private Icon loadIcon(String severityLabel) {
        if (iconSource == null) {
            iconSource = (IconSource) ApplicationServicesLocator.services().getService(IconSource.class);
        }
        return iconSource.getIcon("severity." + severityLabel + ".overlay");
    }

    private static class MessagableTab {

        private Map<Object, Message> messageMap = new HashMap<Object, Message>();
        private Stack<Message> errorMessageStack = new Stack<Message>();

        private Icon icon = null;
        private String toolTipText = null;

        public void put(Object key, Message message) {
            Message oldMessage = messageMap.get(key);
            if (oldMessage != message) {
                // Update errorMessageStack
                if ((oldMessage != null) && (oldMessage.getSeverity() == Severity.ERROR)) {
                    errorMessageStack.remove(oldMessage);
                }
                if ((message != null) && (message.getSeverity() == Severity.ERROR)) {
                    errorMessageStack.add(message);
                }
                // Update messageMap
                if (message != null) {
                    messageMap.put(key, message);
                } else {
                    messageMap.remove(key);
                }
            }
        }

        public Message get(Object key) {
            return messageMap.get(key);
        }

        public String getFirstErrorMessage() {
            if (!hasErrors()) {
                return null;
            }
            return errorMessageStack.firstElement().getMessage();
        }

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

        public Icon getIcon() {
            return icon;
        }

        public void setIcon(Icon icon) {
            this.icon = icon;
        }

        public String getToolTipText() {
            return toolTipText;
        }

        public void setToolTipText(String toolTipText) {
            this.toolTipText = toolTipText;
        }
    }

}

@@ -612,36 +612,32 @@
 ist%3C
-MessagableTab%3E messagableTab
+TabMetaData%3E tabMetaData
 s = 
@@ -650,29 +650,27 @@
 rayList%3C
-MessagableTab
+TabMetaData
 %3E();%0A%0A  
@@ -1145,29 +1145,27 @@
         
-messagableTab
+tabMetaData
 s.add(in
@@ -1173,29 +1173,27 @@
 ex, new 
-MessagableTab
+TabMetaData
 ());%0A   
@@ -1339,51 +1339,45 @@
     
-MessagableTab messagableTab = messagableTab
+TabMetaData tabMetaData = tabMetaData
 s.ge
@@ -1390,37 +1390,35 @@
 x);%0A        
-messagableTab
+tabMetaData
 .setIcon(ico
@@ -1430,37 +1430,35 @@
        if (!
-messagableTab
+tabMetaData
 .hasErrors()
@@ -1693,51 +1693,45 @@
     
-MessagableTab messagableTab = messagableTab
+TabMetaData tabMetaData = tabMetaData
 s.ge
@@ -1748,29 +1748,27 @@
         
-messagableTab
+tabMetaData
 .setTool
@@ -1802,29 +1802,27 @@
    if (!
-messagableTab
+tabMetaData
 .hasErro
@@ -1986,51 +1986,45 @@
     
-MessagableTab messagableTab = messagableTab
+TabMetaData tabMetaData = tabMetaData
 s.ge
@@ -2041,29 +2041,27 @@
         
-messagableTab
+tabMetaData
 .put(sou
@@ -2087,29 +2087,27 @@
     if (
-messagableTab
+tabMetaData
 .hasErro
@@ -2305,37 +2305,35 @@
 xtAt(index, 
-messagableTab
+tabMetaData
 .getFirstErr
@@ -2474,37 +2474,35 @@
 onAt(index, 
-messagableTab
+tabMetaData
 .getIcon());
@@ -2548,21 +2548,19 @@
 ex, 
-messagableTab
+tabMetaData
 .get
@@ -2901,29 +2901,27 @@
 c class 
-MessagableTab
+TabMetaData
  %7B%0A%0A    

rename private inner class MessagableTab to TabMetaData to distinguish it from interface MessagableTab