package portal.server.users;

import java.io.Serializable;
import java.util.*;

import javax.persistence.*;

import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.Session;
import org.slf4j.*;

import com.isomorphic.datasource.*;
import com.isomorphic.util.*;

public class UserDao {

    private final Logger log = LoggerFactory.getLogger(getClass());
    
    @PersistenceContext
    protected EntityManager entityManager;

    public Session getHibernateSession() {
        Session hibernateSession = (Session)entityManager.getDelegate();
        return hibernateSession;
    }

    public DSResponse fetch(DSRequest dsRequest) throws Exception {
        log.info("procesing DMI fetch operation");
        DSResponse dsResponse = new DSResponse();
        Session hibernateSession = getHibernateSession();

        // DataSource protocol: get filter criteria
        String email = (String)dsRequest.getFieldValue("email");

        // DataSource protocol: get requested row range
        long startRow = (int)dsRequest.getStartRow();
        long endRow = (int)dsRequest.getEndRow();

        Criteria criteria = hibernateSession.createCriteria(User.class);
        Criterion emailRestriction = null;
        if (email != null) {
            emailRestriction = Restrictions.like("email", email, MatchMode.ANYWHERE);
            criteria.add(emailRestriction);
        }

        // determine total available rows
        // this is used by e.g. the ListGrid to auto-size its scrollbar
        criteria.setProjection(Projections.rowCount());
        Object rowCount = criteria.uniqueResult();
        long totalRows = 0;
        // Later versions of Hibernate return a Long rather than an Integer here, for all
        // those occasions when a fetch returns more than 2.1 billion rows...
        if (rowCount instanceof Integer) {
            totalRows = ((Integer)rowCount).intValue();
        } else if (rowCount instanceof Long) {
            totalRows = ((Long)rowCount).longValue();
        }

        // clamp endRow to available rows and slice out requested range
        endRow = Math.min(endRow, totalRows);

        // rebuilt the criteria minus the rowCount projection
        criteria = hibernateSession.createCriteria(User.class);
        if (email != null) criteria.add(emailRestriction);

        // limit number of rows returned to just what the ListGrid asked for
        criteria.setFirstResult((int)startRow);
        criteria.setMaxResults((int)(endRow - startRow));
        List matchingItems = criteria.list();

        // DataSource protocol: return matching user beans
        dsResponse.setData(matchingItems);
        // tell client what rows are being returned, and what's available
        dsResponse.setStartRow(startRow);
        dsResponse.setEndRow(endRow);
        dsResponse.setTotalRows(totalRows);

        return dsResponse;
    }

    public DSResponse add(DSRequest dsRequest, User user) throws Exception {
        log.info("procesing DMI add operation");

        DSResponse dsResponse = new DSResponse();

        // perform validation
        ErrorReport errorReport =
            dsRequest.getDataSource().validate(DataTools.getProperties(user), false);
        if (errorReport != null) {
            dsResponse.setStatus(DSResponse.STATUS_VALIDATION_ERROR);
            dsResponse.setErrorReport(errorReport);
            System.out.println("Errors: " + DataTools.prettyPrint(errorReport));
            return dsResponse;
        }

        Session hibernateSession = getHibernateSession();
        hibernateSession.saveOrUpdate(user);
        dsResponse.setData(user);
        return dsResponse;
    }

    public DSResponse update(DSRequest dsRequest, Map newValues) throws Exception {
        log.info("procesing DMI update operation");

        DSResponse dsResponse = new DSResponse();

        // perform validation
        ErrorReport errorReport = dsRequest.getDataSource().validate(newValues, false);
        if (errorReport != null) {
            dsResponse.setStatus(DSResponse.STATUS_VALIDATION_ERROR);
            dsResponse.setErrorReport(errorReport);
            System.out.println("Errors: " + DataTools.prettyPrint(errorReport));
            return dsResponse;
        }

        // primary key
        Serializable id = (Serializable)dsRequest.getFieldValue("id");

        Session hibernateSession = getHibernateSession();
        User user = (User)hibernateSession.get(User.class, id);

        log.warn("fetched user: " + DataTools.prettyPrint(user));

        // apply new values to the as-saved bean
        DataTools.setProperties(newValues, user);

        log.warn("Saving record: " + DataTools.prettyPrint(user));

        // persist
        hibernateSession.saveOrUpdate(user);
        dsResponse.setData(user);
        return dsResponse;
    }

    public User remove(User user) throws Exception {
        log.info("procesing DMI remove operation");

        Session hibernateSession = getHibernateSession();
        hibernateSession.delete(user);

        return user;
    }

}
