package org.mmbase.bridge.util;

import org.mmbase.bridge.Cloud;
import org.mmbase.bridge.CloudContext;
import org.mmbase.bridge.ContextProvider;
import org.mmbase.bridge.Node;
import org.mmbase.bridge.NotFoundException;
import org.mmbase.module.core.MMBaseContext;
import org.mmbase.util.logging.Logger;
import org.mmbase.util.logging.Logging;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Created by Finalist IT Group
 * Date: Mar 27, 2003 Time: 6:40:16 PM
 *
 * @author Vincent
 */
public class CloudFactory {

   /** MMbase logging system */
   private static Logger log = Logging.getLoggerInstance(CloudFactory.class.getName());

   public static final String DEFAULT_SESSIONNAME = "cloud_mmbase";
   public static final String DEFAULT_CLOUD_NAME = "adminCloud";
   public static final String NP_AUTHENTICATION = "name/password";
   public static final String CLASS_AUTHENTICATION = "class";
   
   /** Constant for SECURITY_NODE */
   public static final int SECURITY_NODE = 0;
   /** Constant for SECURITY_PROPS */
   public static final int SECURITY_PROPS = 1;
   /** Constant for SECURITY_CLASS */
   public static final int SECURITY_CLASS = 2;

   private static Cloud adminCloud;

   /**
    * Returns the admin instance of the Cloud
    * @return The admin Cloud
    */
   public static Cloud getCloudNode() {
       return getCloud(SECURITY_NODE);
   }

   /**
    * Returns the admin instance of the Cloud
    * @return The admin Cloud
    */
   public static Cloud getCloudProps() {
       return getCloud(SECURITY_PROPS);
   }

   /**
    * Returns the admin instance of the Cloud
    * @return The admin Cloud
    */
   public static Cloud getCloudClass() {
       return getCloud(SECURITY_CLASS);
   }
   
   /**
    * Returns the admin instance of the Cloud
    * @param securityType Type to get an user
    * @return The admin Cloud
    */
   public static Cloud getCloud(int securityType) {
      if (adminCloud == null) {
         if (securityType == SECURITY_CLASS) {
            adminCloud = ContextProvider.getDefaultCloudContext().getCloud(DEFAULT_CLOUD_NAME, CLASS_AUTHENTICATION, null);
         }
         else {
            log.info("Cloud not yet initialized. Doing now!");
            CloudContext context = ContextProvider.getDefaultCloudContext();
            Map loginInfo = getAdminUserCredentials(securityType);
            adminCloud = context.getCloud(DEFAULT_CLOUD_NAME, NP_AUTHENTICATION, loginInfo);
         }
      }
      if (adminCloud != null) {
         log.debug("CloudFactory cloud" + adminCloud.getUser().getIdentifier());
      } else {
         log.debug("CloudFactory cloud = null");
      }
      return adminCloud;
   }

   /**
    * @param securityType Type to get an user
    * @return Admin credentials
    * @throws org.mmbase.bridge.NotFoundException when failed to get User
    */
   public static Map getAdminUserCredentials(int securityType) {
      String username = null;
      String password = null;
      switch (securityType) {
          case SECURITY_NODE: {
              // Retrieve username/password using node alias for admin users node.
              CloudContext context = ContextProvider.getCloudContext("local");
              Cloud cloud = context.getCloud(DEFAULT_CLOUD_NAME);
              // will throw NotFoundException
              Node node = cloud.getNode("users.admin");
              username = node.getStringValue("account");
              password = node.getStringValue("password");
          }
          case SECURITY_PROPS: {
              File adminFile = new File(MMBaseContext.getConfigPath() + File.separator + "security" + File.separator + "admins.properties");
              try {
                  Properties propAdmins = new Properties();
                  if (adminFile.canRead()) {
                      propAdmins.load(new FileInputStream(adminFile));
                      if (!propAdmins.isEmpty()) {
                          username = (String) propAdmins.keySet().iterator().next();
                          password = propAdmins.getProperty(username);
                      }
                      else {
                          throw new NotFoundException("No admins found in (" + adminFile + ")");
                      }
                  } else {
                      throw new NotFoundException("No admins (" + adminFile + " can not be read)");
                  }
              } catch (IOException e) {
                  throw new NotFoundException("IO (" + adminFile + ")" + e.getMessage());
              }
          }
      }
      return getUserCredentials(username, password);
   }

   public static Map getUserCredentials(String username, String password) {
      Map result = new HashMap(3, 0.7f);
      result.put("username", username);
      result.put("password", password);
      return result;
   }

   /**
    * @return the Anonymous cloud
    */
   public static Cloud getAnonymousCloud() {
      CloudContext cloudContext = ContextProvider.getDefaultCloudContext();
      return cloudContext.getCloud(DEFAULT_CLOUD_NAME);
   }
   
   /**
    * get the Cloud from session
    * @param request httprequest Returns the instance of the Cloud
    * @return The Cloud
    */
   public static Cloud getCloud(HttpServletRequest request) {
      return getCloud(request, DEFAULT_SESSIONNAME);
   }

   /**
    * get the Cloud from session
    * @param sessionname name where the cloud is stored under
    * @param request httprequest Returns the instance of the Cloud
    * @return The Cloud
    */
   public static Cloud getCloud(HttpServletRequest request, String sessionname) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Cloud cloud = (Cloud) session.getAttribute(sessionname);
         if (cloud != null) {
            log.debug("cloud for user " + cloud.getUser().getIdentifier());
            return cloud;
         }
      }
      throw new RuntimeException("No cloud instance in user session.");
   }
   
   /**
    * Create cloud
    * @param username login username
    * @param password login password
    * @return the cloud for the user.
    **/
   public static Cloud createCloud(String username, String password) {
      CloudContext cloudContext = ContextProvider.getDefaultCloudContext();
      Map credentials = getUserCredentials(username, password);   
      return cloudContext.getCloud(DEFAULT_CLOUD_NAME, NP_AUTHENTICATION, credentials);
   }

   /**
    * Create cloud based on parameters in the request (username/password)
    * This uses the default session name to store the created cloud in 
    * @param req http request
    * @return The cloud
    */
   public static Cloud createCloud(HttpServletRequest req) {
      return createCloud(req, DEFAULT_SESSIONNAME);
   }

   /**
    * Create cloud based on parameters in the request (username/password)
    * @param req http request
    * @param sessionname session name to store the created cloud in
    * @return The cloud
    */
   public static Cloud createCloud(HttpServletRequest req, String sessionname) {
      String username = req.getParameter("username");
      String password = req.getParameter("password");
      String cloudName = req.getParameter("cloud");
      String authenticate = req.getParameter("authenticate");

      if (isEmptyOrWhitespace(cloudName)) {
         cloudName = DEFAULT_CLOUD_NAME;
      }
      if (isEmptyOrWhitespace(authenticate)) {
         authenticate = NP_AUTHENTICATION;
      }

      Cloud cloud = null;
      if (!isEmptyOrWhitespace(username) && !isEmptyOrWhitespace(password)) {
         final CloudContext context = ContextProvider.getCloudContext("local");
         final Map loginInfo = CloudFactory.getUserCredentials(username, password);
         cloud = context.getCloud(DEFAULT_CLOUD_NAME, NP_AUTHENTICATION, loginInfo);
         HttpSession session = req.getSession();
         if (session != null) {
            session.setAttribute(sessionname, cloud);
         }
         else {
            log.warn("Could not get or create a session to put the cloud on.");
         }
      }

      return cloud;
   }

   /**
    * is Empty Or Whitespace.String
    * @param str String to check emptiness
    * @return boolean is it empty
    */
   public static boolean isEmptyOrWhitespace(String str) {
      return (str == null) || "".equals(str.trim());
   }
   
   /**
    * Checks if a cloud is on the session with given the default sessionname.
    * @param request HttpServletRequest to search for the cloud.
    * @return true if a cloud is found, false otherwise.
    */
   public static boolean hasCloud(HttpServletRequest request) {
      return hasCloud(request, DEFAULT_SESSIONNAME);
   }

   /**
    * Checks if a cloud is on the session with given sessionname.
    * @param request HttpServletRequest to search for the cloud.
    * @param sessionname The name of the cloud on the session.
    * @return true if a cloud is found, false otherwise.
    */
   public static boolean hasCloud(HttpServletRequest request, String sessionname) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Cloud cloud = (Cloud) session.getAttribute(sessionname);
         if (cloud != null) {
            log.debug("cloud for user " + cloud.getUser().getIdentifier());
            return true;
         }
      }
      return false;
   }
}