package com.apobates.forum.strategy.exposure.config;

import com.apobates.forum.member.entity.Member;
import com.apobates.forum.strategy.StrategyMode;
import com.apobates.forum.strategy.exception.StrategyConfigLostException;
import com.apobates.forum.strategy.exception.StrategyException;
import com.apobates.forum.utils.lang.TriPredicate;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.ImmutableTriple;

/**
 * 抽像的实现配置文件策略
 * 
 * @param <T> 实体
 * @param <C> 实体的配置文件
 * @author xiaofanku
 * @since 20200806
 */
public abstract class AbstractConfigStrategy<T, C> {
    //B: config中的实体项目{checkSelf}
    protected abstract BiPredicate<T, StrategyMode> checkEntity();
    
    //A: 针对config可用性检测
    protected Predicate<C> checkConfig(){
        return (C config)->{
            if (null == config) {
                throw new StrategyConfigLostException("配置文件无法访问");
            }
            return true;
        };
    }
    
    //D: config中的会员检查项目{checkCulpritor()}
    protected abstract TriPredicate<C, Member, StrategyMode> checkCulpritor();
    
    /**
     * 配置文件策略初步检测,依次执行: 
     *  checkConfig, 默认的,保证实体配置文件不为null
     *  checkEntity, 子类负责实现
     *  checkCulpritor 子类负责实现
     * 若不希望执行以上某个检测可以在子类中覆盖该方法
     * @param entityConfig
     * @param entity
     * @param mode
     * @param culpritor
     * @return
     * @throws StrategyException 
     */
    public ComplexConfigStrategyExector<T,C> initial(C entityConfig, T entity, StrategyMode mode, Member culpritor)throws StrategyException{
        ConfigStrategyChain<T,C> csc = ConfigStrategyChain.<T,C>start()
                .config(checkConfig(), entityConfig)
                .entity(checkEntity(), ImmutablePair.of(entity, mode))
                .culpritor(checkCulpritor(), ImmutableTriple.of(entityConfig, culpritor, mode));
        if(csc.getBreakException().isPresent()){
            throw csc.getBreakException().get();
        }
        return new ComplexConfigStrategyExector<>(culpritor, entity, mode, entityConfig);
    }
}