/*
 * Copyright 2006-2010 Daniel Henninger.  All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */

package net.sf.kraken.web;

import net.sf.kraken.KrakenPlugin;
import net.sf.kraken.registration.Registration;
import net.sf.kraken.registration.RegistrationManager;
import net.sf.kraken.type.TransportType;

import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.PluginManager;
import org.xmpp.packet.JID;
import org.apache.log4j.Logger;

import java.util.*;

/**
 * This provides the conduit through which all XMLRPC interface actions occur.
 *
 * @author Daniel Henninger
 */
public class XMLRPCConduit {

    static Logger Log = Logger.getLogger(XMLRPCConduit.class);

    ConfigManager configManager = new ConfigManager();

    public String authPassword = null;

    public XMLRPCConduit() {
        authPassword = JiveGlobals.getProperty("plugin.gateway.xmlrpc.password", null);
    }

    public void destroy() {
        authPassword = null;
    }

    private boolean verifyPassword(String password) {
        if (authPassword == null) {
            Log.error("XML-RPC authentication not set up on server.");
            return false;
        }
        if (!authPassword.equals(password)) {
            Log.warn("Unauthorized attempt on XML-RPC interface.");
            return false;
        }
        return true;
    }

    /**
     * Toggles whether a transport is enabled or disabled.
     *
     * @param password Auth password for making changes
     * @param transportName Name of the transport to be enabled or disabled (type of transport)
     * @return True or false if the transport is enabled after this call.
     */
    public boolean toggleTransport(String password, String transportName) {
        if (verifyPassword(password)) {
            return configManager.toggleTransport(transportName);
        }
        else {
            return false;
        }
    }

    /**
     * Adds a new registration
     *
     * @param password Auth password for making changes
     * @param user Username or full JID of user who is getting an account registered.
     * @param transportType Type of transport to add user to.
     * @param legacyUsername User's username on the legacy service.
     * @param legacyPassword User's password on the legacy service.
     * @param legacyNickname User's nickname on the legacy service.
     * @return Error message or "Success" on success.
     */
    public String addRegistration(String password, String user, String transportType, String legacyUsername, String legacyPassword, String legacyNickname) {
        if (!verifyPassword(password)) {
            return "Authorization failed!";
        }
        String results = configManager.addRegistration(user, transportType, legacyUsername, legacyPassword, legacyNickname);
        if (results == null) {
            return "Success";
        }
        else {
            return results;
        }
    }

    /**
     * Deletes a registration.
     *
     * @param password Auth password for making changes
     * @param user Username or full JID of user who is getting an account deleted.
     * @param transportType Type of transport to add user to.
     * @return Error message or "Success" on success.
     */
    public String deleteRegistration(String password, String user, String transportType) {
        if (!verifyPassword(password)) {
            return "Authorization failed!";
        }
        JID jid;
        if (user.contains("@")) {
            jid = new JID(user);
        }
        else {
            jid = new JID(user, XMPPServer.getInstance().getServerInfo().getXMPPDomain(), null);
        }
        Collection<Registration> registrations = RegistrationManager.getInstance().getRegistrations(jid, TransportType.valueOf(transportType));
        if (registrations.isEmpty()) {
            // User is not registered with us.
            return "Unable to find registration to delete.";
        }
        Registration registration = registrations.iterator().next();
        String results = configManager.deleteRegistration((int)registration.getRegistrationID());
        if (results == null) {
            return "Success";
        }
        else {
            return results;
        }
    }

    /**
     * Updates a registration.
     *
     * @param password Auth password for making changes
     * @param user Username or full JID of user who is getting an account updated.
     * @param transportType Type of transport to add user to.
     * @param legacyUsername User's username on the legacy service.
     * @param legacyPassword User's password on the legacy service.
     * @param legacyNickname User's nickname on the legacy service.
     * @return Error message or "Success" on success.
     */
    public String updateRegistration(String password, String user, String transportType, String legacyUsername, String legacyPassword, String legacyNickname) {
        if (!verifyPassword(password)) {
            return "Authorization failed!";
        }
        JID jid;
        if (user.contains("@")) {
            jid = new JID(user);
        }
        else {
            jid = new JID(user, XMPPServer.getInstance().getServerInfo().getXMPPDomain(), null);
        }
        Collection<Registration> registrations = RegistrationManager.getInstance().getRegistrations(jid, TransportType.valueOf(transportType));
        if (registrations.isEmpty()) {
            // User is not registered with us.
            return "Unable to find registration to update.";
        }
        Registration registration = registrations.iterator().next();
        String results = configManager.updateRegistration((int)registration.getRegistrationID(), legacyUsername, legacyPassword, legacyNickname);
        if (results == null) {
            return "Success";
        }
        else {
            return results;
        }
    }

    /**
     * Retrieve a list of all active/enabled transports.
     *
     * @param password Auth password for making changes
     * @return List of active transports.
     */
    public List<String> getActiveTransports(String password) {
        if (!verifyPassword(password)) {
            return Arrays.asList("Authorization failed!");
        }
        PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
        KrakenPlugin plugin = (KrakenPlugin)pluginManager.getPlugin("kraken");
        List<String> activeTransports = new ArrayList<String>();
        for (String transport : plugin.getTransports()) {
            if (plugin.serviceEnabled(transport)) {
                activeTransports.add(transport);
            }
        }
        return activeTransports;
    }

    /**
    * Lists  the currently registered transports for a given User.
    * @param password Auth password for making changes
    * @param user Username or full JID of user whose registrations should be listed
    * @return a Collection of RegistrationBeans. In XML-RPC, this will convert into an array of structs.
    */
    public Collection<RegistrationBean> getRegistrations(String password, String user) {
         /*
          * the redstone xml-rpc library can convert collections into xml-rpc arrays and
          * load all properties from a java bean into an xml-rpc struct
          */
         if (!verifyPassword(password)) {
             return Collections.emptyList();
         }
         JID jid; 
         if (user.contains("@")) {
             jid = new JID(user);
         }
         else {
             jid = new JID(user, XMPPServer.getInstance().getServerInfo().getXMPPDomain(), null);
         }
         Collection<Registration> registrations = RegistrationManager.getInstance().getRegistrations(jid);
         Collection<RegistrationBean> result = new LinkedList<RegistrationBean>();

         /*
          * copy the values of Registration objects
          * into RegistrationBean objects. Unfortunately the Registration bean is too complex to send
          * it directly.  There is also a bug in redstone xml-rpc, it assumes that there is a getter for
          * a property if there is a setter, too.
          */
         for (Registration r: registrations) {
                result.add(new RegistrationBean(r));
         }
         return result;
    }
    
    /**
     * Lists  the currently registered transports for all Users.
     * @param password Auth password for making changes
     * @return a Map of <user,Collection of RegistrationBeans>. In XML-RPC, this will convert into an array of structs.
     */
     public Map<String, Collection<RegistrationBean>> getAllRegistrations(String password) {
          /*
           * the redstone xml-rpc library can convert collections into xml-rpc arrays and
           * load all properties from a java bean into an xml-rpc struct
           */
          if (!verifyPassword(password)) {
              return null;
          }
          Collection<Registration> registrations = RegistrationManager.getInstance().getRegistrations();
          Map<String, Collection<RegistrationBean>> result = new HashMap<String, Collection<RegistrationBean>>();

          for(Registration reg : registrations) {
              Collection<RegistrationBean> coll = result.get(reg.getJID().getNode()); 
              if(coll == null) {
                  coll = new LinkedList<RegistrationBean>();
                  result.put(reg.getJID().getNode(), coll);
              }
              coll.add(new RegistrationBean(reg));
          }
          return result;
     }
}
