/*
 * Copyright (C) 2005 - 2011 Jaspersoft Corporation. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License  as
 * published by the Free Software Foundation, either version 3 of  the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero  General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public  License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.jaspersoft.jasperserver.api.engine.common.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.common.domain.LogEvent;
import com.jaspersoft.jasperserver.api.common.domain.impl.RepoLogEvent;
import com.jaspersoft.jasperserver.api.engine.common.service.LoggingService;
import com.jaspersoft.jasperserver.api.engine.common.service.SecurityContextProvider;
import com.jaspersoft.jasperserver.api.engine.common.user.UserPersistenceHandler;
import com.jaspersoft.jasperserver.api.metadata.common.service.ResourceFactory;
import com.jaspersoft.jasperserver.api.metadata.user.domain.User;
import com.jaspersoft.jasperserver.api.metadata.user.domain.impl.hibernate.RepoUser;

/**
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 * @version $Id: HibernateLoggingService.java 19922 2010-12-11 14:59:51Z tmatyashovsky $
 */
public class HibernateLoggingService extends HibernateDaoSupport implements LoggingService {

	private static final Log log = LogFactory.getLog(HibernateLoggingService.class);

	private static final long EVENT_ID_NEW = 0l;

	private SecurityContextProvider securityContextProvider;
	private int maximumAge;
    private ResourceFactory objectFactory;
    private UserPersistenceHandler userHandler;

    public UserPersistenceHandler getUserHandler() {
		return userHandler;
	}

	public void setUserHandler(UserPersistenceHandler userHandler) {
		this.userHandler = userHandler;
	}

	public ResourceFactory getObjectMappingFactory() {
        return objectFactory;
    }

    public void setObjectMappingFactory(ResourceFactory objectFactory) {
        this.objectFactory = objectFactory;
    }

	public SecurityContextProvider getSecurityContextProvider() {
		return securityContextProvider;
	}

	public void setSecurityContextProvider(
			SecurityContextProvider securityContextProvider) {
		this.securityContextProvider = securityContextProvider;
	}

	public int getMaximumAge() {
		return maximumAge;
	}

	public void setMaximumAge(int days) {
		this.maximumAge = days;
	}

	public LogEvent instantiateLogEvent() {
		return createLogEvent();
	}

	protected LogEvent createLogEvent() {
		return new RepoLogEvent();
	}

	public void log(LogEvent event) {
		prepareForSave(event);
		getHibernateTemplate().save(event);
	}

    public void update(LogEvent event) {
        RepoLogEvent repoLogEvent = getRepoLogEvent(null, event.getId());
        repoLogEvent.copyFromClient(event);

        getHibernateTemplate().saveOrUpdate(repoLogEvent);
    }

    protected void prepareForSave(LogEvent event) {
		event.setId(EVENT_ID_NEW);
		if (event.getOccurrenceDate() == null) {
			event.setOccurrenceDate(new Date());
		}
        RepoLogEvent repoLogEvent = (RepoLogEvent)event;
        
        RepoUser persistentUser = userHandler.getPersistentUserFromContext();
        repoLogEvent.setUser(persistentUser);
    }

	public void purge() {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.DATE, -getMaximumAge());
		Date last = cal.getTime();

		if (log.isDebugEnabled()) {
			log.debug("Purging log events older than " + last);
		}

		getHibernateTemplate().bulkUpdate("delete RepoLogEvent e where e.occurrenceDate < ?", last);
	}


	public void delete(ExecutionContext context, long[] eventIds) {
		for (int i = 0; i < eventIds.length; i++) {
			LogEvent event = getRepoLogEvent(context, eventIds[i]);
			getHibernateTemplate().delete(event);
		}
	}


	public List getUserEvents(ExecutionContext context) {
        List events;
        RepoUser user = userHandler.getPersistentUserFromContext();
        if (user != null) {
            DetachedCriteria criteria = DetachedCriteria.forClass(RepoLogEvent.class);
            criteria.add(Restrictions.eq("user", user));
            criteria.addOrder(Order.desc("occurrenceDate"));

            events = getHibernateTemplate().findByCriteria(criteria);
        } else {
            events = Collections.emptyList();
        }

        return getClientEvents(events);
	}

    private List getClientEvents(List repoEvents) {
        List events = new ArrayList();

        for (int i=0; i<repoEvents.size(); i++) {
            events.add(((RepoLogEvent)repoEvents.get(i)).toClient(objectFactory));
        }
        return events;
    }

    public List getUnreadEvents(ExecutionContext context) {
        List events;
        RepoUser user = userHandler.getPersistentUserFromContext();
        if (user != null) {
            DetachedCriteria criteria = DetachedCriteria.forClass(RepoLogEvent.class);
            criteria.add(Restrictions.eq("state", new Byte(LogEvent.STATE_UNREAD)));
            criteria.add(Restrictions.eq("user", user));
            criteria.addOrder(Order.desc("occurrenceDate"));

            events = getHibernateTemplate().findByCriteria(criteria);
        } else {
            events = Collections.emptyList();
        }

        return getClientEvents(events);
	}

	public LogEvent getLogEvent(ExecutionContext context, long id) {
		return getRepoLogEvent(context, id).toClient(objectFactory);
	}

    public RepoLogEvent getRepoLogEvent(ExecutionContext context, long id) {
        RepoLogEvent event = (RepoLogEvent) getHibernateTemplate().get(RepoLogEvent.class, new Long(id));
        if (event == null) {
            log.debug("Log event with id " + id + " not found");
        } else {
            User user = securityContextProvider.getContextUser();
            if (event.getUser() == null) {
                log.debug("Log event with id " + id + " has no user.");
            } else {
                User eventUser = (User) ((RepoUser) event.getUser()).toClient(objectFactory);
                if (user.getTenantId() == null) {
                    if (!eventUser.getUsername().equals(user.getUsername()) ||
                            eventUser.getTenantId() != null) {
                        log.debug("Log event with id " + id + " does not belongs to user [username=\"" +
                                user.getUsername() + "\", tenantId=\"" + user.getTenantId() + "\".");
                        event = null;
                    }
                }
            }
        }

        return event;
    }

	public int getUserEventsCount(ExecutionContext context)
	{
		//FIXME ?
		String username = securityContextProvider.getContextUsername();

		List result = getHibernateTemplate().find("select count(*) from RepoLogEvent where state=?", new Byte(LogEvent.STATE_UNREAD));

		if (result != null) {
			Integer size = (Integer) result.get(0);
			return size.intValue();
		}
		return 0;
	}
}
