package com.hand.hap.system.service.impl;

import com.github.pagehelper.PageHelper;
import com.hand.hap.core.IRequest;
import com.hand.hap.core.annotation.StdWho;
import com.hand.hap.core.exception.UpdateFailedException;
import com.hand.hap.extensible.components.ServiceListenerChainFactory;
import com.hand.hap.mybatis.common.Criteria;
import com.hand.hap.mybatis.common.Mapper;
import com.hand.hap.system.dto.BaseDTO;
import com.hand.hap.system.dto.DTOStatus;
import com.hand.hap.system.service.IBaseService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author shengyang.zhou@hand-china.com
 */
@Service
public abstract class BaseServiceImpl<T> implements IBaseService<T> {

    @Autowired
    protected Mapper<T> mapper;

    @Autowired
    private ServiceListenerChainFactory chainFactory;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<T> select(IRequest request, T condition, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return mapper.select(condition);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T insert(IRequest request, T record) {
        record = (T) chainFactory.getChain(this).beforeInsert(request, record);
        mapper.insert(record);
        record = (T) chainFactory.getChain(this).afterInsert(request, record);
        return record;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T insertSelective(IRequest request, T record) {
        record = (T) chainFactory.getChain(this).beforeInsert(request, record);
        mapper.insertSelective(record);
        record = (T) chainFactory.getChain(this).afterInsert(request, record);
        return record;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T updateByPrimaryKey(IRequest request, @StdWho T record) {
        record = (T) chainFactory.getChain(this).beforeUpdate(request, record);
        int ret = mapper.updateByPrimaryKey(record);
        checkOvn(ret, record);
        record = (T) chainFactory.getChain(this).afterUpdate(request, record);
        return record;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T updateByPrimaryKeySelective(IRequest request, @StdWho T record) {
        record = (T) chainFactory.getChain(this).beforeUpdate(request, record);
        int ret = mapper.updateByPrimaryKeySelective(record);
        checkOvn(ret, record);
        record = (T) chainFactory.getChain(this).afterUpdate(request, record);
        return record;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T updateByPrimaryKeyOptions(IRequest request, @StdWho T record, Criteria criteria) {
        record = (T) chainFactory.getChain(this).beforeUpdate(request, record);
        int ret = mapper.updateByPrimaryKeyOptions(record,criteria);
        checkOvn(ret, record);
        record = (T) chainFactory.getChain(this).afterUpdate(request, record);
        return record;
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public T selectByPrimaryKey(IRequest request, T record) {
        return mapper.selectByPrimaryKey(record);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByPrimaryKey(T record) {
        record = (T) chainFactory.getChain(this).beforeDelete(null, record);
        int ret = mapper.deleteByPrimaryKey(record);
        checkOvn(ret, record);
        record = (T) chainFactory.getChain(this).afterDelete(null, record);
        return ret;
    }

    /**
     * 检查乐观锁<br>
     * 检测到更新,删除失败时,抛出UpdateFailedException 异常
     * 
     * @param updateCount
     *            update,delete 操作返回的值
     * @param record
     *            操作参数
     */
    protected void checkOvn(int updateCount, Object record) {
        if (updateCount == 0 && record instanceof BaseDTO) {
            BaseDTO baseDTO = (BaseDTO) record;
            if (baseDTO.getObjectVersionNumber() != null) {
                throw new RuntimeException(new UpdateFailedException(baseDTO));
            }
        }
    }

    @Override
    @Deprecated
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<T> selectAll() {
        return mapper.selectAll();
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<T> selectAll(IRequest request) {
        return mapper.selectAll();
    }

    /**
     * this method assume the object in list is BaseDTO.
     * 
     * @param request
     *            requestContext
     * @param list
     *            dto list
     * @return the list
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<T> batchUpdate(IRequest request, List<T> list) {
        IBaseService<T> self = ((IBaseService<T>) AopContext.currentProxy());
        for (T t : list) {
            switch (((BaseDTO) t).get__status()) {
            case DTOStatus.ADD:
                self.insertSelective(request, t);
                break;
            case DTOStatus.UPDATE:
                if (useSelectiveUpdate()) {
                    self.updateByPrimaryKeySelective(request, t);
                } else {
                    self.updateByPrimaryKey(request, t);
                }
                break;
            case DTOStatus.DELETE:
                self.deleteByPrimaryKey(t);
                break;
            default:
                break;
            }
        }
        return list;
    }

    /**
     * 默认 true,表示在 batchUpdate 中,更新操作,使用updateByPrimaryKeySelective(只更新不为 null
     * 的字段)。<br>
     * 若返回 false,则使用 updateByPrimaryKey(更新所有字段)
     * 
     * @return
     */
    protected boolean useSelectiveUpdate() {
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchDelete(List<T> list) {
        IBaseService<T> self = ((IBaseService<T>) AopContext.currentProxy());
        int c = 0;
        for (T t : list) {
            c += self.deleteByPrimaryKey(t);
        }
        return c;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public List<T> selectOptions(IRequest request, T record, Criteria criteria) {
        if(criteria == null) criteria = new Criteria(record);
        IBaseService<T> self = ((IBaseService<T>) AopContext.currentProxy());
        return self.selectOptions(request,record, criteria, null, null);
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public List<T> selectOptions(IRequest request, T record, Criteria criteria, Integer pageNum, Integer pageSize) {
        if(pageNum != null && pageSize != null)
        PageHelper.startPage(pageNum, pageSize);
        if(criteria == null) criteria = new Criteria(record);
        List<T>  list =  mapper.selectOptions(record, criteria);
        return list;
    }

}