/**
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.exoplatform.webui.organization;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.exoplatform.commons.serialization.api.annotations.Serialized;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.portal.Constants;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.UserProfile;
import org.exoplatform.services.organization.UserProfileHandler;
import org.exoplatform.services.resources.LocaleConfig;
import org.exoplatform.services.resources.LocaleConfigService;
import org.exoplatform.services.resources.ResourceBundleService;
import org.exoplatform.web.application.ApplicationMessage;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.application.portlet.PortletRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.core.UIApplication;
import org.exoplatform.webui.core.UIComponent;
import org.exoplatform.webui.core.model.SelectItemOption;
import org.exoplatform.webui.form.UIFormInput;
import org.exoplatform.webui.form.UIFormInputSet;
import org.exoplatform.webui.form.UIFormSelectBox;
import org.exoplatform.webui.form.UIFormStringInput;
import org.exoplatform.webui.form.validator.StringLengthValidator;
import org.gatein.security.oauth.exception.OAuthException;
import org.gatein.security.oauth.exception.OAuthExceptionCode;
import org.gatein.security.oauth.common.OAuthConstants;
import org.gatein.security.oauth.spi.OAuthProviderType;
import org.gatein.security.oauth.spi.OAuthProviderTypeRegistry;
import org.exoplatform.webui.form.validator.UserConfigurableValidator;

/**
 * Created by The eXo Platform SARL Author : Dang Van Minh minhdv81@yahoo.com Jun 28, 2006
 */
@ComponentConfig(template = "system:/groovy/webui/form/UIVTabInputSet.gtmpl")
@Serialized
public class UIUserProfileInputSet extends UIFormInputSet {

    public static final String MALE = "male";

    public static final String FEMALE = "female";

    public static final int DEFAULT_MAX_LENGTH = 255;
    private int maxLength = -1;

    public static final String JOB_TITLE = "jobtitle";

    public UIUserProfileInputSet() {
    }

    public UIUserProfileInputSet(String name) throws Exception {
        super(name);
        setComponentConfig(UIUserProfileInputSet.class, null);

        UIFormInputSet personalInputSet = new UIFormInputSet("Profile");
        addInput(personalInputSet, UserProfile.PERSONAL_INFO_KEYS);
        addUIFormInput(personalInputSet);

        UIFormInputSet homeInputSet = new UIFormInputSet("HomeInfo");
        addInput(homeInputSet, UserProfile.HOME_INFO_KEYS);
        homeInputSet.setRendered(false);
        addUIFormInput(homeInputSet);

        UIFormInputSet businessInputSet = new UIFormInputSet("BusinessInfo");
        addInput(businessInputSet, UserProfile.BUSINESE_INFO_KEYS);
        businessInputSet.setRendered(false);
        addUIFormInput(businessInputSet);

        OAuthProviderTypeRegistry registry = getApplicationComponent(OAuthProviderTypeRegistry.class);
        if (registry.isOAuthEnabled()) {
            UIFormInputSet socialInputSet = new UIFormInputSet("SocialNetworksInfo");
            addInput(socialInputSet, getSocialInfoKeys());
            socialInputSet.setRendered(false);
            addUIFormInput(socialInputSet);
        }
    }

    public void reset() {
        for (UIComponent uiChild : getChildren()) {
            if (uiChild instanceof UIFormInputSet || uiChild instanceof UIFormInput) {
                ((UIFormInputSet) uiChild).reset();
            }
        }
    }

    private void addInput(UIFormInputSet set, String[] keys) throws Exception {
        int maxLength = getMaxLengthOfTextField();

        for (String key : keys) {
            if (key.equalsIgnoreCase("user.gender")) {
                List<SelectItemOption<String>> ls = new ArrayList<SelectItemOption<String>>();
                ls.add(new SelectItemOption<String>(MALE, MALE));
                ls.add(new SelectItemOption<String>(FEMALE, FEMALE));
                UIFormSelectBox genderSelectBox = new UIFormSelectBox(key, key, ls);
                set.addUIFormInput(genderSelectBox);
                continue;
            } else if (key.equalsIgnoreCase("user.jobtitle")) {
                set.addUIFormInput(new UIFormStringInput(key, null, null).addValidator(UserConfigurableValidator.class,
                        JOB_TITLE, UserConfigurableValidator.KEY_PREFIX + JOB_TITLE, false));
                continue;
            } else if (key.equalsIgnoreCase(Constants.USER_LANGUAGE)) {
                UIFormSelectBox langSelectBox = new UIFormSelectBox(key, key, null);
                set.addUIFormInput(langSelectBox);
                initLanguageCombo(langSelectBox);
                continue;
            }

            try {
                set.addUIFormInput(new UIFormStringInput(key, null, null).addValidator(StringLengthValidator.class, 0, maxLength));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Update language select box
     */
    @Override
    public void processRender(WebuiRequestContext context) throws Exception {
        UIFormSelectBox langSelectBox = this.findComponentById(Constants.USER_LANGUAGE);
        initLanguageCombo(langSelectBox);
        super.processRender(context);
    }

    private void initLanguageCombo(UIFormSelectBox langSelectBox) {
        if (langSelectBox == null)
            return;
        String selectedLang = langSelectBox.getSelectedValues()[0];

        List<SelectItemOption<String>> lang = new ArrayList<SelectItemOption<String>>();
        langSelectBox.setOptions(lang); // Clear

        LocaleConfigService localeService = getApplicationComponent(LocaleConfigService.class);
        Locale currentLocale = ((PortletRequestContext) WebuiRequestContext.getCurrentInstance()).getLocale();
        Iterator<LocaleConfig> i = localeService.getLocalConfigs().iterator();
        String displayName = null;
        String language = null;
        String country = null;
        SelectItemOption<String> option;
        while (i.hasNext()) {
            LocaleConfig config = i.next();
            Locale locale = config.getLocale();

            language = locale.getLanguage();
            country = locale.getCountry();
            if (country != null && country.length() > 0) {
                language = language + "_" + country;
            }

            ResourceBundle localeResourceBundle;

            displayName = null;
            try {
                localeResourceBundle = getResourceBundle(currentLocale);
                String key = "Locale." + language;
                String translation = localeResourceBundle.getString(key);
                displayName = translation;
            } catch (MissingResourceException e) {
                displayName = capitalizeFirstLetter(locale.getDisplayName(currentLocale));
            } catch (Exception e) {

            }

            option = new SelectItemOption<String>(displayName, language);
            if (language.equals(selectedLang)) {
                option.setSelected(true);
            }
            lang.add(option);
        }

        // Set default language for new user is empty
        lang.add(new SelectItemOption<String>("", ""));

        Collections.sort(lang, new LanguagesComparator());

        langSelectBox.setOptions(lang);
    }

    private String[] getSocialInfoKeys() {
        List<String> result = new ArrayList<String>();
        OAuthProviderTypeRegistry registry = getApplicationComponent(OAuthProviderTypeRegistry.class);
        for (OAuthProviderType oauthProvType : registry.getEnabledOAuthProviders()) {
            String oauthUsernameAttrName = oauthProvType.getUserNameAttrName();
            result.add(oauthUsernameAttrName);
        }
        return result.toArray(new String[] {});
    }

    @SuppressWarnings("deprecation")
    public void setUserProfile(String user) throws Exception {
        if (user == null)
            return;
        OrganizationService service = getApplicationComponent(OrganizationService.class);
        UserProfile userProfile = service.getUserProfileHandler().findUserProfileByName(user);
        if (userProfile == null) {
            userProfile = service.getUserProfileHandler().createUserProfileInstance();
            userProfile.setUserName(user);
        }

        if (userProfile.getUserInfoMap() == null)
            return;
        for (UIComponent set : getChildren()) {
            UIFormInputSet inputSet = (UIFormInputSet) set;
            for (UIComponent uiComp : inputSet.getChildren()) {
                UIFormStringInput uiInput = (UIFormStringInput) uiComp;
                uiInput.setValue(userProfile.getAttribute(uiInput.getName()));
            }
        }
    }

    @SuppressWarnings("deprecation")
    public boolean save(OrganizationService service, String user, boolean isnewUser) throws Exception {
        UserProfileHandler hanlder = service.getUserProfileHandler();
        UserProfile userProfile = hanlder.findUserProfileByName(user);

        if (userProfile == null) {
            userProfile = hanlder.createUserProfileInstance();
            userProfile.setUserName(user);
        }

        for (UIComponent set : getChildren()) {
            UIFormInputSet inputSet = (UIFormInputSet) set;
            for (UIComponent uiComp : inputSet.getChildren()) {
                UIFormStringInput uiInput = (UIFormStringInput) uiComp;
                // if(uiInput.getValue() == null || uiInput.getValue().length() < 1)
                // continue;
                userProfile.getUserInfoMap().put(uiInput.getName(), uiInput.getValue());
            }
        }

        WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
        UIApplication uiApp = context.getUIApplication();

        try {
            hanlder.saveUserProfile(userProfile, true);
        } catch (OAuthException gtnOauthOAuthException) {
            // Show warning message if user with this facebookUsername (or googleUsername) already exists
            if (gtnOauthOAuthException.getExceptionCode() == OAuthExceptionCode.DUPLICATE_OAUTH_PROVIDER_USERNAME) {
                addOAuthExceptionMessage(context, gtnOauthOAuthException, uiApp);
                return false;
            } else {
                throw gtnOauthOAuthException;
            }
        }

        Object[] args = { "UserProfile", user };
        if (isnewUser) {
            uiApp.addMessage(new ApplicationMessage("UIAccountInputSet.msg.successful.create.user", args));
            return true;
        }
        uiApp.addMessage(new ApplicationMessage("UIUserProfileInputSet.msg.sucsesful.update.userprofile", args));
        return true;
    }

    private String capitalizeFirstLetter(String word) {
        if (word == null) {
            return null;
        }
        if (word.length() == 0) {
            return word;
        }
        StringBuilder result = new StringBuilder(word);
        result.replace(0, 1, result.substring(0, 1).toUpperCase());
        return result.toString();
    }

    private ResourceBundle getResourceBundle(Locale locale) {
        ExoContainer appContainer = ExoContainerContext.getCurrentContainer();
        ResourceBundleService service = (ResourceBundleService) appContainer
                .getComponentInstanceOfType(ResourceBundleService.class);
        ResourceBundle res = service.getResourceBundle("locale.portal.webui", locale);
        return res;
    }

    private class LanguagesComparator implements Comparator<SelectItemOption> {
        public int compare(SelectItemOption item0, SelectItemOption item1) {
            return item0.getLabel().compareToIgnoreCase(item1.getLabel());
        }
    }

    private void addOAuthExceptionMessage(WebuiRequestContext context, OAuthException gtnOauthOAuthException, UIApplication uiApp) {
        Object[] args = convertOAuthExceptionAttributes(context, "UIAccountSocial.label.", gtnOauthOAuthException.getExceptionAttributes());
        ApplicationMessage appMessage = new ApplicationMessage("UIUserProfileInputSet.msg.oauth-username-exists", args, ApplicationMessage.WARNING);
        appMessage.setArgsLocalized(false);
        uiApp.addMessage(appMessage);
    }

    private Object[] convertOAuthExceptionAttributes(WebuiRequestContext context, String messageKeyPrefix, Map<String, Object> exceptionAttribs) {
        String oauthProviderUsernameAttrName = (String)exceptionAttribs.get(OAuthConstants.EXCEPTION_OAUTH_PROVIDER_USERNAME_ATTRIBUTE_NAME);
        ResourceBundle resBundle = context.getApplicationResourceBundle();
        String localizedOAuthProviderUsernameAttrName;
        try {
            localizedOAuthProviderUsernameAttrName = resBundle.getString(messageKeyPrefix + oauthProviderUsernameAttrName);
        } catch (MissingResourceException mre) {
            localizedOAuthProviderUsernameAttrName = oauthProviderUsernameAttrName;
        }

        Object oauthProviderUsername = exceptionAttribs.get(OAuthConstants.EXCEPTION_OAUTH_PROVIDER_USERNAME);

        return new Object[] { localizedOAuthProviderUsernameAttrName, oauthProviderUsername };
    }

    private int getMaxLengthOfTextField() {
        if (maxLength == -1) {
            String property = System.getProperty("gatein.validators.profile.maxlength");
            maxLength = property!=null ? Integer.parseInt(property) : DEFAULT_MAX_LENGTH;
        }
        return maxLength;
    }
}
