package org.jboss.seam.theme;

import static org.jboss.seam.annotations.Install.BUILT_IN;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Events;
import org.jboss.seam.faces.Selector;
import org.jboss.seam.core.Locale;
import org.jboss.seam.international.Messages;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.navigation.Pages;
import org.jboss.seam.util.IteratorEnumeration;

/**
 * Selects the current user's theme
 * 
 * @author Gavin King
 */
@Scope(ScopeType.SESSION)
@Name("org.jboss.seam.theme.themeSelector")
@BypassInterceptors
@Install(precedence=BUILT_IN, classDependencies="javax.faces.context.FacesContext")
public class ThemeSelector extends Selector
{
   private static final long serialVersionUID = 3920407140011388341L;

   private static final LogProvider log = Logging.getLogProvider(ThemeSelector.class);
   
   private String theme;
   private String[] availableThemes;
   
   @Create
   public void initDefaultTheme()
   {
      String themeName = getCookieValueIfEnabled();
      if ( themeName!=null && Arrays.asList(availableThemes).contains(themeName) ) 
      {
         setTheme(themeName);
      }
      
      if (theme==null)
      {
         if (availableThemes==null || availableThemes.length==0)
         {
            throw new IllegalStateException("no themes defined");
         }
         theme = availableThemes[0];
      }
   }
   
   @Override
   protected String getCookieName()
   {
      return "org.jboss.seam.core.Theme";
   }
   
   /**
    * Recreate the JSF view, using the new theme, and raise the 
    * org.jboss.seam.themeSelected event
    *
    */
   public void select()
   {
      Contexts.removeFromAllContexts("org.jboss.seam.theme.theme");
      FacesContext facesContext = FacesContext.getCurrentInstance();
      String viewId = Pages.getViewId(facesContext);
      UIViewRoot viewRoot = facesContext.getApplication().getViewHandler().createView(facesContext, viewId);
      facesContext.setViewRoot(viewRoot);
      
      setCookieValueIfEnabled( getTheme() );

      if ( Events.exists() ) 
      {
          Events.instance().raiseEvent( "org.jboss.seam.themeSelected", getTheme() );
      }
   }

   public void select(ValueChangeEvent event) 
   {
      selectTheme( (String) event.getNewValue() );
   }
   
   public void selectTheme(String themeName)
   {
      setTheme(themeName);
      select();
   }
   
   /**
    * Get a selectable list of available themes for display in the UI
    */
   public List<SelectItem> getThemes()
   {
      List<SelectItem> selectItems = new ArrayList<SelectItem>(availableThemes.length);
      for ( String name: availableThemes )
      {
         selectItems.add( new SelectItem( name, getLocalizedThemeName(name) ) );
      }
      return selectItems;
   }

   /**
    * Get the name of the current theme
    */
   public String getTheme()
   {
      return theme;
   }

   public void setTheme(String themeName)
   {
      setDirty(this.theme, themeName);
      this.theme = themeName;
   }

   public void setAvailableThemes(String[] themeNames)
   {
      setDirty(this.availableThemes, themeNames);
      this.availableThemes = themeNames;
   }
   
   /**
    * Get the resource bundle for the theme
    */
   public ResourceBundle getThemeResourceBundle()
   {
      try
      {
         java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle( 
               theme, 
               Locale.instance(), 
               Thread.currentThread().getContextClassLoader() 
            );
         log.trace("loaded resource bundle: " + theme);
         return bundle;
      }
      catch (MissingResourceException mre)
      {
         log.debug("resource bundle missing: " + theme);
         return new ResourceBundle()
         {
            @Override
            public Enumeration<String> getKeys()
            {
               return new IteratorEnumeration( Collections.EMPTY_LIST.iterator() );
            }
            @Override
            protected Object handleGetObject(String key)
            {
               return null;
            }
         };
      }
   }

   /**
    * Get the localized name of the named theme, by looking for
    * org.jboss.seam.theme.&lt;name&gt; in the Seam resource
    * bundle
    */
   public String getLocalizedThemeName(String name) 
   {
       String key = "org.jboss.seam.theme." + name;
       String localizedName = Messages.instance().get(key);
       return key.equals(localizedName) ? name : localizedName;
   }
   
   public static ThemeSelector instance()
   {
      if ( !Contexts.isSessionContextActive() )
      {
         throw new IllegalStateException("No active session context");
      }
      return (ThemeSelector) Component.getInstance(ThemeSelector.class, ScopeType.SESSION);
   }

   public String[] getAvailableThemes()
   {
      return availableThemes;
   }
   
}
