001package scpc.model.support;
002
003import java.math.BigDecimal;
004import javax.script.ScriptException;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007import scpc.Calculator;
008import scpc.model.IChainRule;
009import scpc.model.IRule;
010import scpc.model.SingleItem;
011
012/**
013 * Implement basic function of {@link IRule}
014 *
015 * @author Kent Yeh
016 * @param <T> type of real cart item.
017 */
018public abstract class AbstractRuleBase<T> implements IRule<T> {
019
020    private static final Logger logger = LoggerFactory.getLogger(AbstractRuleBase.class);
021
022    private int containsCount = 0;
023    private BigDecimal containsSumOfOriginalPrice = BigDecimal.ZERO;
024    private BigDecimal containsSumOfSalePrice = BigDecimal.ZERO;
025    private BigDecimal sumOfSerialOriginalPrice = BigDecimal.ZERO;
026    private BigDecimal serialSumOfSalePrice = BigDecimal.ZERO;
027    private IChainRule<T> previousRule = null;
028    private String triggerFormula = "";
029    private String quantityFormula = "";
030    private EvalHelper evalHelper = null;
031
032    /**
033     * A trigger formula is evaluated to determine whether to get a bonus or
034     * not.
035     * <br>走訪品項時決定是否觸發取得優惠品項的決策公式
036     *
037     * @return
038     */
039    public String getTriggerFormula() {
040        return triggerFormula;
041    }
042
043    /**
044     * Setting a trigger formula is evaluated to determine whether to get a
045     * bonus or not.
046     *
047     * @param triggerFormula
048     */
049    public void setTriggerFormula(String triggerFormula) {
050        this.triggerFormula = triggerFormula;
051    }
052
053    /**
054     * When triggered, a formula to check how many quantities should offer.
055     * <br>品項走訪觸發後,以公式決定優惠品項的數量
056     *
057     * @return
058     */
059    public String getQuantityFormula() {
060        return isLeaf() ? quantityFormula : null;
061    }
062
063    /**
064     * Setting a formula to check how many quantities should offer after
065     * triggered.
066     *
067     * @param quantityFormula
068     */
069    public void setQuantityFormula(String quantityFormula) {
070        this.quantityFormula = quantityFormula;
071    }
072
073    /**
074     * @see IRule#getPrevious()
075     * @return
076     */
077    @Override
078    public IChainRule<T> getPrevious() {
079        return previousRule;
080    }
081
082    /**
083     * Set previous rule if exists.
084     * <br>如果是串連規則,設定前串連規則
085     *
086     * @param previousRule previous rule
087     */
088    public void setPrevious(IChainRule<T> previousRule) {
089        this.previousRule = previousRule;
090    }
091
092    /**
093     * for {@link Calculator} use only.
094     *
095     * @return
096     */
097    @Override
098    public IRule<T> containsCountInc() {
099        containsCount++;
100        return this;
101    }
102
103    /**
104     * @see IRule#getContainsCount()
105     * @return
106     */
107    @Override
108    public int getContainsCount() {
109        return containsCount;
110    }
111
112    /**
113     * @see IRule#sumOfContainsOriginalPriceInc(java.math.BigDecimal)
114     * @param originalPrice
115     * @return
116     */
117    @Override
118    public IRule<T> sumOfContainsOriginalPriceInc(BigDecimal originalPrice) {
119        containsSumOfOriginalPrice = containsSumOfOriginalPrice.add(originalPrice);
120        return this;
121    }
122
123    /**
124     * @see IRule#sumOfContainsSalePriceInc(java.math.BigDecimal)
125     * @param salePrice
126     * @return
127     */
128    @Override
129    public IRule<T> sumOfContainsSalePriceInc(BigDecimal salePrice) {
130        containsSumOfSalePrice = containsSumOfSalePrice.add(salePrice.setScale(getPriceScale()));
131        return this;
132    }
133
134    /**
135     * @see IRule#getSumOfContainsOriginalPrice()
136     * @return
137     */
138    @Override
139    public BigDecimal getSumOfContainsOriginalPrice() {
140        return containsSumOfOriginalPrice;
141    }
142
143    /**
144     * @see IRule#getSumOfContainsSalePrice()
145     * @return
146     */
147    @Override
148    public BigDecimal getSumOfContainsSalePrice() {
149        return containsSumOfSalePrice;
150    }
151
152    /**
153     * @see IRule#getSumOfSerialOriginalPrice()
154     * @return
155     */
156    @Override
157    public BigDecimal getSumOfSerialOriginalPrice() {
158        return sumOfSerialOriginalPrice;
159    }
160
161    /**
162     * @see IRule#getSumOfSerialSalePrice()
163     * @return
164     */
165    @Override
166    public BigDecimal getSumOfSerialSalePrice() {
167        return serialSumOfSalePrice;
168    }
169
170    /**
171     * Setting the summary sale price of the same applicable items.
172     * <br>設定符合此規則的同品項售價小計
173     *
174     * @param serialSumOfSalePrice
175     */
176    public void setSerialSumOfSalePrice(BigDecimal serialSumOfSalePrice) {
177        this.serialSumOfSalePrice = serialSumOfSalePrice;
178    }
179
180    /**
181     * @see IRule#sumOfSerialOriginalPriceInc(java.math.BigDecimal)
182     * @param saleprice
183     * @return
184     */
185    @Override
186    public IRule<T> sumOfSerialOriginalPriceInc(BigDecimal saleprice) {
187        this.sumOfSerialOriginalPrice = this.sumOfSerialOriginalPrice.add(saleprice.setScale(getPriceScale()));
188        return this;
189    }
190
191    /**
192     * @see IRule#sumOfSerialSalePriceInc(java.math.BigDecimal)
193     * @param saleprice
194     * @return
195     */
196    @Override
197    public IRule<T> sumOfSerialSalePriceInc(BigDecimal saleprice) {
198        this.serialSumOfSalePrice = this.serialSumOfSalePrice.add(saleprice.setScale(getPriceScale()));
199        return this;
200    }
201
202    /**
203     * @see IRule#resetSumOfSerialOriginalPrice()
204     * @return
205     */
206    @Override
207    public IRule<T> resetSumOfSerialOriginalPrice() {
208        this.sumOfSerialOriginalPrice = BigDecimal.ZERO;
209        return this;
210    }
211
212    /**
213     * @see IRule#resetSumOfSerialSalePrice()
214     * @return
215     */
216    @Override
217    public IRule<T> resetSumOfSerialSalePrice() {
218        this.serialSumOfSalePrice = BigDecimal.ZERO;
219        return this;
220    }
221
222    @Override
223    public void resetSumOfPrice() {
224        containsSumOfOriginalPrice = BigDecimal.ZERO;
225        containsSumOfSalePrice = BigDecimal.ZERO;
226        sumOfSerialOriginalPrice = BigDecimal.ZERO;
227        serialSumOfSalePrice = BigDecimal.ZERO;
228    }
229
230    /**
231     * A formula evaluator to help analysis.
232     * <br>公式解譯工具
233     *
234     * @return
235     */
236    public EvalHelper getEvalHelper() {
237        return evalHelper;
238    }
239
240    public void setEvalHelper(EvalHelper evalHelper) {
241        this.evalHelper = evalHelper;
242    }
243
244    /**
245     * @see IRule#isTriggered(scpc.model.SingleItem)
246     * @param item
247     * @return
248     * @throws ScriptException
249     */
250    @Override
251    public boolean isTriggered(SingleItem<T> item) throws ScriptException {
252        if (contains(item)) {
253            if (evalHelper == null) {
254                throw new RuntimeException("Not any evalHelper already setting yet!");
255            } else {
256                boolean res = Boolean.TRUE.equals(evalHelper.bindVaribles(this, item).eval(getTriggerFormula()));
257                logger.debug("eval(\"{}\") = {}", getTriggerFormula(), res);
258                return res;
259            }
260        } else {
261            logger.debug("not triggered owing not counts");
262            return false;
263        }
264    }
265
266    /**
267     * Quantity measure of bonus after {@link #isTriggered(scpc.model.SingleItem)
268     * }.
269     * * <br> 觸發觸發後,優惠品項的數量估值
270     *
271     * @return
272     * @throws ScriptException
273     */
274    public double evalQuantity() throws ScriptException {
275        if (isLeaf()) {
276            if (evalHelper == null) {
277                throw new RuntimeException("Not any evalHelper already setting yet!");
278            } else {
279                Number mb = (Number) evalHelper.eval(getQuantityFormula());
280                logger.debug("evail quantity(\"{}\")={}", getQuantityFormula(), mb);
281                return mb.doubleValue();
282            }
283        } else {
284            return 0d;
285        }
286    }
287
288}