/*
 * 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.services.organization.jdbc;

import org.exoplatform.commons.utils.LazyPageList;
import org.exoplatform.commons.utils.ListAccess;
import org.exoplatform.services.database.DBObjectMapper;
import org.exoplatform.services.database.DBObjectQuery;
import org.exoplatform.services.database.ExoDatasource;
import org.exoplatform.services.database.StandardSQLDAO;
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.ExtendedUserHandler;
import org.exoplatform.services.organization.Group;
import org.exoplatform.services.organization.GroupHandler;
import org.exoplatform.services.organization.Membership;
import org.exoplatform.services.organization.MembershipHandler;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.Query;
import org.exoplatform.services.organization.User;
import org.exoplatform.services.organization.UserEventListener;
import org.exoplatform.services.organization.UserHandler;
import org.exoplatform.services.organization.impl.mock.LazyListImpl;
import org.exoplatform.services.security.PasswordEncrypter;

import java.util.Calendar;
import java.util.List;

/**
 * Created by The eXo Platform SAS Apr 7, 2007
 */
public class UserDAOImpl extends StandardSQLDAO<UserImpl> implements UserHandler, ExtendedUserHandler
{

   protected static final Log LOG = ExoLogger.getLogger("exo.core.component.organization.jdbc.UserDAOImpl");

   protected ListenerService listenerService_;

   protected final OrganizationService orgService;

   public UserDAOImpl(OrganizationService orgSerivce, ListenerService lService, ExoDatasource datasource,
      DBObjectMapper<UserImpl> mapper)
   {
      super(datasource, mapper, UserImpl.class);

      this.orgService = orgSerivce;
      this.listenerService_ = lService;
   }

   public User createUserInstance()
   {
      return new UserImpl();
   }

   public User createUserInstance(String username)
   {
      return new UserImpl(username);
   }

   public void createUser(User user, boolean broadcast) throws Exception
   {
      if (LOG.isDebugEnabled())
         LOG.debug("----------- CREATE USER " + user.getUserName());
      UserImpl userImpl = (UserImpl)user;
      if (broadcast)
         listenerService_.broadcast(UserHandler.PRE_CREATE_USER_EVENT, this, userImpl);
      super.save(userImpl);
      if (broadcast)
         listenerService_.broadcast(UserHandler.POST_CREATE_USER_EVENT, this, userImpl);
   }

   public boolean authenticate(String username, String password) throws Exception
   {
      return authenticate(username, password, null);
   }

   public boolean authenticate(String username, String password, PasswordEncrypter pe) throws Exception
   {
      User user = findUserByName(username);
      if (user == null)
      {
         return false;
      }

      boolean authenticated;
      if (pe == null)
      {
         authenticated = user.getPassword().equals(password);
      }
      else
      {
         String encryptedPassword = new String(pe.encrypt(user.getPassword().getBytes()));
         authenticated = encryptedPassword.equals(password);
      }

      if (LOG.isDebugEnabled())
      {
         LOG.debug("+++++++++++AUTHENTICATE USERNAME " + username + " AND PASS " + password + " - " + authenticated);
      }
      if (authenticated)
      {
         UserImpl userImpl = (UserImpl)user;
         userImpl.setLastLoginTime(Calendar.getInstance().getTime());
         saveUser(userImpl, false);
      }
      return authenticated;
   }

   public User findUserByName(String userName) throws Exception
   {
      DBObjectQuery<UserImpl> query = new DBObjectQuery<UserImpl>(UserImpl.class);
      query.addEQ("USER_NAME", userName);
      User user = loadUnique(query.toQuery());;
      if (LOG.isDebugEnabled())
      {
         LOG.debug("+++++++++++FIND USER BY USER NAME " + userName + " - " + (user != null));
      }
      return user;
   }

   public LazyPageList<User> findUsers(org.exoplatform.services.organization.Query orgQuery) throws Exception
   {
      return new LazyPageList<User>(findUsersByQuery(orgQuery), 20);
   }

   /**
    * Query( name = "" , standardSQL = "..." oracleSQL = "..." )
    */
   public ListAccess<User> findUsersByQuery(Query orgQuery) throws Exception
   {
      DBObjectQuery<UserImpl> dbQuery = new DBObjectQuery<UserImpl>(UserImpl.class);
      if (orgQuery.getUserName() != null)
      {
         dbQuery.addLIKE("UPPER(USER_NAME)", addAsterisk(orgQuery.getUserName().toUpperCase()));
      }
      if (orgQuery.getFirstName() != null)
      {
         dbQuery.addLIKE("UPPER(FIRST_NAME)", orgQuery.getFirstName().toUpperCase());
      }
      if (orgQuery.getLastName() != null)
      {
         dbQuery.addLIKE("UPPER(LAST_NAME)", orgQuery.getLastName().toUpperCase());
      }
      dbQuery.addLIKE("EMAIL", orgQuery.getEmail());
      dbQuery.addGT("LAST_LOGIN_TIME", orgQuery.getFromLoginDate());
      dbQuery.addLT("LAST_LOGIN_TIME", orgQuery.getToLoginDate());

      return new JDBCListAccess<User>(this, dbQuery.toQuery(), dbQuery.toCountQuery());
   }

   public LazyPageList<User> findUsersByGroup(String groupId) throws Exception
   {
      return new LazyPageList<User>(findUsersByGroupId(groupId), 20);
   }

   public ListAccess<User> findUsersByGroupId(String groupId) throws Exception
   {
      if (LOG.isDebugEnabled())
         LOG.debug("+++++++++++FIND USER BY GROUP_ID " + groupId);

      MembershipHandler membershipHandler = orgService.getMembershipHandler();
      GroupHandler groupHandler = orgService.getGroupHandler();
      Group group = groupHandler.findGroupById(groupId);
      if (group == null)
      {
         return new LazyListImpl();
      }

      List<Membership> members = (List<Membership>)membershipHandler.findMembershipsByGroup(group);

      DBObjectQuery<UserImpl> dbQuery = new DBObjectQuery<UserImpl>(UserImpl.class);
      for (Membership member : members)
      {
         dbQuery.addEQ("USER_NAME", member.getUserName());
      }

      return new JDBCListAccess<User>(this, dbQuery.toQueryUseOR(), dbQuery.toCountQueryUseOR());
   }

   public LazyPageList<User> getUserPageList(int pageSize) throws Exception
   {
      return new LazyPageList<User>(findAllUsers(), pageSize);
   }

   public ListAccess<User> findAllUsers() throws Exception
   {
      DBObjectQuery<UserImpl> dbQuery = new DBObjectQuery<UserImpl>(UserImpl.class);
      return new JDBCListAccess<User>(this, dbQuery.toQuery(), dbQuery.toCountQuery());
   }

   public User removeUser(String userName, boolean broadcast) throws Exception
   {
      UserImpl userImpl = (UserImpl)findUserByName(userName);
      if (userImpl == null)
         return null;
      if (broadcast)
         listenerService_.broadcast(UserHandler.PRE_DELETE_USER_EVENT, this, userImpl);
      super.remove(userImpl);
      if (broadcast)
         listenerService_.broadcast(UserHandler.POST_DELETE_USER_EVENT, this, userImpl);
      return userImpl;
   }

   public void saveUser(User user, boolean broadcast) throws Exception
   {
      UserImpl userImpl = (UserImpl)user;
      if (broadcast)
         listenerService_.broadcast(UserHandler.PRE_UPDATE_USER_EVENT, this, userImpl);
      super.update(userImpl);
      if (broadcast)
         listenerService_.broadcast(UserHandler.POST_UPDATE_USER_EVENT, this, userImpl);
   }

   public void addUserEventListener(UserEventListener listener)
   {
   }

   public void removeUserEventListener(UserEventListener listener)
   {
   }

   private String addAsterisk(String s)
   {
      StringBuffer sb = new StringBuffer(s);
      if (!s.startsWith("*"))
      {
         sb.insert(0, "*");
      }
      if (!s.endsWith("*"))
      {
         sb.append("*");
      }

      return sb.toString();
   }
}
