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