package com.brilliance.isc.common.irtcal.service.impl;

import cn.hutool.core.lang.Assert;
import com.brilliance.isc.common.cbsmod.service.CbsmodService;
import com.brilliance.isc.common.cbsmod.service.XrtmodService;
import com.brilliance.isc.common.irtcal.days.IrtDaysCalculatorFactory;
import com.brilliance.isc.common.irtcal.service.IrtcalService;
import com.brilliance.isc.common.irtcal.vo.IrdeleVo;
import com.brilliance.isc.common.irtcal.vo.IrpeleVo;
import com.brilliance.isc.common.irtcal.vo.IrtcalVo;
import com.brilliance.isc.common.util.SettleExceptionUtils;
import com.brilliance.isc.common.util.StringUtils;
import com.brilliance.isc.common.util.Utils;
import com.brilliance.mda.runtime.mda.util.*;
import com.brilliance.isc.bo.Cbb;
import com.brilliance.isc.bo.Irs;
import com.brilliance.isc.bo.Irt;
import com.brilliance.isc.mda.dao.CbbMapper;
import com.brilliance.isc.mda.dao.IrsMapper;
import com.brilliance.isc.mda.dao.IrtMapper;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("irtcalService")
public class IrtcalServiceImpl implements IrtcalService {

    //High Value for Date
    @JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8")
    private Date maxdat;

    @Autowired
    private IrtMapper irtMapper;

    @Autowired
    private CbbMapper cbbMapper;

    @Autowired
    private IrsMapper irsMapper;

    @Autowired
    private XrtmodService xrtmodService;

    @Autowired
    private CbsmodService cbsmodService;

    @Autowired
    private IrtDaysCalculatorFactory irtDaysCalculatorFactory;

    @Override
    public void setIRT(IrtcalVo irtcal, String cod, String cur) {
        irtcal.setIrt(setIRT(cod, cur));
    }

    /**
     * source:irtcal.0001.script
     */
    public Irt setIRT(String cod, String cur) {
        //! Determine IRT
        //! 1. can be used to set MIC and MCC at once
        //! 2. can be used to prepare ´SetUseRefRate´
        String sql = "select INR, COD, CUR, ALTCOD, SEQ, IRTMCC, IRTMIC, VER, ETGEXTKEY from IRT"
                + Utils.sdbEtyGenSql("IRT", "where COD = '" + cod + "' AND CUR = '" + cur + "'");
        Map<String, Object> dyncMap = new HashMap<>();
        dyncMap.put("sql", sql);
        List<Irt> irtList = irtMapper.dyncRead(dyncMap);
        if (irtList != null && irtList.size() > 0) {
            return irtList.get(0);
        }
        return null;
    }

    /**
     * source:irtcal.0002.script
     */
    public void initialize(IrtcalVo irtcal) {
        //! Initialize IRTCAL for next calculation
        //! delete content of IRDELE
        //! delete content of IRSELE
        irtcal.setMinamt(new BigDecimal(-1));
        irtcal.setMaxamt(new BigDecimal(-1));
        irtcal.setCalbegdat(null);
        irtcal.setCalenddat(null);
        // assert minimum one row in IRDELE (required for SetPeriod)
        if (irtcal.getIrdele().size() == 0) {
            irtcal.getIrdele().add(new IrdeleVo());
        }
        /*if (Grids.gridCount(this.getIrdele()) < 1) {
            Grids.gridInsertRow(this.getIrdele(), 0);
        }*/
        irtcal.getIrdele().forEach(irdeleVo -> irdeleVo.setBegdat(Utils.getMaxDate()));
       /* int grdCnt = Grids.gridCount(this.getIrdele());
        for (int idx = 1; idx <= grdCnt; idx++) {
            MdaUtils.clear(this.getIrdele().getElement(idx));
            this.getIrdele().getElement(idx).setBegdat(this.getMaxdat());
        }*/
        // assert minimum one row in IRPELE (required for SetPeriod)
        if (irtcal.getIrpele().size() == 0) {
            irtcal.getIrpele().add(new IrpeleVo());
        }
        irtcal.getIrpele().forEach(irpeleVo -> irpeleVo.setBegdat(Utils.getMaxDate()));
        /*if (Grids.gridCount(this.getIrpele()) < 1) {
            Grids.gridInsertRow(this.getIrpele(), 0);
        }
        grdCnt = Grids.gridCount(this.getIrpele());
        for (int idx = 1; idx <= grdCnt; idx++) {
            MdaUtils.clear(this.getIrpele().getElement(idx));
            this.getIrpele().getElement(idx).setBegdat(this.getMaxdat());
        }*/
        irtcal.setIrpcur("");
        irtcal.setPerclsflg("");
        irtcal.setFulperflg("");

    }

    /**
     * source:irtcal.0003.script
     */
    public void setPeriod(IrtcalVo irtcal, Date frodat, Date tildat) {
        //! Set (closed) period
        initialize(irtcal);
        irtcal.getIrdele().get(0).setBegdat(frodat);
        irtcal.getIrdele().get(0).setEnddat(tildat);
        irtcal.getIrpele().get(0).setBegdat(frodat);
        irtcal.getIrpele().get(0).setEnddat(tildat);
        irtcal.setCalbegdat(frodat);
        irtcal.setCalenddat(tildat);

        /*MdaUtils.clear(this.getIrdele().getElement(1));
        this.getIrdele().getElement(1).setBegdat(frodat);
        this.getIrdele().getElement(1).setEnddat(tildat);
        this.setIrpcnt(1);
        MdaUtils.clear(this.getIrpele().getElement(1));
        this.getIrpele().getElement(1).setBegdat(frodat);
        this.getIrpele().getElement(1).setEnddat(tildat);
        this.setCalbegdat(frodat);
        this.setCalenddat(tildat);
        if (Dates.diff(tildat, Dynamic.sdbRngGetMaxDat()) > 0) {
            ctx.setErrorCode(1000, Strings.formatText("End of period can not exceed $1.\nDate $2 is not allowed.", Formats.fmtDateShort(Dynamic.sdbRngGetMaxDat()), Formats.fmtDateShort(tildat)));
            if (1 == 1) return;
        }*/
    }

    /**
     * source:irtcal.0004.script
     */
    public void setMCC(IrtcalVo irtcal, String mcc) {
        //! Set Calendar Calculation Method
        irtcal.getIrt().setIrtmcc(mcc);
    }

    /**
     * source:irtcal.0005.script
     */
    public void setMIC(IrtcalVo irtcal, String mic) {
        //! Set Interest Calculation Method
        irtcal.getIrt().setIrtmic(mic);
    }

    /**
     * source:irtcal.0006.script
     */
    @Override
    public void setPrincipal(IrtcalVo irtcal, BigDecimal amt, String cur) {
        irtcal.getIrdele().forEach(irdeleVo -> irdeleVo.setBasamt(amt));
        irtcal.setIrpcur(cur);
    }

    /**
     * source:irtcal.0007.script
     */
    @Override
    public void setRate(IrtcalVo irtcal, BigDecimal rte) {
        //! Set fixed rate
        irtcal.getIrdele().forEach(irdeleVo -> irdeleVo.setRefrte(rte));
    }

    /**
     * source:irtcal.0008.script
     */
    public void setMargin(IrtcalVo irtcal, BigDecimal margin) {
        //取得上下文
        irtcal.getIrdele().forEach(irdeleVo -> irdeleVo.setMargin(margin));
    }

    /**
     * source:irtcal.0009.script
     */
    public BigDecimal calcAll(IrtcalVo irtcal) {
        calcIrd(irtcal);
        calcIrp(irtcal);
        BigDecimal res = new BigDecimal("0");
        for (int i = 0; i < irtcal.getIrpele().size(); i++) {
            res = res.add(irtcal.getIrpele().get(i).getAmt());
        }
        return res;

    }

    /**
     * source:irtcal.0010.script
     */
    public void calcIrp(IrtcalVo irtcal) {
        //! Calculate Interest for every period, apply min/max and calculate total interest amount
        irtcal.getIrpele().forEach(irpeleVo -> irpeleVo.setAmt(BigDecimal.ZERO));
        int irpidx = 0;
        for (int irdidx = 0; irdidx < irtcal.getIrdele().size(); irdidx++) {
            if (Dates.diff(irtcal.getIrdele().get(irdidx).getEnddat(), irtcal.getIrpele().get(irpidx).getEnddat()) > 0) {
                irpidx = irpidx + 1;
            }
            switch (irtcal.getFulperflg()) {
                case "M":
                    if (MdaUtils.compareTo(irtcal.getIrpele().get(irpidx).getAmt(), 0) == 0) {
                        irtcal.getIrpele().get(irpidx).setCalflg("C");
                        irtcal.getIrpele().get(irpidx).setAmt(irtcal.getIrdele().get(irdidx).getAmt());
                    } else {
                        if (MdaUtils.compareTo(Numerics.abs(irtcal.getIrdele().get(irdidx).getAmt()), Numerics.abs(irtcal.getIrpele().get(irpidx).getAmt())) > 0) {
                            irtcal.getIrpele().get(irpidx).setAmt(irtcal.getIrdele().get(irdidx).getAmt());
                        }
                        irtcal.getIrpele().get(irpidx).setCalflg("M");
                    }
                    break;
                case "":
                    irtcal.getIrpele().get(irpidx).setAmt(Decimals.add(irtcal.getIrpele().get(irpidx).getAmt(), irtcal.getIrdele().get(irdidx).getAmt()));
                    irtcal.getIrpele().get(irpidx).setCalflg("C");
                    break;
                default:
                    SettleExceptionUtils.sysDump(Strings.formatText("FULPERFLG >$1< not supported in CalcIrp", irtcal.getFulperflg()));
            }
            irtcal.getIrpele().get(irpidx).setCur(irtcal.getIrpcur());
        }
        for (irpidx = 0; irpidx < irtcal.getIrpele().size(); irpidx++) {
            if (MdaUtils.compareTo(irtcal.getMinamt(), 0) > 0 && MdaUtils.compareTo(irtcal.getIrpele().get(irpidx).getAmt(), irtcal.getMingrcamt()) >= 0) {
                if (MdaUtils.compareTo(irtcal.getIrpele().get(irpidx).getAmt(), irtcal.getMinamt()) < 0) {
                    irtcal.getIrpele().get(irpidx).setAmt(irtcal.getMinamt());
                    irtcal.getIrpele().get(irpidx).setCalflg("L");
                }
            }
            if (MdaUtils.compareTo(irtcal.getMaxamt(), 0) > 0) {
                if (MdaUtils.compareTo(irtcal.getIrpele().get(irpidx).getAmt(), irtcal.getMaxamt()) > 0) {
                    irtcal.getIrpele().get(irpidx).setAmt(irtcal.getMaxamt());
                    irtcal.getIrpele().get(irpidx).setCalflg("U");
                }
            }
        }

    }

    /**
     * source:irtcal.0011.script
     */
    public void calcIrd(IrtcalVo irtcal) {
        String perclsflg = "";
        BigDecimal irtmiccal = null;
        for (int idx = 0; idx < irtcal.getIrdele().size(); idx++) {
            // set `closed at end` flag for last period
            if (idx == irtcal.getIrdele().size() - 1) {
                perclsflg = irtcal.getPerclsflg();
            }
            // determine # of units by calendart method
            irtcal.getIrdele().get(idx).setUni(new BigDecimal(irtmcccal(irtcal.getIrdele().get(idx).getBegdat(), irtcal.getIrdele().get(idx).getEnddat(), irtcal.getIrt().getIrtmcc(), perclsflg)));
            // add reference rate + margin
            irtcal.getIrdele().get(idx).setRte(Decimals.add(irtcal.getIrdele().get(idx).getRefrte(), irtcal.getIrdele().get(idx).getMargin()));
            // determine interest amount by applying interest calculation
            irtmiccal = irtmiccal(irtcal.getIrdele().get(idx).getBasamt(), irtcal.getIrdele().get(idx).getRte(), irtcal.getIrdele().get(idx).getUni(), irtcal.getIrt().getIrtmic());
            irtcal.getIrdele().get(idx).setAmt(xrtmodService.amtRnd(irtmiccal, irtcal.getIrpcur()));
            irtcal.getIrdele().get(idx).setCur(irtcal.getIrpcur());
        }

    }

    /**
     * source:irtcal.0012.script
     */
    public void setMinMax(IrtcalVo irtcal, BigDecimal min, BigDecimal max) {
        //! Set minimum/maximum amount per period
        //! -1 for ´no minimum´ (resp. no maximum)
        irtcal.setMinamt(min);
        irtcal.setMaxamt(max);

    }

    /**
     * source:irtcal.0013.script
     */
    @Override
    public void modifyPrincipal(IrtcalVo irtcal, Date modifydat, BigDecimal amt, String cur) {
        //! Modify principal amount at date (amt contains difference amount)
        if (!StringUtils.isEmpty(irtcal.getIrpcur()) && !irtcal.getIrpcur().equals(cur)) {
            SettleExceptionUtils.sysDump("Change of currency during calculation");
        }
        irtcal.setIrpcur(cur);
        Date dat = modifydat;
        if (Dates.diff(dat, irtcal.getCalbegdat()) < 0) {
            dat = irtcal.getCalbegdat();
        }
        // modification after end of calculaiton period is irrelevant
        if (Dates.diff(dat, irtcal.getCalenddat()) >= 0) {
            return;
        }
        int idx = addIRDELE(irtcal, dat);
        if (idx >= 0) {
            for (int irdidx = idx; irdidx < irtcal.getIrdele().size(); irdidx++) {
                irtcal.getIrdele().get(irdidx).setBasamt(Decimals.add(irtcal.getIrdele().get(irdidx).getBasamt(), amt));
            }
        }
    }

    /**
     * source:irtcal.0014.script
     */
    public int addIRDELE(IrtcalVo irtcal, Date dat) {
        // internal function: adds an element into IRDELE if necessary, ending at date DAT
        // returns index of period that starts at `DAT`
        if (Dates.diff(dat, irtcal.getCalbegdat()) < 0) {
            //ctx.setErrorCode(1000, "Internal Error: Attempt to set date in IRD before start of calculation period");
            //return 0;
            SettleExceptionUtils.sysDump("Internal Error: Attempt to set date in IRD before start of calculation period");
        }
        if (Dates.diff(dat, irtcal.getCalenddat()) >= 0) {
            //ctx.setErrorCode(1000, "Internal Error: Attempt to set date in IRD at or after end of calculation period");
            //return 0;
            SettleExceptionUtils.sysDump("Internal Error: Attempt to set date in IRD before start of calculation period");
        }
        int idx = -1;
        IrdeleVo newIrdele = null;
        for (int i = 0; i < irtcal.getIrdele().size(); i++) {
            if (dat == irtcal.getIrdele().get(i).getBegdat()) {
                // there exists already an IRDELE with the required end date
                idx = i;
                break;
            }
            if (Dates.diff(dat, irtcal.getIrdele().get(i).getEnddat()) < 0) {
                // new date is in the range of IRDELE ($IRDIDX)
                // a a new element at the end of the list
                // after sort, new element will be insert after $IRDidx
                IrdeleVo irdele = irtcal.getIrdele().get(i);
                newIrdele = new IrdeleVo();
                BeanUtils.copyProperties(irdele, newIrdele);
                irdele.setEnddat(dat);
                newIrdele.setBegdat(dat);
                idx = i + 1;
                break;
            }
        }
        // after sort, new element will be insert after $IRDidx
        if (newIrdele != null) {
            irtcal.getIrdele().add(idx, newIrdele);
        }
        return idx;
        /*TODO(方法逻辑重写了) int idx = -1;
        for (int irdidx = 0; irdidx < irtcal.getIrdele().size(); irdidx++) {
            if (dat == irtcal.getIrdele().get(irdidx).getBegdat()) {
                // there exists already an IRDELE with the required end date
                idx = irdidx;
                return idx; //   ####   goto done
            }
            if (Dates.diff(dat, irtcal.getIrdele().get(irdidx).getEnddat()) < 0) {
                // new date is in the range of IRDELE ($IRDIDX)
                // a a new element at the end of the list
                this.setIrdcnt(this.getIrdcnt() + 1);
                // extend and initalize IRDELE-list if necessary
                if (this.getIrdcnt() > gridcnt) {
                    Grids.gridInsertRow(this.getIrdele(), 0, 10);
                    gridcnt = Grids.gridCount(this.getIrdele());
                    for (int iniidx = this.getIrdcnt(); iniidx <= gridcnt; iniidx++) {
                        this.getIrdele().getElement(iniidx).setBegdat(this.getMaxdat());
                    }
                }
                Datas.setModuleContent(this.getIrdele().getElement(this.getIrdcnt()), this.getIrdele().getElement(iRDidx));
                this.getIrdele().getElement(this.getIrdcnt()).setBegdat(dat);
                this.getIrdele().getElement(iRDidx).setEnddat(dat);
                Grids.gridSort(this.getIrdele(), "BEGDAT");
                // after sort, new element will be insert after $IRDidx
                idx = iRDidx + 1;
                return idx; //   ####   goto done
            }
        }
        // as range from CALBEGDAT until CALENDDAT is covered by IRDELE
        // DAT must be found, so loop should never be left at the end
        //ctx.setErrorCode(1000, "Internal Error in IRTCAL.AddIRDELE");
        SettleExceptionUtils.sysDump("Internal Error in IRTCAL.AddIRDELE");
        //    ####    label done*/
    }

    /**
     * source:irtcal.0015.script
     */
    public void setIRTforRate(IrtcalVo irtcal, String cod, String cur) {
        //! Determine IRT
        //! 1. can be used to set MIC and MCC at once
        //! 2. can be used to prepare 'L0000024'
        Irt irt = setIRT(cod, cur);
        setRateIRS(irtcal, "IRT", irt.getInr(), irt.getCur());
    }

    /**
     * source:irtcal.0016.script
     */
    @Override
    public void setRateIRS(IrtcalVo irtcal, String objtyp, String objinr, String cur) {
        //! Set varying base rate from IRS
        Irs irs = new Irs();
        irs.setCur(cur);
        irs.setObjinr(objinr);
        irs.setObjtyp(objtyp);
        Date beginDate = irtcal.getCalbegdat();
        Date endDate = irtcal.getCalenddat();
        String otherArgSql = " AND CUR = '" + cur + "'";
        String sql = Utils.sdbRngSelectValidInRange("IRS", beginDate, endDate, objinr, objtyp, otherArgSql);
        List<Irs> irsList = irsMapper.selectIrsByConditionSql(sql);
        if (irsList != null && irsList.size() > 0) {
            irs = irsList.get(0);
        }
        if (MdaUtils.isEmpty(irs.getEnddat())) {
            irs.setEnddat(Utils.getMaxDate());
        }
        if (Dates.diff(irs.getBegdat(), irtcal.getCalbegdat()) < 0) {
            irs.setBegdat(irtcal.getCalbegdat());
        }
        if (Dates.diff(irs.getBegdat(), irtcal.getCalenddat()) < 0) {
            int idx = addIRDELE(irtcal, irs.getBegdat());
            for (int irdidx = idx; irdidx < irtcal.getIrdele().size(); irdidx++) {
                irtcal.getIrdele().get(irdidx).setRefrte(irs.getRat());
            }
        }
    }

    /**
     * source:irtcal.0017.script
     */
    /*TODO (全部都是Systems.setVisible的逻辑)public void cutGrids() {
        //取得上下文
        IContext ctx = MdaEnv.getContext();
        ctx.setErrorCode(NO_ERROR);
        //! make columns in IRDELE/IRPELE that are not required invisible
        //! (with index > xxCNT which might irritate when diplayed)
        if (this.getIrdcnt() > 0) {
            for (int idx = 1; idx <= this.getIrdcnt(); idx++) {
                Systems.setVisible(this, "irdele[idx]");
            }
        }
        int gridcnt = Grids.gridCount(this.getIrdele());
        if (this.getIrdcnt() < gridcnt) {
            for (int idx = this.getIrdcnt() + 1; idx <= gridcnt; idx++) {
                Systems.resetVisible(this, "irdele[idx]");
            }
        }
        if (this.getIrpcnt() > 0) {
            for (int idx = 1; idx <= this.getIrpcnt(); idx++) {
                Systems.setVisible(this, "irpele[idx]");
            }
        }
        gridcnt = Grids.gridCount(this.getIrpele());
        if (this.getIrpcnt() < gridcnt) {
            for (int idx = this.getIrpcnt() + 1; idx <= gridcnt; idx++) {
                Systems.resetVisible(this, "irpele[idx]");
            }
        }
    }*/

    /**
     * source:irtcal.0019.script
     */
    public int getLastDayofMonth(BigDecimal yea, BigDecimal mon) {
        // determine last day of month
        // requires 'L0000029' to respect leap years
        BigDecimal nextmonth = Decimals.add(mon, 1);
        BigDecimal nextyear = null;
        if (MdaUtils.compareTo(nextmonth, 12) > 0) {
            nextyear = Decimals.add(yea, 1);
            nextmonth = new BigDecimal("1");
        } else {
            nextyear = yea;
        }
        String date = "01/" + Strings.mid(MdaUtils.toString(Numerics.toInt(nextmonth), -2), 1, 2) + "/" + Strings.mid(MdaUtils.toString(nextyear, -4), 1, 4);
        Date nextdate = Dates.add(Strings.parseDate(date), -1);
        return Dates.getDay(nextdate);
    }

    /**
     * source:irtcal.0020.script
     */
    public void setPeriods(IrtcalVo irtcal, Date begdat, String met) {
        //! Generate periods for date range defined by "L0000017"
        //! Starting date for period generation might be different from
        //! begin of is passed as first argument
        if (MdaUtils.isEmpty(irtcal.getCalbegdat()) || MdaUtils.isEmpty(irtcal.getCalenddat())) {
            SettleExceptionUtils.sysDump("Total calculation period not yet defined by \"SetPeriod\"");
        }
        if (Dates.diff(begdat, irtcal.getCalenddat()) >= 0) {
            SettleExceptionUtils.sysDump(Strings.formatText("Startdate for periods ($1) can not be later then  end date of calculation period ($2)", begdat, irtcal.getCalenddat()));
        }
        // separate last two digets and handle them as separate, numeric argument (e.g. grace period in days)
        String method = Strings.mid(met, 1, 4);
        String addarg = Strings.mid(met, 5, 2);
        int numarg = Strings.parseInt(addarg);
        // if not first period and within grace period there is no period to settle
        if (Dates.diff(begdat, irtcal.getCalbegdat()) < 0 && Dates.diff(irtcal.getCalenddat(), irtcal.getCalbegdat()) <= numarg) {
            return;
        }
        Date lastdate = begdat;

        Date nextdate = null;
        Date startdate = null;
        int len1 = 0;
        Date futuredate = null;
        Date enddate = null;
        int len2 = 0;
        int idx = 0;
        while (Dates.diff(lastdate, irtcal.getCalenddat()) < 0) {
            nextdate = getNextPeriod(begdat, lastdate, met);
            if (Dates.diff(nextdate, irtcal.getCalbegdat()) > 0 && Dates.diff(nextdate, irtcal.getCalenddat()) < 0) {
                // respect grace period
                if (numarg > 0) {
                    if (Dates.diff(lastdate, irtcal.getCalbegdat()) < 0) {
                        startdate = irtcal.getCalbegdat();
                    } else {
                        startdate = lastdate;
                    }
                    len1 = Dates.diff(nextdate, startdate);
                    futuredate = getNextPeriod(begdat, nextdate, met);
                    if (Dates.diff(futuredate, irtcal.getCalenddat()) > 0) {
                        enddate = irtcal.getCalenddat();
                    } else {
                        enddate = futuredate;
                    }
                    len2 = Dates.diff(enddate, nextdate);
                    if (len1 <= numarg || len2 <= numarg) {
                        nextdate = futuredate;
                    }
                }
            }
            lastdate = nextdate;
            // IRPELE is filled directly
            // IRDELE is filled by use of AddIRDELE, so "L0000019" can be called at any time before "L0000020"
            if (Dates.diff(nextdate, irtcal.getCalbegdat()) > 0) {
                //TODO 重写了这段代码了逻辑
                IrpeleVo irpeleVo = new IrpeleVo();
                if (irtcal.getIrpele().size() == 0) {
                    irpeleVo.setBegdat(irtcal.getCalbegdat());
                } else {
                    irpeleVo.setBegdat(lastdate);
                }
                irtcal.getIrpele().add(irpeleVo);
                // IRDELE is filled by use of AddIRDELE, so "L0000021" can be called at any time before "L0000022"
                if (Dates.diff(lastdate, irtcal.getCalenddat()) < 0) {
                    addIRDELE(irtcal, lastdate);
                }
                /*// use generated date for new period
                // IRPELE is filled directly
                this.setIrpcnt(this.getIrpcnt() + 1);
                if (this.getIrpcnt() > gridcnt) {
                    Grids.gridInsertRow(this.getIrpele(), 0, 10);
                    gridcnt = Grids.gridCount(this.getIrpele());
                    for (int iniidx = this.getIrdcnt(); iniidx <= gridcnt; iniidx++) {
                        this.getIrpele().getElement(iniidx).setBegdat(this.getMaxdat());
                    }
                }
                if (this.getIrpcnt() == 1) {
                    this.getIrpele().getElement(this.getIrpcnt()).setBegdat(this.getCalbegdat());
                } else {
                    this.getIrpele().getElement(this.getIrpcnt()).setBegdat(this.getIrpele().getElement(this.getIrpcnt() - 1).getEnddat());
                }
                this.getIrpele().getElement(this.getIrpcnt()).setEnddat(lastdate);
                // IRDELE is filled by use of AddIRDELE, so "L0000021" can be called at any time before "L0000022"
                if (Dates.diff(lastdate, this.getCalenddat()) < 0) {
                    addIRDELE(lastdate);
                }*/
            }
        }
        //重新设置irpele的begdate和enddate
        int gridcnt = irtcal.getIrpele().size();
        for (int i = gridcnt - 1; i >= 0; i--) {
            if (i == gridcnt - 1) {
                irtcal.getIrpele().get(i).setEnddat(irtcal.getCalenddat());
            } else {
                irtcal.getIrpele().get(i).setEnddat(irtcal.getIrpele().get(i + 1).getBegdat());
            }
        }
    }

    /**
     * source:irtcal.0021.script
     */
    @Override
    public Date getEndofCurrentPeriod(IrtcalVo irtcal, Date begdat, Date todaydat, String pertyp) {
        //! Determine end of period for `todaydat´
        //! This function destroys the current content of the IRTCAL instance used!
        initialize(irtcal);
        Date todat = Dates.add(todaydat, 800);
        setPeriod(irtcal, begdat, todat);
        setPeriods(irtcal, begdat, pertyp);
        for (int idx = 0; idx < irtcal.getIrpele().size(); idx++) {
            if (Dates.diff(irtcal.getIrpele().get(idx).getEnddat(), todaydat) >= 0) {
                return irtcal.getIrpele().get(idx).getEnddat();
            }
        }
        // can only be result of an error in SetPeriods, implementation of PERTYP
        return null;

    }

    /**
     * source:irtcal.0022.script
     */
    @Override
    public void setTierMargin(IrtcalVo irtcal, String tier, BigDecimal threshold, BigDecimal margin) {
        //! Set margin for part exceeding specified tier
        //! must be called for every threshold separately before CalcCall
        //! must becalled for greatest tier amount first
        //! splits IRDELE that do not contain a tiered amount (i.e. empty TRXFLG)
        //! where amount exceeds threshold into two lines
        if (MdaUtils.isEmpty(tier)) {
            //ctx.setErrorCode(200, "1st argument \" tier \" flag is not set in IRTCAL.SetTierMargin");
            SettleExceptionUtils.sysDump("1st argument \" tier \" flag is not set in IRTCAL.SetTierMargin");
        }
        if (MdaUtils.compareTo(threshold, 0) <= 0) {
            //ctx.setErrorCode(200, "2nd argument \"threshold\" is not greater then zero in IRTCAL.SetTierMargin");
            SettleExceptionUtils.sysDump("2nd argument \"threshold\" is not greater then zero in IRTCAL.SetTierMargin");
        }
        Map<Integer, IrdeleVo> maps = new HashMap<>();
        for (int idx = 0; idx < irtcal.getIrdele().size(); idx++) {
            // check all entries that set margin for every period
            IrdeleVo irdele = irtcal.getIrdele().get(idx);
            if (MdaUtils.isEmpty(irdele.getTrxflg())) {
                if (MdaUtils.compareTo(irdele.getBasamt(), threshold) > 0) {
                    IrdeleVo newIrdele = new IrdeleVo();
                    BeanUtils.copyProperties(irdele, newIrdele);
                    irdele.setBasamt(threshold);
                    newIrdele.setBasamt(newIrdele.getBasamt().subtract(threshold));
                    newIrdele.setMargin(margin);
                    newIrdele.setTrxflg(tier);
                    maps.put(idx + 1, newIrdele);
                }
            }
        }
        if (maps.size() > 0) {
            maps.forEach((index, irdeleVo) -> irtcal.getIrdele().add(index, irdeleVo));
        }
    }

    /**
     * source:irtcal.0024.script
     */
    public void setPeriodClosed(IrtcalVo irtcal) {
        //! Define that period is to be understood as closed at the end
        // to becalled immediately after call to SetPerio / SetPeriods
        irtcal.setPerclsflg("C");
        //IRDELE( 1 )\\ENDDAT = tildat
        //IRPELE( 1 )\\ENDDAT = tildat
        //CALENDDAT = tildat

    }

    /**
     * source:irtcal.0025.script
     */
    @Override
    public void modifyMargin(IrtcalVo irtcal, Date modifydat, BigDecimal newmargin) {
        //! Modify margin from date on (newmargin contains new value for margin)
        Date dat = modifydat;
        if (Dates.diff(dat, irtcal.getCalbegdat()) < 0) {
            dat = irtcal.getCalbegdat();
        }
        // modification after end of calculaiton period is irrelevant
        if (Dates.diff(dat, irtcal.getCalenddat()) >= 0) {
            return;
        }
        int idx = addIRDELE(irtcal, dat);
        if (idx >= 0) {
            for (int irdidx = idx; irdidx < irtcal.getIrdele().size(); irdidx++) {
                // set margin for every period
                irtcal.getIrdele().get(irdidx).setMargin(newmargin);
            }
        }

    }

    /**
     * source:irtcal.0026.script
     */
    @Override
    public void setPrincipalCBB(IrtcalVo irtcal, String cbcarg, String extId, String objtyp, String objinr) {
        //! Add varying principal from CBB
        //! Note:  SetPrincipalCBB adds the balance to the history of amounts.
        //!        Thus, if SetPrincipalCBB is called more than once between Initialize and CalcAll the result is calulated on the sum of the balances of contracts used in that calls of SetPrincipalCBB.
        Cbb cbb = cbsmodService.cbsSelectValidInRangeForObject(objtyp, objinr, cbcarg, extId, irtcal.getCalbegdat(), irtcal.getCalenddat());
        if (cbb == null) {
            SettleExceptionUtils.sysDump("找不到CBB记录, OBJTYPE["+objtyp+"], OBJINR["+objinr+"]");
        }
        if (!MdaUtils.isEmpty(irtcal.getIrpcur()) && MdaUtils.compareTo(irtcal.getIrpcur(), cbb.getCur()) != 0) {
            SettleExceptionUtils.sysDump("Currency from CBB does not match");
        }
        Assert.notNull(cbb);
        irtcal.setIrpcur(cbb.getCur());
        if (cbb.getEnddat() == null) {
            cbb.setEnddat(Utils.getMaxDate());
        }
        // relevant at starting date of calculation at the earliest
        // (otherwise AddIRDELE fails)
        if (Dates.diff(cbb.getBegdat(), irtcal.getCalbegdat()) < 0) {
            cbb.setBegdat(irtcal.getCalbegdat());
        }
        if (Dates.diff(cbb.getBegdat(), irtcal.getCalenddat()) < 0) {
            int idx1 = addIRDELE(irtcal, cbb.getBegdat());
            int idx2 = 0;
            if (Dates.diff(cbb.getEnddat(), irtcal.getCalenddat()) >= 0) {
                idx2 = irtcal.getIrdele().size();
            } else {
                idx2 = addIRDELE(irtcal, cbb.getEnddat()) - 1;
            }
            for (int irdidx = idx1; irdidx <= idx2; irdidx++) {
                irtcal.getIrdele().get(irdidx).setBasamt(Decimals.add(irtcal.getIrdele().get(irdidx).getBasamt(), cbb.getAmt()));
            }
        }
    }

    /**
     * source:irtcal.0027.script
     */
    public void setMinGrcAmt(IrtcalVo irtcal, BigDecimal argAmt) {
        //! Set grace amount for minimum per period
        //! If less than that amount is calualted, the minimum amount is not applied
        //! Use 0.01 as grace amount if minimum should not be aaplied if no amount is calulated in period
        irtcal.setMingrcamt(argAmt);

    }

    /**
     * source:irtcal.0028.script
     */
    public int irtmcccal(Date begdat, Date enddat, String methodarg, String argPERCLSFLG) {
        // Calculation of # of units ( days) for given period
        // begdat included, enddat excluded
        // if argPERCLSFLG="C" then enddat is included
        if (Dates.diff(begdat, enddat) >= 0) {
            //ctx.setErrorCode(200, "From date >= Until date in IRTCAL.IRTMCCCAL");
            //return 0;
            SettleExceptionUtils.sysDump("From date >= Until date in IRTCAL.IRTMCCCAL");
        }
        if (MdaUtils.isEmpty(begdat)) {
            //ctx.setErrorCode(200, "From date is empty in IRTCAL.IRTMCCCAL");
            //return 0;
            SettleExceptionUtils.sysDump("From date is empty in IRTCAL.IRTMCCCAL");
        }
        if (MdaUtils.isEmpty(enddat)) {
            //ctx.setErrorCode(200, "Until date is empty in IRTCAL.IRTMCCCAL");
            //return 0;
            SettleExceptionUtils.sysDump("Until date is empty in IRTCAL.IRTMCCCAL");
        }
        return irtDaysCalculatorFactory.getCalculator(methodarg).calculateDays(begdat, enddat, "C".equals(argPERCLSFLG));
        /*int y1 = 0;
        int m1 = 0;
        int d1 = 0;
        int y2 = 0;
        int m2 = 0;
        int d2 = 0;
        int month = 0;
        int dayfirst = 0;
        int daylast = 0;
        switch (methodarg) {
            //! CAL units = calendar days
            case "CAL":
                ret = Dates.diff(enddat, begdat);
                if (MdaUtils.compareTo(argPERCLSFLG, "C") == 0) {
                    ret = ret + 1;
                }
                //! M30 unit days, every month with 30 days
                break;
            case "M30":
                y1 = Dates.getYear(begdat);
                m1 = Dates.getMonth(begdat);
                d1 = Dates.getDay(begdat);
                y2 = Dates.getYear(enddat);
                m2 = Dates.getMonth(enddat);
                d2 = Dates.getDay(enddat);
                if (y1 == y2 && m1 == m2) {
                    // begin and end in same month
                    ret = d2 - d1;
                    // if closed period, add 1 day but never return value > 30
                    if (MdaUtils.compareTo(argPERCLSFLG, "C") == 0) {
                        if (ret < 30) {
                            ret = ret + 1;
                        }
                    }
                } else {
                    // full month
                    month = (12 - m1) + (m2 - 1) + (y2 - y1 - 1) * 12;
                    // days in 1st month
                    dayfirst = 30 - d1 + 1;
                    if (dayfirst > 30) {
                        dayfirst = 30;
                    }
                    // days in last month
                    daylast = d2 - 1;
                    // if closed period, add 1 day in last month but never > 30
                    if (MdaUtils.compareTo(argPERCLSFLG, "C") == 0) {
                        if (daylast < 30) {
                            daylast = daylast + 1;
                        }
                    }
                    ret = month * 30 + dayfirst + daylast;
                }
                break;
            default:
                //ctx.setErrorCode(200, "Calculation method " + methodarg + " unknown");
                SettleExceptionUtils.sysDump("Calculation method " + methodarg + " unknown");
        }
        return ret;*/

    }

    /**
     * source:irtcal.0029.script
     */
    public BigDecimal irtmiccal(BigDecimal refamt, BigDecimal rate, BigDecimal units, String methodarg) {
        // Calculation of interest/fee amount as amount * rate/100 * units
        if (MdaUtils.compareTo(rate, 0) < 0) {
            //ctx.setErrorCode(200, "Rate must be zero or positive in IRTCAL.IRTMICCAL");
            //return new BigDecimal(0);
            SettleExceptionUtils.sysDump("Rate must be zero or positive in IRTCAL.IRTMICCAL");
        }
        BigDecimal year = null;
        BigDecimal result = null;
        BigDecimal quot = null;
        BigDecimal nv = null;
        int completeYears = 0;
        BigDecimal remainingDays = null;
        BigDecimal effrate = null;
        switch (methodarg) {
            //! 365 Floating Rate,  Year = 365
            case "365":
                year = Decimals.div(Decimals.mul(refamt, rate), 100);
                result = Decimals.div(Decimals.mul(year, units), 365);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! 360 Floating rate, Year = 360
                break;
            case "360":
                year = Decimals.div(Decimals.mul(refamt, rate), 100);
                result = Decimals.div(Decimals.mul(year, units), 360);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! F65 Fixed rate, Year = 365
                break;
            case "F65":
                year = Decimals.div(Decimals.mul(refamt, rate), 100);
                result = Decimals.div(Decimals.mul(year, units), 365);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! F60 Fixed rate, Year = 360
                break;
            case "F60":
                year = Decimals.div(Decimals.mul(refamt, rate), 100);
                result = Decimals.div(Decimals.mul(year, units), 360);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! BPQ Base Point per quarter (each call = 1 quarter)
                break;
            case "BPQ":
                year = Decimals.div(Decimals.mul(refamt, rate), 10000);
                result = Decimals.div(Decimals.mul(year, 90), 90);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! BPY Base Point per year (each call = 1 year)
                break;
            case "BPY":
                year = Decimals.div(Decimals.mul(refamt, rate), 10000);
                result = Decimals.div(Decimals.mul(year, 90), 90);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! BP3 Base Point per 90 days
                break;
            case "BP3":
                year = Decimals.div(Decimals.mul(refamt, rate), 10000);
                result = Decimals.div(Decimals.mul(year, units), 90);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! B65 Base Point per year / 365 days
                break;
            case "B65":
                year = Decimals.div(Decimals.mul(refamt, rate), 10000);
                result = Decimals.div(Decimals.mul(year, units), 365);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! B60 Base Point per year / 360 days
                break;
            case "B60":
                year = Decimals.div(Decimals.mul(refamt, rate), 10000);
                result = Decimals.div(Decimals.mul(year, units), 360);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                //! DY0      Simple Discount to Yield Year = 360 days
                break;
            case "DY0":
                quot = Numerics.round(Decimals.add(1, Decimals.div(Decimals.mul(Decimals.div(rate, 100), units), 360)), 8);
                nv = Numerics.round(Decimals.div(refamt, quot), 8);
                result = Decimals.sub(refamt, nv);
                //! DY5, D65 Simple Discount to Yield Year = 365 days
                break;
            case "DY5":
            case "D65":
                quot = Numerics.round(Decimals.add(1, Decimals.div(Decimals.mul(Decimals.div(rate, 100), units), 365)), 8);
                nv = Numerics.round(Decimals.div(refamt, quot), 8);
                result = Decimals.sub(refamt, nv);
                //! DA0      Discount to Yield, Compounded Annually, Year = 360 days
                //           =DY0 for periods up to one year
                break;
            case "DA0":
                completeYears = Numerics.toInt(Decimals.div(units, 365));
                remainingDays = Decimals.sub(units, completeYears * 365);
                quot = Numerics.round(Decimals.add(1, Decimals.div(Decimals.mul(Decimals.div(rate, 100), remainingDays), 360)), 8);
                for (int yearidx = 1; yearidx <= completeYears; yearidx++) {
                    quot = Numerics.round(Decimals.mul((Decimals.add(1, Decimals.div(Decimals.mul(Decimals.div(rate, 100), 365), 360))), quot), 8);
                }
                nv = Numerics.round(Decimals.div(refamt, quot), 8);
                result = Decimals.sub(refamt, nv);
                //! DA5      Discount to Yield, Compounded Annually, Year = 365 days
                //           =DY5 for periods up to one year
                break;
            case "DA5":
                completeYears = Numerics.toInt(Decimals.div(units, 365));
                remainingDays = Decimals.sub(units, completeYears * 365);
                quot = Numerics.round(Decimals.add(1, Decimals.div(Decimals.mul(Decimals.div(rate, 100), remainingDays), 365)), 8);
                for (int yearidx = 1; yearidx <= completeYears; yearidx++) {
                    quot = Numerics.round(Decimals.mul((Decimals.add(1, Decimals.div(rate, 100))), quot), 8);
                }
                nv = Numerics.round(Decimals.div(refamt, quot), 8);
                result = Decimals.sub(refamt, nv);
                //! DFL Discount rate, flat
                break;
            case "DFL":
                quot = Numerics.round(Decimals.add(1, Decimals.div(rate, 100)), 8); // required to prevent problems with precision of division
                effrate = Decimals.div(rate, quot);
                year = Decimals.div(Decimals.mul(refamt, effrate), 100);
                result = Decimals.mul(year, units);
                if (MdaUtils.compareTo(result, 0) < 0) {
                    result = new BigDecimal("0");
                }
                break;
            default:
                //ctx.setErrorCode(200, "IRTMIC Calculation method " + methodarg + " unknown");
                //result = new BigDecimal("0");
                SettleExceptionUtils.sysDump("IRTMIC Calculation method " + methodarg + " unknown");
        }
        return result;

    }

    /**
     * source:irtcal.0030.script
     */
    @Override
    public String getPerMetCodes(IrtcalVo irtcal) {
        return irtcal.getPermet();
    }

    /**
     * source:irtcal.0031.script
     */
    public Date getNextPeriod(Date begdat, Date lastdate, String met) {
        //! retrieves next date
        //! this function needs not to respect grace period, as this is respected on calling
        // ####### 改写 LABEL与GOTO   ####    label nextdate
        // generate next date
        int year = 0;
        int month = 0;
        Date nextdate = null;
        int day = 0;
        int lastday = 0;
        int begyear = 0;
        int begmonth = 0;
        int diffmonth = 0;
        switch (Strings.mid(met, 1, 4)) {
            case "CALM":
                // calendar month
                year = Dates.getYear(lastdate);
                month = Dates.getMonth(lastdate);
                month = month + 1;
                if (month > 12) {
                    year = year + 1;
                    month = 1;
                }
                nextdate = Strings.parseDate("01/" + MdaUtils.toString(Numerics.toInt(month), -2) + "/" + MdaUtils.toString(year, -4));
                break;
            case "CALQ":
                // calendar quarter
                year = Dates.getYear(lastdate);
                month = Dates.getMonth(lastdate);
                month = Numerics.toInt(Decimals.div((month - 1), 3)) * 3 + 1; // make month dividable by three
                month = month + 3;
                if (month > 12) {
                    year = year + 1;
                    month = 1;
                }
                nextdate = Strings.parseDate("01/" + MdaUtils.toString(Numerics.toInt(month), -2) + "/" + MdaUtils.toString(year, -4));
                break;
            case "DURM":
                // month
                year = Dates.getYear(lastdate);
                month = Dates.getMonth(lastdate);
                day = Dates.getDay(begdat);
                month = month + 1;
                if (month > 12) {
                    year = year + 1;
                    month = 1;
                }
                lastday = getLastDayofMonth(new BigDecimal(year), new BigDecimal(month));
                if (day > lastday) {
                    day = lastday;
                }
                nextdate = Strings.parseDate(MdaUtils.toString(day, -2) + "/" + MdaUtils.toString(Numerics.toInt(month), -2) + "/" + MdaUtils.toString(year, -4));
                break;
            case "DURQ":  // quarter, in case of doubt, next day
                // quarter year
                begyear = Dates.getYear(begdat);
                begmonth = Dates.getMonth(begdat);
                year = Dates.getYear(lastdate);
                month = Dates.getMonth(lastdate);
                diffmonth = year * 12 + month - (begyear * 12 + begmonth);
                diffmonth = Numerics.toInt(Decimals.div(diffmonth, 3)) * 3;
                day = Dates.getDay(begdat);
                diffmonth = diffmonth + 3;
                month = begmonth + diffmonth;
                year = begyear + Numerics.toInt(Decimals.div((month - 1), 12));
                month = month - 12 * Numerics.toInt(Decimals.div((month - 1), 12));
                lastday = getLastDayofMonth(new BigDecimal(year), new BigDecimal(month));
                if (day > lastday) {
                    day = 1;
                    month = month + 1;
                    if (month > 12) {
                        month = 1;
                        year = year + 1;
                    }
                }
                nextdate = Strings.parseDate(MdaUtils.toString(day, -2) + "/" + MdaUtils.toString(Numerics.toInt(month), -2) + "/" + MdaUtils.toString(year, -4));
                break;
            case "DURH":      // half year, in case of doubt, next day
                // half year
                begyear = Dates.getYear(begdat);
                begmonth = Dates.getMonth(begdat);
                year = Dates.getYear(lastdate);
                month = Dates.getMonth(lastdate);
                diffmonth = year * 12 + month - (begyear * 12 + begmonth);
                diffmonth = Numerics.toInt(Decimals.div(diffmonth, 6)) * 6;
                day = Dates.getDay(begdat);
                diffmonth = diffmonth + 6;
                month = begmonth + diffmonth;
                year = begyear + Numerics.toInt(Decimals.div((month - 1), 12));
                month = month - 12 * Numerics.toInt(Decimals.div((month - 1), 12));
                lastday = getLastDayofMonth(new BigDecimal(year), new BigDecimal(month));
                if (day > lastday) {
                    day = 1;
                    month = month + 1;
                    if (month > 12) {
                        month = 1;
                        year = year + 1;
                    }
                }
                nextdate = Strings.parseDate(MdaUtils.toString(day, -2) + "/" + MdaUtils.toString(Numerics.toInt(month), -2) + "/" + MdaUtils.toString(year, -4));
                break;
            case "DURY":  // year, in case of doubt, next day
                // year
                begyear = Dates.getYear(begdat);
                begmonth = Dates.getMonth(begdat);
                year = Dates.getYear(lastdate);
                month = Dates.getMonth(lastdate);
                diffmonth = year * 12 + month - (begyear * 12 + begmonth);
                diffmonth = Numerics.toInt(Decimals.div(diffmonth, 12)) * 12;
                day = Dates.getDay(begdat);
                diffmonth = diffmonth + 12;
                month = begmonth + diffmonth;
                year = begyear + Numerics.toInt(Decimals.div((month - 1), 12));
                month = month - 12 * Numerics.toInt(Decimals.div((month - 1), 12));
                lastday = getLastDayofMonth(new BigDecimal(year), new BigDecimal(month));
                if (day > lastday) {
                    day = 1;
                    month = month + 1;
                    if (month > 12) {
                        month = 1;
                        year = year + 1;
                    }
                }
                nextdate = Strings.parseDate(MdaUtils.toString(day, -2) + "/" + MdaUtils.toString(Numerics.toInt(month), -2) + "/" + MdaUtils.toString(year, -4));
                break;
            case "DUPQ":  // quarter, in case of doubt, last day before
                // quarter year
                year = Dates.getYear(lastdate);
                month = Dates.getMonth(lastdate);
                day = Dates.getDay(begdat);
                // $month = Int( $month - 1 ) / 3 * 3 + 1   # do not make month dividable by three
                month = month + 3;
                if (month > 12) {
                    year = year + 1;
                    month = month - 12;
                }
                lastday = getLastDayofMonth(new BigDecimal(year), new BigDecimal(month));
                if (day > lastday) {
                    day = lastday;
                }
                nextdate = Strings.parseDate(MdaUtils.toString(day, -2) + "/" + MdaUtils.toString(Numerics.toInt(month), -2) + "/" + MdaUtils.toString(year, -4));
                break;
            default:
                //ctx.setErrorCode(1000, Strings.formatText("Method $1 not defined in IRTCAL.GetNextPeriod.", met));
                //return NULLDATE;
                SettleExceptionUtils.sysDump(Strings.formatText("Method $1 not defined in IRTCAL.GetNextPeriod.", met));
        }
        return nextdate;

    }

    /**
     * source:irtcal.0032.script
     */
    @Override
    public void irtGrdsAdjustForShow(IrtcalVo irtcal) {
        // it`s recomended to remove adjustments but some want to show closed intervals...
        int irdSize = irtcal.getIrdele().size();
        int irpSize = irtcal.getIrpele().size();
        if ("C".equals(irtcal.getPerclsflg())) {
            irdSize = irdSize - 1;
            irpSize = irpSize - 1;
        }
        for (int i = 0; i < irdSize; i++) {
            irtcal.getIrdele().get(i).setEnddat(Dates.add(irtcal.getIrdele().get(i).getEnddat(), -1));
        }
        for (int i = 1; i <= irpSize; i++) {
            irtcal.getIrpele().get(i).setEnddat(Dates.add(irtcal.getIrpele().get(i).getEnddat(), -1));
        }
    }

    /**
     * source:irtcal.0033.script
     */
    @Override
    public BigDecimal calcAllCur(IrtcalVo irtcal, String argNewCur, BigDecimal argMinAmt, BigDecimal argMaxAmt) {
        //! Calculate commision amount converting amounts before applying minimum and maximum
        //! ArgNewCur : Currency in which the amount is to be calculated
        //! ArgMinAmt : Minimum amount to be used (in ArgNewCur)
        //! ArgMaxAmt : Maximum amount to be used (in ArgNewCur)
        calcIrd(irtcal);
        // converting IRD-amounts to target currency
        if (MdaUtils.compareTo(irtcal.getIrpcur(), argNewCur) != 0) {
            irtcal.getIrdele().forEach(irdeleVo ->
                    {
                        irdeleVo.setBasamt(xrtmodService.amtConvTypes(irtcal.getIrpcur(), irdeleVo.getBasamt(), "M", argNewCur, "M", Dates.today()));
                        irdeleVo.setAmt(xrtmodService.amtConvTypes(irtcal.getIrpcur(), irdeleVo.getAmt(), "M", argNewCur, "M", Dates.today()));
                    }
            );
            irtcal.setIrpcur(argNewCur);
        }
        // set minimum/maximum
        BigDecimal min = null;
        if (MdaUtils.compareTo(argMinAmt, 0) <= 0) {
            min = new BigDecimal(-1);
        } else {
            min = argMinAmt;
        }
        BigDecimal max = null;
        if (MdaUtils.compareTo(argMaxAmt, 0) <= 0) {
            max = new BigDecimal(-1);
        } else {
            max = argMaxAmt;
        }
        setMinMax(irtcal, min, max);
        // caclulate portions
        calcIrp(irtcal);
        BigDecimal res = new BigDecimal("0");
        for (int idx = 0; idx < irtcal.getIrpele().size(); idx++) {
            res = res.add(irtcal.getIrpele().get(idx).getAmt());
        }
        return res;
    }

}
