/*
 * Copyright (C) 2005-2008 Jive Software. 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.admin;

import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.jivesoftware.openfire.ldap.LdapManager;
import org.jivesoftware.openfire.ldap.LdapVCardProvider;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Iterator;

/**
 * Bean that stores the vcard mapping. It is also responsible for saving the mapping
 * as a system property and retrieving it.
 *
 * @author Gaston Dombiak
 */
public class LdapUserProfile {

    private static final Logger Log = LoggerFactory.getLogger(LdapUserProfile.class);

    private String name = "";
    private String email = "";
    private String fullName = "";
    private String nickname = "";
    private String birthday = "";
    private String photo = "";
    private String homeStreet = "";
    private String homeCity = "";
    private String homeState = "";
    private String homeZip = "";
    private String homeCountry = "";
    private String homePhone = "";
    private String homeMobile = "";
    private String homeFax = "";
    private String homePager = "";
    private String businessStreet = "";
    private String businessCity = "";
    private String businessState = "";
    private String businessZip = "";
    private String businessCountry = "";
    private String businessJobTitle = "";
    private String businessDepartment = "";
    private String businessPhone = "";
    private String businessMobile = "";
    private String businessFax = "";
    private String businessPager = "";
    private Boolean avatarStoredInDB = false;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getPhoto() {
        return photo;
    }

    public void setPhoto(String photo) {
        this.photo = photo;
    }

    public String getHomeStreet() {
        return homeStreet;
    }

    public void setHomeStreet(String homeStreet) {
        this.homeStreet = homeStreet;
    }

    public String getHomeCity() {
        return homeCity;
    }

    public void setHomeCity(String homeCity) {
        this.homeCity = homeCity;
    }

    public String getHomeState() {
        return homeState;
    }

    public void setHomeState(String homeState) {
        this.homeState = homeState;
    }

    public String getHomeZip() {
        return homeZip;
    }

    public void setHomeZip(String homeZip) {
        this.homeZip = homeZip;
    }

    public String getHomeCountry() {
        return homeCountry;
    }

    public void setHomeCountry(String homeCountry) {
        this.homeCountry = homeCountry;
    }

    public String getHomePhone() {
        return homePhone;
    }

    public void setHomePhone(String homePhone) {
        this.homePhone = homePhone;
    }

    public String getHomeMobile() {
        return homeMobile;
    }

    public void setHomeMobile(String homeMobile) {
        this.homeMobile = homeMobile;
    }

    public String getHomeFax() {
        return homeFax;
    }

    public void setHomeFax(String homeFax) {
        this.homeFax = homeFax;
    }

    public String getHomePager() {
        return homePager;
    }

    public void setHomePager(String homePager) {
        this.homePager = homePager;
    }

    public String getBusinessStreet() {
        return businessStreet;
    }

    public void setBusinessStreet(String businessStreet) {
        this.businessStreet = businessStreet;
    }

    public String getBusinessCity() {
        return businessCity;
    }

    public void setBusinessCity(String businessCity) {
        this.businessCity = businessCity;
    }

    public String getBusinessState() {
        return businessState;
    }

    public void setBusinessState(String businessState) {
        this.businessState = businessState;
    }

    public String getBusinessZip() {
        return businessZip;
    }

    public void setBusinessZip(String businessZip) {
        this.businessZip = businessZip;
    }

    public String getBusinessCountry() {
        return businessCountry;
    }

    public void setBusinessCountry(String businessCountry) {
        this.businessCountry = businessCountry;
    }

    public String getBusinessJobTitle() {
        return businessJobTitle;
    }

    public void setBusinessJobTitle(String businessJobTitle) {
        this.businessJobTitle = businessJobTitle;
    }

    public String getBusinessDepartment() {
        return businessDepartment;
    }

    public void setBusinessDepartment(String businessDepartment) {
        this.businessDepartment = businessDepartment;
    }

    public String getBusinessPhone() {
        return businessPhone;
    }

    public void setBusinessPhone(String businessPhone) {
        this.businessPhone = businessPhone;
    }

    public String getBusinessMobile() {
        return businessMobile;
    }

    public void setBusinessMobile(String businessMobile) {
        this.businessMobile = businessMobile;
    }

    public String getBusinessFax() {
        return businessFax;
    }

    public void setBusinessFax(String businessFax) {
        this.businessFax = businessFax;
    }

    public String getBusinessPager() {
        return businessPager;
    }

    public void setBusinessPager(String businessPager) {
        this.businessPager = businessPager;
    }

    public Boolean getAvatarStoredInDB() {
        return avatarStoredInDB;
    }

    public void setAvatarStoredInDB(Boolean avatarStoredInDB) {
        if (avatarStoredInDB == null) {
            this.avatarStoredInDB = false;
        }
        else {
            this.avatarStoredInDB = avatarStoredInDB;
        }
    }

    /**
     * Sets default mapping values when using an Active Directory server.
     */
    public void initForActiveDirectory() {
        name = "{cn}";
        email = "{mail}";
        fullName = "{displayName}";
        nickname = "";
        birthday = "";
        photo = "{jpegPhoto}";
        homeStreet = "{homePostalAddress}";
        homeCity = "";
        homeState = "";
        homeZip = "{homeZip}";
        homeCountry = "{co}";
        homePhone = "{homePhone}";
        homeMobile = "{mobile}";
        homeFax = "";
        homePager = "";
        businessStreet = "{streetAddress}";
        businessCity = "{l}";
        businessState = "{st}";
        businessZip = "{postalCode}";
        businessCountry = "{co}";
        businessJobTitle = "{title}";
        businessDepartment = "{department}";
        businessPhone = "{telephoneNumber}";
        businessMobile = "{mobile}";
        businessFax = "{facsimileTelephoneNumber}";
        businessPager = "{pager}";
        avatarStoredInDB = false;
    }

    /**
     * Sets default mapping values when using an Active Directory server.
     */
    public void initForOpenLDAP() {
        name = "{cn}";
        email = "{mail}";
        fullName = "{displayName}";
        nickname = "{uid}";
        birthday = "";
        photo = "{jpegPhoto}";
        homeStreet = "{homePostalAddress}";
        homeCity = "";
        homeState = "";
        homeZip = "";
        homeCountry = "";
        homePhone = "{homePhone}";
        homeMobile = "";
        homeFax = "";
        homePager = "";
        businessStreet = "{postalAddress}";
        businessCity = "{l}";
        businessState = "{st}";
        businessZip = "{postalCode}";
        businessCountry = "";
        businessJobTitle = "{title}";
        businessDepartment = "{departmentNumber}";
        businessPhone = "{telephoneNumber}";
        businessMobile = "{mobile}";
        businessFax = "";
        businessPager = "{pager}";
        avatarStoredInDB = false;
    }

    /**
     * Saves current configuration as XML/DB properties.
     */
    public void saveProperties() {
        Element vCard = DocumentHelper.createElement(QName.get("vCard", "vcard-temp"));
        Element subelement;

        // Add name
        if (name != null && name.trim().length() > 0) {
            subelement = vCard.addElement("N");
            subelement.addElement("GIVEN").setText(name.trim());
        }
        // Add email
        if (email != null && email.trim().length() > 0) {
            subelement = vCard.addElement("EMAIL");
            subelement.addElement("INTERNET");
            subelement.addElement("USERID").setText(email.trim());
        }
        // Add Full Name
        vCard.addElement("FN").setText(fullName.trim());
        // Add nickname
        if (nickname != null && nickname.trim().length() > 0) {
            vCard.addElement("NICKNAME").setText(nickname.trim());
        }
        // Add birthday
        if (birthday != null && birthday.trim().length() > 0) {
            vCard.addElement("BDAY").setText(birthday.trim());
        }
        // Add photo/avatar
        if (photo != null && photo.trim().length() > 0) {
            Element element = vCard.addElement("PHOTO");
            element.addElement("TYPE").setText("image/jpeg");
            element.addElement("BINVAL").setText(photo.trim());
        }
        // Add home address
        subelement = vCard.addElement("ADR");
        subelement.addElement("HOME");
        if (homeStreet != null && homeStreet.trim().length() > 0) {
            subelement.addElement("STREET").setText(homeStreet.trim());
        }
        if (homeCity != null && homeCity.trim().length() > 0) {
            subelement.addElement("LOCALITY").setText(homeCity.trim());
        }
        if (homeState != null && homeState.trim().length() > 0) {
            subelement.addElement("REGION").setText(homeState.trim());
        }
        if (homeZip != null && homeZip.trim().length() > 0) {
            subelement.addElement("PCODE").setText(homeZip.trim());
        }
        if (homeCountry != null && homeCountry.trim().length() > 0) {
            subelement.addElement("CTRY").setText(homeCountry.trim());
        }
        // Add business address
        subelement = vCard.addElement("ADR");
        subelement.addElement("WORK");
        if (businessStreet != null && businessStreet.trim().length() > 0) {
            subelement.addElement("STREET").setText(businessStreet.trim());
        }
        if (businessCity != null && businessCity.trim().length() > 0) {
            subelement.addElement("LOCALITY").setText(businessCity.trim());
        }
        if (businessState != null && businessState.trim().length() > 0) {
            subelement.addElement("REGION").setText(businessState.trim());
        }
        if (businessZip != null && businessZip.trim().length() > 0) {
            subelement.addElement("PCODE").setText(businessZip.trim());
        }
        if (businessCountry != null && businessCountry.trim().length() > 0) {
            subelement.addElement("CTRY").setText(businessCountry.trim());
        }
        // Add home phone
        if (homePhone != null && homePhone.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("HOME");
            subelement.addElement("VOICE");
            subelement.addElement("NUMBER").setText(homePhone.trim());
        }
        // Add home mobile
        if (homeMobile != null && homeMobile.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("HOME");
            subelement.addElement("CELL");
            subelement.addElement("NUMBER").setText(homeMobile.trim());
        }
        // Add home fax
        if (homeFax != null && homeFax.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("HOME");
            subelement.addElement("FAX");
            subelement.addElement("NUMBER").setText(homeFax.trim());
        }
        // Add home pager
        if (homePager != null && homePager.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("HOME");
            subelement.addElement("PAGER");
            subelement.addElement("NUMBER").setText(homePager.trim());
        }
        // Add business phone
        if (businessPhone != null && businessPhone.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("WORK");
            subelement.addElement("VOICE");
            subelement.addElement("NUMBER").setText(businessPhone.trim());
        }
        // Add business mobile
        if (businessMobile != null && businessMobile.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("WORK");
            subelement.addElement("CELL");
            subelement.addElement("NUMBER").setText(businessMobile.trim());
        }
        // Add business fax
        if (businessFax != null && businessFax.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("WORK");
            subelement.addElement("FAX");
            subelement.addElement("NUMBER").setText(businessFax.trim());
        }
        // Add business pager
        if (businessPager != null && businessPager.trim().length() > 0) {
            subelement = vCard.addElement("TEL");
            subelement.addElement("WORK");
            subelement.addElement("PAGER");
            subelement.addElement("NUMBER").setText(businessPager.trim());
        }
        // Add job title
        if (businessJobTitle != null && businessJobTitle.trim().length() > 0) {
            vCard.addElement("TITLE").setText(businessJobTitle.trim());
        }
        // Add job department
        if (businessDepartment != null && businessDepartment.trim().length() > 0) {
            vCard.addElement("ORG").addElement("ORGUNIT").setText(businessDepartment.trim());
        }
        // Generate content to store in property
        String vcardXML;
        StringWriter writer = new StringWriter();
        OutputFormat prettyPrinter = OutputFormat.createPrettyPrint();
        XMLWriter xmlWriter = new XMLWriter(writer, prettyPrinter);
        try {
            xmlWriter.write(vCard);
            vcardXML = writer.toString();
        }
        catch (IOException e) {
            Log.error("Error pretty formating XML", e);
            vcardXML = vCard.asXML();
        }

        StringBuilder sb = new StringBuilder(vcardXML.length());
        sb.append("<![CDATA[").append(vcardXML).append("]]>");
        // Save mapping as an XML property
        JiveGlobals.setProperty("ldap.vcard-mapping", sb.toString());

        // Set that the vcard provider is LdapVCardProvider
        JiveGlobals.setProperty("provider.vcard.className", LdapVCardProvider.class.getName());

        // Save duplicated fields in LdapManager (should be removed in the future)
        LdapManager.getInstance().setNameField(name.replaceAll("(\\{)([\\d\\D&&[^}]]+)(})", "$2"));
        LdapManager.getInstance().setEmailField(email.replaceAll("(\\{)([\\d\\D&&[^}]]+)(})", "$2"));

        // Store the DB storage variable in the actual database.
        JiveGlobals.setProperty("ldap.override.avatar", avatarStoredInDB.toString());
    }

    /**
     * Returns true if the vCard mappings where successfully loaded from the XML/DB
     * properties.
     *
     * @return true if mappings where loaded from saved property.
     */
    public boolean loadFromProperties() {
        String xmlProperty = JiveGlobals.getProperty("ldap.vcard-mapping");
        if (xmlProperty == null || xmlProperty.trim().length() == 0) {
            return false;
        }

        try {
            // Remove CDATA wrapping element
            if (xmlProperty.startsWith("<![CDATA[")) {
                xmlProperty = xmlProperty.substring(9, xmlProperty.length()-3);
            }
            // Parse XML
            Document document = DocumentHelper.parseText(xmlProperty);
            Element vCard = document.getRootElement();

            Element element = vCard.element("N");
            if (element != null) {
                name = element.elementTextTrim("GIVEN");
            }
            element = vCard.element("EMAIL");
            if (element != null) {
                email = element.elementTextTrim("USERID");
            }
            element = vCard.element("FN");
            if (element != null) {
                fullName = element.getTextTrim();
            }
            element = vCard.element("NICKNAME");
            if (element != null) {
                nickname = element.getTextTrim();
            }
            element = vCard.element("BDAY");
            if (element != null) {
                birthday = element.getTextTrim();
            }
            // Parse addresses
            Iterator addresses = vCard.elementIterator("ADR");
            while (addresses.hasNext()) {
                element = (Element) addresses.next();
                if (element.element("HOME") != null) {
                    if (element.element("STREET") != null) {
                        homeStreet = element.elementTextTrim("STREET");
                    }
                    if (element.element("LOCALITY") != null) {
                        homeCity = element.elementTextTrim("LOCALITY");
                    }
                    if (element.element("REGION") != null) {
                        homeState = element.elementTextTrim("REGION");
                    }
                    if (element.element("PCODE") != null) {
                        homeZip = element.elementTextTrim("PCODE");
                    }
                    if (element.element("CTRY") != null) {
                        homeCountry = element.elementTextTrim("CTRY");
                    }
                }
                else if (element.element("WORK") != null) {
                    if (element.element("STREET") != null) {
                        businessStreet = element.elementTextTrim("STREET");
                    }
                    if (element.element("LOCALITY") != null) {
                        businessCity = element.elementTextTrim("LOCALITY");
                    }
                    if (element.element("REGION") != null) {
                        businessState = element.elementTextTrim("REGION");
                    }
                    if (element.element("PCODE") != null) {
                        businessZip = element.elementTextTrim("PCODE");
                    }
                    if (element.element("CTRY") != null) {
                        businessCountry = element.elementTextTrim("CTRY");
                    }
                }
            }
            // Parse telephones
            Iterator telephones = vCard.elementIterator("TEL");
            while (telephones.hasNext()) {
                element = (Element) telephones.next();
                if (element.element("HOME") != null) {
                    if (element.element("VOICE") != null) {
                        homePhone = element.elementTextTrim("NUMBER");
                    }
                    else if (element.element("CELL") != null) {
                        homeMobile = element.elementTextTrim("NUMBER");
                    }
                    else if (element.element("FAX") != null) {
                        homeFax = element.elementTextTrim("NUMBER");
                    }
                    else if (element.element("PAGER") != null) {
                        homePager = element.elementTextTrim("NUMBER");
                    }
                }
                else if (element.element("WORK") != null) {
                    if (element.element("VOICE") != null) {
                        businessPhone = element.elementTextTrim("NUMBER");
                    }
                    else if (element.element("CELL") != null) {
                        businessMobile = element.elementTextTrim("NUMBER");
                    }
                    else if (element.element("FAX") != null) {
                        businessFax = element.elementTextTrim("NUMBER");
                    }
                    else if (element.element("PAGER") != null) {
                        businessPager = element.elementTextTrim("NUMBER");
                    }
                }
            }
            element = vCard.element("TITLE");
            if (element != null) {
                businessJobTitle = element.getTextTrim();
            }
            element = vCard.element("ORG");
            if (element != null) {
                if (element.element("ORGUNIT") != null) {
                    businessDepartment = element.elementTextTrim("ORGUNIT");
                }
            }
            avatarStoredInDB = JiveGlobals.getBooleanProperty("ldap.override.avatar", false);
        }
        catch (DocumentException e) {
            Log.error("Error loading vcard mappings from property", e);
            return false;
        }

        return true;
    }
}
