/*
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package anyframe.core.basis.dao.query;

import java.io.Serializable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.util.ClassUtils;

import anyframe.common.Page;
import anyframe.common.exception.BaseException;
import anyframe.common.util.SearchVO;
import anyframe.common.util.StringUtil;
import anyframe.core.basis.dao.GenericDao;
import anyframe.core.properties.IPropertiesService;
import anyframe.core.query.AbstractDAO;

/**
 * This class serves as the Base class for all other
 * DAOs - namely to hold common CRUD methods that they
 * might all use. You should only need to extend this
 * class when your require custom CRUD logic. <p/>
 * 
 * @author <a href="mailto:bwnoll@gmail.com">Bryan Noll</a>
 * @author modified by SooYeon Park
 * @param <T>
 *        a type variable
 * @param <PK>
 *        the primary key for that type
 */
public class GenericDaoQuery<T, PK extends Serializable> extends AbstractDAO
        implements GenericDao<T, PK> {
    /**
     * Log variable for all child classes. Uses
     * LogFactory.getLog(getClass()) from Commons
     * Logging
     */
    protected final Log log = LogFactory.getLog(getClass());
    private Class<T> persistentClass;

    /** an instance variable for the propertiesService. */
    private IPropertiesService propertiesService;

    public Class<T> getPersistentClass() {
        return persistentClass;
    }

    /**
     * Constructor that takes in a class to see which
     * type of entity to persist. Use this constructor
     * when subclassing or using dependency injection.
     * @param persistentClass
     *        the class type you'd like to persist
     */
    public GenericDaoQuery(final Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    public IPropertiesService getPropertiesService() {
        return propertiesService;
    }

    /**
     * Sets the propertiesService to use.
     * @param propertiesService
     *        The propertiesService to set
     */
    public void setPropertiesService(IPropertiesService propertiesService) {
        this.propertiesService = propertiesService;
    }

    private Object getObject(PK id) throws InstantiationException,
            IllegalAccessException, ClassNotFoundException {
        // Object obj =
        // Class.forName(this.persistentClass.getName()).newInstance();
        Object obj =
            Thread.currentThread().getContextClassLoader().loadClass(
                this.persistentClass.getName()).newInstance();
        QueryDaoUtils.setPrimaryKey(obj, id.getClass(), id);
        return obj;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public T get(PK id) throws Exception {
        T object =
            (T) findByPk(ClassUtils.getShortName(this.persistentClass),
                getObject(id));
        if (object == null) {
            log.warn("Uh oh, '" + this.persistentClass + "' object with id '"
                + id + "' not found...");
            throw new ObjectRetrievalFailureException(ClassUtils
                .getShortName(this.persistentClass), id);
        }
        return object;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public boolean exists(PK id) throws Exception {
        T object =
            (T) findByPk(ClassUtils.getShortName(this.persistentClass),
                getObject(id));
        return object != null;
    }

    /**
     * {@inheritDoc}
     */
    public T save(T object) throws Exception {
        String className = ClassUtils.getShortName(object.getClass());
        Object primaryKey = QueryDaoUtils.getPrimaryKeyValue(object);

        // check for new record
        int result = 0;
        if (findByPk(ClassUtils.getShortName(this.persistentClass),
            getObject((PK) primaryKey)) == null) {
            result = create(className, object);
        } else {
            result = update(className, object);
        }

        // check for null id
        if (QueryDaoUtils.getPrimaryKeyValue(object) == null) {
            throw new ObjectRetrievalFailureException(className, object);
        } else if (result < 1) {
            throw new BaseException("Fail to execute sql statement.");
        } else if (result > 1) {
            throw new BaseException(
                "Affected rows was multiple. It should be affected to only one row.");
        } else {
            return object;
        }
    }

    /**
     * {@inheritDoc}
     */
    public void remove(PK id) throws Exception {
        remove(ClassUtils.getShortName(this.persistentClass), getObject(id));
    }

    public Page getList(SearchVO searchVO) throws Exception {
        int pageIndex = searchVO.getPageIndex();
        int pageSize = this.getPropertiesService().getInt("PAGE_SIZE");
        int pageUnit = this.getPropertiesService().getInt("PAGE_UNIT");

        String searchCondition =
            StringUtil.null2str(searchVO.getSearchCondition());
        String searchKeyword = StringUtil.null2str(searchVO.getSearchKeyword());
        String isNumeric =
            StringUtil.isNumeric(searchKeyword) ? "true" : "false";

        Object[] args = new Object[4];
        args[0] = "condition=" + searchCondition;
        args[1] = "keywordStr=%" + searchKeyword + "%";
        args[2] = "keywordNum=" + searchKeyword + "";
        args[3] = "isNumeric=" + isNumeric;

        return this.findListWithPaging(ClassUtils
            .getShortName(getPersistentClass()), args, pageIndex, pageSize,
            pageUnit);
    }
}

