package jp.sourceforge.shovel.service.impl;

import static org.seasar.framework.container.ContainerConstants.*;
import static jp.sourceforge.shovel.GrowlType.*;
import static jp.sourceforge.shovel.SessionConst.*;

import java.util.HashMap;
import java.util.Map;

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

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

import jp.sourceforge.shovel.RepliesType;
import jp.sourceforge.shovel.SortOrderType;
import jp.sourceforge.shovel.SortType;
import jp.sourceforge.shovel.entity.IGrowlPacket;
import jp.sourceforge.shovel.entity.IServerFile;
import jp.sourceforge.shovel.entity.IUser;
import jp.sourceforge.shovel.exception.ApplicationException;
import jp.sourceforge.shovel.interceptor.CsrfInterceptor;
import jp.sourceforge.shovel.logic.IDirectoryLogic;
import jp.sourceforge.shovel.logic.IGrowlLogic;
import jp.sourceforge.shovel.service.IDirectoryService;

public class DirectoryServiceImpl implements IDirectoryService {
    Map<Long, IUser> userMapByPK_;
    Map<String, IUser> userMapByFK_;
    
    public DirectoryServiceImpl() {
        userMapByPK_ = new HashMap<Long, IUser>();
        userMapByFK_ = new HashMap<String, IUser>();
    }
    void cacheUser(IUser user) {
        if (user != null && fetchUser(user.getUserId()) == null) {
            userMapByPK_.put(user.getUserId(), user);
            userMapByFK_.put(user.getForeignKey(), user);
        }
    }
    void cacheUsers(IUser[] users) {
        for (IUser user : users) {
            cacheUser(user);
        }
    }
    void purgeAllUser() {
        userMapByPK_.clear();
        userMapByFK_.clear();
    }
    void purgeUser(long userId) {
        userMapByPK_.remove(userId);
        IUser user = userMapByPK_.get(userId);
        String foreignKey = user.getForeignKey();
        userMapByFK_.remove(foreignKey);
    }
    IUser fetchUser(long userId) {
        return userMapByPK_.get(userId);
    }
    IUser fetchUser(String foreignKey) {
        return userMapByFK_.get(foreignKey);
    }
    int correctLimit(int limit) {
        IUser user = getLoginUser();
        if (user != null && limit <= 0) {
            limit = user.getViewLines();
        }
        //TODO
        if (limit <= 0 || limit > 20) {
            limit = 20;
        }
        return limit;
    }

    /////
    
    String once_;
    
    public IUser createUser(String displayName, String foreignKey, String password, String email,
            IServerFile serverFile, String location, String timeZoneId, String description, boolean protect, boolean cache) throws ApplicationException {
        if (cache) {
            IUser user = getUser(foreignKey);
            if (user != null) {
                throw new ApplicationException("");
            }
        }
        long serverFileId = 0;
        if (serverFile != null) {
            serverFileId = serverFile.getServerFileId();
        }
        long creatorId = getLoginUser().getUserId();
        IUser user = getDirectoryLogic().createUser(displayName, foreignKey, password, email,
                serverFileId, location, timeZoneId, description, creatorId, protect);
        return user;
    }
    public IUser createTemporaryUser() {
        return getDirectoryLogic().createTemporaryUser();
    }
    public IUser login(String foreignKey, String password, boolean once) {
        IUser user = null;
        if (foreignKey != null && foreignKey.length() > 0) {
            //外部キー必須
            user = getUser(foreignKey);
            if (user != null && !user.isRemove()) {
                if (password == null) {
                    //null＝空文字
                    password = "";
                }
                if (password.compareTo(user.getPassword()) == 0) {
                    if (once) {
                        once_ = user.getForeignKey();
                    } else {
                        String redirectUrl = (String)getSession().getAttribute(S_LOGIN_REDIRECT_URL);
                        //Session Fixation対策にログイン前後でセッションを変える
                        getSession().invalidate();
                        getSession().setAttribute(S_LOGIN, user.getForeignKey());
                        getSession().setAttribute(S_LOGIN_REDIRECT_URL, redirectUrl);
                    }
                    CsrfInterceptor.setCsrfTicket(user);
                } else {
                    user = null;
                }
            }
        }
        return user;
    }
    public void logout() {
        getSession().invalidate();
    }
    public IUser getLoginUser() {
        String foreignKey = once_;
        if (foreignKey == null) {
            foreignKey = (String)getSession().getAttribute(S_LOGIN);
        }
        IUser user = getUser(foreignKey);
        if (user != null && user.isRemove()) {
            return null;
        }
        getRequest().setAttribute("loginUser", user);
        return user;
    }
    public IUser getUser(long userId) {
        IUser user = fetchUser(userId);
        if (user == null) {
            user = getDirectoryLogic().getUser(userId);
            cacheUser(user);
        }
        return user;
    }
    public IUser getUser(String foreignKey) {
        if (foreignKey == null || foreignKey.length() <= 0) {
            return null;
        }
        IUser user = fetchUser(foreignKey);
        if (user == null) {
            user = getDirectoryLogic().getUserByForeignKey(foreignKey);
            cacheUser(user);
        }
        return user;
    }
    public IUser getUser(String foreignKey, boolean email) {
        IUser user = null;
        if (foreignKey == null || foreignKey.length() <= 0) {
            return null;
        }
        user = fetchUser(foreignKey);
        if (user == null) {
            user = getDirectoryLogic().getUserByForeignKeyOrEmail(foreignKey, email);
            cacheUser(user);
        }
        return user;
    }
    public IUser[] getUsers(SortType sortType, SortOrderType sortOrderType, int page) {
        IUser user = getLoginUser();
        //TODO
        int limit = 10;
        if (user != null) {
            limit = user.getViewLines();
        }
        IUser[] users = getDirectoryLogic().getUsers(sortType, sortOrderType, page * limit, limit + 1);
        cacheUsers(users);
        return users;
    }
    public IUser[] getUsers(long[] userIds) {
        IUser[] users = getDirectoryLogic().getUsers(userIds);
        cacheUsers(users);
        return users;
    }
    public IUser[] getUsers(String[] foreignKeys) {
        IUser[] users = getDirectoryLogic().getUsers(foreignKeys);
        cacheUsers(users);
        return users;
    }
    public IUser[] searchUsers(String keyword, int page, int limit) {
        limit = correctLimit(limit);
        IUser[] users = getDirectoryLogic().searchUsers(keyword, page * limit, limit + 1);
        cacheUsers(users);
        return users;
    }
    public IUser[] getRandUsers() {
        IUser[] users = getDirectoryLogic().getRandUsers(false, 0, 10);
        cacheUsers(users);
        return users;
    }
    //TODO 将来はキューでまったり
    public int removeUser(long userId) throws ApplicationException {
        return removeUsers(new long[] {userId});
    }
    //TODO 将来はキューでまったり
    public int removeUsers(long[] userIds) {
        return getDirectoryLogic().updateRemove(userIds);
    }
    public int updateUser(IUser user) throws ApplicationException {
        return getDirectoryLogic().updateUser(user);
    }
    public int updateUserFromImportCsv(IUser user) throws ApplicationException {
        return getDirectoryLogic().updateUserFromImportCsv(user);
    }
    public int updateUserFromSettings(IUser user) throws ApplicationException {
        return getDirectoryLogic().updateUserFromSettings(user);
    }
    public int updatePassword(IUser user, String password) {
        if (user == null) {
            user = getLoginUser();
        }
        user.setPassword(password);
        return getDirectoryLogic().updatePassword(user.getUserId(), password);
    }
    public int updatePicture(long serverFileId) {
        IUser user = getLoginUser();
        return getDirectoryLogic().updatePicture(user.getUserId(), serverFileId);
    }
    public int updateNotices(RepliesType repliesType, boolean newFriends, boolean reply, boolean recipientDirectMessage,
            boolean growl, String growlHost, String growlPassword) throws ApplicationException {
        if (growl) {
            IGrowlPacket packet = getGrowlLogic().createPacket("Shovel", REGISTRATION, growlPassword);
            packet.addNotification("Updated Status", true);
            packet.addNotification("Received Direct Message", true);
            getGrowlLogic().sendPacket(growlHost, packet);
        }
        
        IUser user = getLoginUser();
        return getDirectoryLogic().updateNotices(user.getUserId(), repliesType, newFriends,
                reply, recipientDirectMessage, growl, growlHost, growlPassword);
    }
    public int incrementFavorites(long userId) {
        return getDirectoryLogic().incrementFavorites(userId);
    }
    public int incrementFavorites(String foreignKey) {
        return getDirectoryLogic().incrementFavorites(foreignKey);
    }
    public int incrementGivenFavorites(long userId) {
        return getDirectoryLogic().incrementGivenFavorites(userId);
    }
    public int incrementGivenFavorites(String foreignKey) {
        return getDirectoryLogic().incrementGivenFavorites(foreignKey);
    }
    public int incrementDirectMessages(long userId) {
        return getDirectoryLogic().incrementDirectMessages(userId);
    }
    public int incrementDirectMessages(String foreignKey) {
        return getDirectoryLogic().incrementDirectMessages(foreignKey);
    }
    public int incrementStatuses(long userId) {
        return getDirectoryLogic().incrementStatuses(userId);
    }
    public int incrementStatuses(String foreignKey) {
        return getDirectoryLogic().incrementStatuses(foreignKey);
    }
    public int decrementFavoritesAll(long statusId) {
        return getDirectoryLogic().decrementFavoritesAll(statusId);
    }
    public int decrementFavorites(long userId) {
        return getDirectoryLogic().decrementFavorites(userId);
    }
    public int decrementFavorites(String foreignKey) {
        return getDirectoryLogic().decrementFavorites(foreignKey);
    }
    public int decrementGivenFavorites(long userId) {
        return getDirectoryLogic().decrementGivenFavorites(userId);
    }
    public int decrementGivenFavorites(String foreignKey) {
        return getDirectoryLogic().decrementGivenFavorites(foreignKey);
    }
    public int decrementStatuses(long userId) {
        return getDirectoryLogic().decrementStatuses(userId);
    }
    public int decrementStatuses(String foreignKey) {
        return getDirectoryLogic().decrementStatuses(foreignKey);
    }
    public int decrementDirectMessages(long userId) {
        return getDirectoryLogic().decrementDirectMessages(userId);
    }
    public int decrementDirectMessages(String foreignKey) {
        return getDirectoryLogic().decrementDirectMessages(foreignKey);
    }
    
    /////
    
    S2Container getContainer() {
        return SingletonS2ContainerFactory.getContainer();
    }
    HttpSession getSession() {
        return getRequest().getSession(true);
    }
    HttpServletRequest getRequest() {
        return (HttpServletRequest)getContainer().getComponent(REQUEST_NAME);
    }
    IDirectoryLogic getDirectoryLogic() {
        return (IDirectoryLogic)getContainer().getComponent(IDirectoryLogic.class);
    }
    IGrowlLogic getGrowlLogic() {
        return (IGrowlLogic)getContainer().getComponent(IGrowlLogic.class);
    }
}
