/*
 * Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. All rights reserved.
 *
 * 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.jivesoftware.openfire.crowd;

import java.io.StringReader;
import java.util.concurrent.ConcurrentHashMap;

import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jivesoftware.openfire.crowd.jaxb.User;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.vcard.DefaultVCardProvider;
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * VCard provider for Crowd.
 * 
 * <p>The name, email will be provided by crowd. All other information
 * can be managed by the XMPP client
 */
public class CrowdVCardProvider extends DefaultVCardProvider {
    private static final Logger LOG = LoggerFactory.getLogger(CrowdVCardProvider.class);
    
    private static final String VCARD_TEMPLATE
        = "<vCard xmlns=\"vcard-temp\"><FN>@displayname@</FN><N><FAMILY>@lastname@</FAMILY><GIVEN>@firstname@</GIVEN></N><NICKNAME>@nickname@</NICKNAME><EMAIL><USERID>@email@</USERID></EMAIL></vCard>";
    private static final ConcurrentHashMap<String, Object> MUTEX = new ConcurrentHashMap<>();

    /**
     * @see org.jivesoftware.openfire.vcard.DefaultVCardProvider#loadVCard(java.lang.String)
     */
    @Override
    public Element loadVCard(String username) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("loadvcard:" + username);
        }
        
        if (MUTEX.containsKey(username)) {
            // preventing looping
            return null;
        }

        try {
            MUTEX.put(username, username);
            
            Element vcard = super.loadVCard(username);
            
            if (vcard == null) {
                CrowdUserProvider userProvider = (CrowdUserProvider) UserManager.getUserProvider();
                try {
                    User user = userProvider.getCrowdUser(username);
                    String str = VCARD_TEMPLATE.replace("@displayname@", user.displayName)
                            .replace("@lastname@", user.lastName)
                            .replace("@firstname@", user.firstName)
                            .replace("@email@", user.email)
                            .replace("@nickname@", username);
                    
                    SAXReader xmlReader = new SAXReader();
                    xmlReader.setEncoding("UTF-8");
                    
                    vcard = xmlReader.read(new StringReader(str)).getRootElement();
                    
                } catch (UserNotFoundException unfe) {
                    LOG.error("Unable to find user:" + String.valueOf(username) + " for loading its vcard", unfe);
                    return null;
                } catch (DocumentException de) {
                    LOG.error("vcard parsing error", de);
                    return null;
                }

                
                if (LOG.isDebugEnabled()) {
                    LOG.debug(vcard != null ? vcard.asXML() : "vcard is null");
                }
                
                
                // store this new vcard
                if (vcard != null) {
                    try {
                        createVCard(username, vcard);
                    } catch (AlreadyExistsException aee) {
                        LOG.error("Unable to create and store a new vcard for user:" + username + "; one already exists", aee);
                    }
                }
            }
            
            return vcard;

        } catch (RuntimeException re) {
            LOG.error("Failure occured when loading a vcard for user:" + username, re);
            throw re;
        } finally {
            MUTEX.remove(username);
        }
    }

    /**
     * @see org.jivesoftware.openfire.vcard.DefaultVCardProvider#createVCard(java.lang.String, org.dom4j.Element)
     */
    @Override
    public Element createVCard(String username, Element vCardElement) throws AlreadyExistsException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("createvcard:" + vCardElement.asXML());
        }
        return super.createVCard(username, vCardElement);
    }

    /**
     * @see org.jivesoftware.openfire.vcard.DefaultVCardProvider#updateVCard(java.lang.String, org.dom4j.Element)
     */
    @Override
    public Element updateVCard(String username, Element vCard) throws NotFoundException {
        // make sure some properties have not been overridden
        Element nickNameNode = vCard.element("NICKNAME");
        Element displayNameNode = vCard.element("FN");
        
        Element nameNode = vCard.element("N");
        Element lastNameNode = nameNode.element("FAMILY");
        Element firstnameNode = nameNode.element("GIVEN");
        
        Element emailNode = vCard.element("EMAIL").element("USERID");
        
        CrowdUserProvider userProvider = (CrowdUserProvider) UserManager.getUserProvider();
        try {
            User user = userProvider.getCrowdUser(username);
            
            nickNameNode.setText(username);
            displayNameNode.setText(user.displayName);
            lastNameNode.setText(user.lastName);
            firstnameNode.setText(user.firstName);
            emailNode.setText(user.email);
            
        } catch (UserNotFoundException unfe) {
            LOG.error("Unable to find user:" + String.valueOf(username) + " for updating its vcard", unfe);
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("updatevcard:" + vCard.asXML());
        }

        return super.updateVCard(username, vCard);
    }
    
}
