/*
 * SDK Pullenti Lingvo, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software.
 * This class is generated using the converter Unisharping (www.unisharping.ru) from Pullenti C#.NET project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

const Utils = require("./../../../unisharp/Utils");
const RefOutArgWrapper = require("./../../../unisharp/RefOutArgWrapper");

const Token = require("./../../Token");
const DateExTokenDateExItemTokenType = require("./DateExTokenDateExItemTokenType");
const ProcessorService = require("./../../ProcessorService");
const DatePointerType = require("./../DatePointerType");
const Referent = require("./../../Referent");
const DateReferent = require("./../DateReferent");
const DateRangeReferent = require("./../DateRangeReferent");
const DateExToken = require("./DateExToken");
const ReferentToken = require("./../../ReferentToken");

class DateRelHelper {
    
    static createReferents(et) {
        if (!et.isDiap || et.itemsTo.length === 0) {
            let li = DateRelHelper._createRefs(et.itemsFrom);
            if (li === null || li.length === 0) 
                return null;
            return li;
        }
        let liFr = DateRelHelper._createRefs(et.itemsFrom);
        let liTo = DateRelHelper._createRefs(et.itemsTo);
        let ra = new DateRangeReferent();
        if (liFr.length > 0) 
            ra.dateFrom = Utils.as(liFr[0].tag, DateReferent);
        if (liTo.length > 0) 
            ra.dateTo = Utils.as(liTo[0].tag, DateReferent);
        let res = new Array();
        res.splice(res.length, 0, ...liFr);
        res.splice(res.length, 0, ...liTo);
        res.push(new ReferentToken(ra, et.beginToken, et.endToken));
        if (res.length === 0) 
            return null;
        res[0].tag = ra;
        return res;
    }
    
    static _createRefs(its) {
        let res = new Array();
        let own = null;
        for (let i = 0; i < its.length; i++) {
            let it = its[i];
            let d = new DateReferent();
            if (it.isValueRelate) 
                d.isRelative = true;
            if (own !== null) 
                d.higher = own;
            if (it.typ === DateExTokenDateExItemTokenType.DAY) {
                d.day = it.value;
                if (it.isLast && ((it.value === 0 || it.value === -1)) && i > 0) {
                    let it0 = its[i - 1];
                    let day = 0;
                    if (it0.typ === DateExTokenDateExItemTokenType.MONTH && !it0.isValueRelate) {
                        let m = d.month;
                        if (((m === 1 || m === 3 || m === 5) || m === 7 || m === 8) || m === 10 || m === 12) 
                            day = 31;
                        else if (m === 2) 
                            day = 28;
                        else if (m > 0) 
                            day = 30;
                    }
                    else if (it0.typ === DateExTokenDateExItemTokenType.QUARTAL && !it0.isValueRelate) {
                        let m = 1 + (((it0.value - 1)) * 4);
                        let dm = new DateReferent();
                        dm.month = m;
                        if (own !== null) 
                            dm.higher = own;
                        res.push(new ReferentToken(dm, it.beginToken, it.endToken));
                        own = d.higher = dm;
                        if (((m === 1 || m === 3 || m === 5) || m === 7 || m === 8) || m === 10 || m === 12) 
                            day = 31;
                        else if (m === 2) 
                            day = 28;
                        else if (m > 0) 
                            day = 30;
                    }
                    else if (it0.typ === DateExTokenDateExItemTokenType.YEAR) {
                        let dm = new DateReferent();
                        dm.month = 12;
                        if (own !== null) 
                            dm.higher = own;
                        res.push(new ReferentToken(dm, it.beginToken, it.endToken));
                        own = d.higher = dm;
                        day = 31;
                    }
                    else if (it0.typ === DateExTokenDateExItemTokenType.CENTURY) {
                        let dy = new DateReferent();
                        dy.year = 99;
                        dy.isRelative = true;
                        if (own !== null) 
                            dy.higher = own;
                        res.push(new ReferentToken(dy, it.beginToken, it.endToken));
                        own = dy;
                        let dm = new DateReferent();
                        dm.month = 12;
                        dm.higher = own;
                        res.push(new ReferentToken(dm, it.beginToken, it.endToken));
                        own = d.higher = dm;
                        day = 31;
                    }
                    if ((day + it.value) > 0) {
                        d.isRelative = false;
                        d.day = day + it.value;
                    }
                }
            }
            else if (it.typ === DateExTokenDateExItemTokenType.DAYOFWEEK) 
                d.dayOfWeek = it.value;
            else if (it.typ === DateExTokenDateExItemTokenType.HOUR) {
                d.hour = it.value;
                if (((i + 1) < its.length) && its[i + 1].typ === DateExTokenDateExItemTokenType.MINUTE && !its[i + 1].isValueRelate) {
                    d.minute = its[i + 1].value;
                    i++;
                }
            }
            else if (it.typ === DateExTokenDateExItemTokenType.MINUTE) 
                d.minute = it.value;
            else if (it.typ === DateExTokenDateExItemTokenType.MONTH) {
                d.month = it.value;
                if (it.isLast && ((it.value === 0 || it.value === -1)) && i > 0) {
                    let it0 = its[i - 1];
                    let m = 0;
                    if (it0.typ === DateExTokenDateExItemTokenType.QUARTAL && !it0.isValueRelate) 
                        m = 1 + (((it0.value - 1)) * 4) + it.value;
                    else if (it0.typ === DateExTokenDateExItemTokenType.YEAR || it0.typ === DateExTokenDateExItemTokenType.DECADE || it0.typ === DateExTokenDateExItemTokenType.CENTURY) 
                        m = 12 + it.value;
                    if (m > 0) {
                        d.isRelative = false;
                        d.month = m;
                    }
                }
            }
            else if (it.typ === DateExTokenDateExItemTokenType.QUARTAL) 
                d.quartal = it.value;
            else if (it.typ === DateExTokenDateExItemTokenType.SEASON) 
                d.season = it.value;
            else if (it.typ === DateExTokenDateExItemTokenType.WEEK) 
                d.week = it.value;
            else if (it.typ === DateExTokenDateExItemTokenType.HALFYEAR) 
                d.halfyear = (it.isLast ? 2 : it.value);
            else if (it.typ === DateExTokenDateExItemTokenType.YEAR) 
                d.year = it.value;
            else if (it.typ === DateExTokenDateExItemTokenType.CENTURY) 
                d.century = it.value;
            else if (it.typ === DateExTokenDateExItemTokenType.DECADE) 
                d.decade = it.value;
            else 
                continue;
            res.push(new ReferentToken(d, it.beginToken, it.endToken));
            own = d;
            it.src = d;
        }
        if (res.length > 0) 
            res[0].tag = own;
        return res;
    }
    
    static _createDateEx(dr) {
        let res = new Array();
        for (; dr !== null; dr = dr.higher) {
            let n = 0;
            for (const s of dr.slots) {
                let tt = new Token(null, 0, 1);
                let it = DateExToken.DateExItemToken._new891(tt, tt, DateExTokenDateExItemTokenType.UNDEFINED);
                if (dr.getStringValue(DateReferent.ATTR_ISRELATIVE) === "true") 
                    it.isValueRelate = true;
                if (s.typeName === DateReferent.ATTR_YEAR) {
                    it.typ = DateExTokenDateExItemTokenType.YEAR;
                    let wrapn892 = new RefOutArgWrapper();
                    let inoutres893 = Utils.tryParseInt(Utils.asString(s.value), wrapn892);
                    n = wrapn892.value;
                    if (inoutres893) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_DECADE) {
                    it.typ = DateExTokenDateExItemTokenType.DECADE;
                    let wrapn894 = new RefOutArgWrapper();
                    let inoutres895 = Utils.tryParseInt(Utils.asString(s.value), wrapn894);
                    n = wrapn894.value;
                    if (inoutres895) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_CENTURY) {
                    it.typ = DateExTokenDateExItemTokenType.CENTURY;
                    let wrapn896 = new RefOutArgWrapper();
                    let inoutres897 = Utils.tryParseInt(Utils.asString(s.value), wrapn896);
                    n = wrapn896.value;
                    if (inoutres897) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_HALFYEAR) {
                    it.typ = DateExTokenDateExItemTokenType.HALFYEAR;
                    let wrapn898 = new RefOutArgWrapper();
                    let inoutres899 = Utils.tryParseInt(Utils.asString(s.value), wrapn898);
                    n = wrapn898.value;
                    if (inoutres899) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_QUARTAL) {
                    it.typ = DateExTokenDateExItemTokenType.QUARTAL;
                    let wrapn900 = new RefOutArgWrapper();
                    let inoutres901 = Utils.tryParseInt(Utils.asString(s.value), wrapn900);
                    n = wrapn900.value;
                    if (inoutres901) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_SEASON) {
                    it.typ = DateExTokenDateExItemTokenType.SEASON;
                    let wrapn902 = new RefOutArgWrapper();
                    let inoutres903 = Utils.tryParseInt(Utils.asString(s.value), wrapn902);
                    n = wrapn902.value;
                    if (inoutres903) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_MONTH) {
                    it.typ = DateExTokenDateExItemTokenType.MONTH;
                    let wrapn904 = new RefOutArgWrapper();
                    let inoutres905 = Utils.tryParseInt(Utils.asString(s.value), wrapn904);
                    n = wrapn904.value;
                    if (inoutres905) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_WEEK) {
                    it.typ = DateExTokenDateExItemTokenType.WEEK;
                    let wrapn906 = new RefOutArgWrapper();
                    let inoutres907 = Utils.tryParseInt(Utils.asString(s.value), wrapn906);
                    n = wrapn906.value;
                    if (inoutres907) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_DAYOFWEEK) {
                    it.typ = DateExTokenDateExItemTokenType.DAYOFWEEK;
                    let wrapn908 = new RefOutArgWrapper();
                    let inoutres909 = Utils.tryParseInt(Utils.asString(s.value), wrapn908);
                    n = wrapn908.value;
                    if (inoutres909) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_DAY) {
                    it.typ = DateExTokenDateExItemTokenType.DAY;
                    let wrapn910 = new RefOutArgWrapper();
                    let inoutres911 = Utils.tryParseInt(Utils.asString(s.value), wrapn910);
                    n = wrapn910.value;
                    if (inoutres911) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_HOUR) {
                    it.typ = DateExTokenDateExItemTokenType.HOUR;
                    let wrapn912 = new RefOutArgWrapper();
                    let inoutres913 = Utils.tryParseInt(Utils.asString(s.value), wrapn912);
                    n = wrapn912.value;
                    if (inoutres913) 
                        it.value = n;
                }
                else if (s.typeName === DateReferent.ATTR_MINUTE) {
                    it.typ = DateExTokenDateExItemTokenType.MINUTE;
                    let wrapn914 = new RefOutArgWrapper();
                    let inoutres915 = Utils.tryParseInt(Utils.asString(s.value), wrapn914);
                    n = wrapn914.value;
                    if (inoutres915) 
                        it.value = n;
                }
                if (it.typ !== DateExTokenDateExItemTokenType.UNDEFINED) 
                    res.splice(0, 0, it);
            }
        }
        // PYTHON: sort(key=attrgetter('typ'))
        res.sort((a, b) => a.compareTo(b));
        return res;
    }
    
    static calculateDate(dr, now, tense, fromto) {
        if (dr.pointer === DatePointerType.TODAY) 
            return now;
        if (!dr.isRelative) {
            let year = dr.year;
            if (year < 1) 
                return null;
            let mon = dr.month;
            if (mon < 1) 
                mon = (fromto === 1 ? 12 : 1);
            let day = dr.day;
            if (day < 1) 
                day = (fromto === 1 ? 31 : 1);
            try {
                return new Date(year, mon - 1, day, 0, 0, 0);
            } catch (ex916) {
            }
            return null;
        }
        let det = new DateExToken(null, null);
        det.itemsFrom = DateRelHelper._createDateEx(dr);
        return det.getDate(now, tense);
    }
    
    static calculateDateRange(dr, now, from, to, tense) {
        if (dr.pointer === DatePointerType.TODAY) {
            from.value = now;
            to.value = now;
            return true;
        }
        if (!dr.isRelative && dr.dt !== null) {
            from.value = (to.value = dr.dt);
            return true;
        }
        let t = new Token(null, 0, 1);
        let det = new DateExToken(t, t);
        det.itemsFrom = DateRelHelper._createDateEx(dr);
        let inoutres917 = det.getDates(now, from, to, tense);
        return inoutres917;
    }
    
    static calculateDateRange2(dr, now, from, to, tense) {
        from.value = Utils.MIN_DATE;
        to.value = Utils.MAX_DATE;
        let dt0 = null;
        let dt1 = null;
        if (dr.dateFrom === null) {
            if (dr.dateTo === null) 
                return false;
            let wrapdt0918 = new RefOutArgWrapper();
            let wrapdt1919 = new RefOutArgWrapper();
            let inoutres920 = DateRelHelper.calculateDateRange(dr.dateTo, now, wrapdt0918, wrapdt1919, tense);
            dt0 = wrapdt0918.value;
            dt1 = wrapdt1919.value;
            if (!inoutres920) 
                return false;
            to.value = dt1;
            return true;
        }
        else if (dr.dateTo === null) {
            let wrapdt0921 = new RefOutArgWrapper();
            let wrapdt1922 = new RefOutArgWrapper();
            let inoutres923 = DateRelHelper.calculateDateRange(dr.dateFrom, now, wrapdt0921, wrapdt1922, tense);
            dt0 = wrapdt0921.value;
            dt1 = wrapdt1922.value;
            if (!inoutres923) 
                return false;
            from.value = dt0;
            return true;
        }
        let wrapdt0927 = new RefOutArgWrapper();
        let wrapdt1928 = new RefOutArgWrapper();
        let inoutres929 = DateRelHelper.calculateDateRange(dr.dateFrom, now, wrapdt0927, wrapdt1928, tense);
        dt0 = wrapdt0927.value;
        dt1 = wrapdt1928.value;
        if (!inoutres929) 
            return false;
        from.value = dt0;
        let dt2 = null;
        let dt3 = null;
        let wrapdt2924 = new RefOutArgWrapper();
        let wrapdt3925 = new RefOutArgWrapper();
        let inoutres926 = DateRelHelper.calculateDateRange(dr.dateTo, now, wrapdt2924, wrapdt3925, tense);
        dt2 = wrapdt2924.value;
        dt3 = wrapdt3925.value;
        if (!inoutres926) 
            return false;
        to.value = dt3;
        return true;
    }
    
    static appendToString(dr, res) {
        let dt0 = null;
        let dt1 = null;
        let cur = (ProcessorService.DEBUG_CURRENT_DATE_TIME === null ? Utils.now() : ProcessorService.DEBUG_CURRENT_DATE_TIME);
        let wrapdt0930 = new RefOutArgWrapper();
        let wrapdt1931 = new RefOutArgWrapper();
        let inoutres932 = DateRelHelper.calculateDateRange(dr, cur, wrapdt0930, wrapdt1931, 0);
        dt0 = wrapdt0930.value;
        dt1 = wrapdt1931.value;
        if (!inoutres932) 
            return;
        DateRelHelper._appendDates(cur, dt0, dt1, res);
    }
    
    static appendToString2(dr, res) {
        let dt0 = null;
        let dt1 = null;
        let cur = (ProcessorService.DEBUG_CURRENT_DATE_TIME === null ? Utils.now() : ProcessorService.DEBUG_CURRENT_DATE_TIME);
        let wrapdt0933 = new RefOutArgWrapper();
        let wrapdt1934 = new RefOutArgWrapper();
        let inoutres935 = DateRelHelper.calculateDateRange2(dr, cur, wrapdt0933, wrapdt1934, 0);
        dt0 = wrapdt0933.value;
        dt1 = wrapdt1934.value;
        if (!inoutres935) 
            return;
        DateRelHelper._appendDates(cur, dt0, dt1, res);
    }
    
    static _appendDates(cur, dt0, dt1, res) {
        let mon0 = Utils.getMonth(dt0);
        res.append(" (").append(dt0.getFullYear()).append(".").append(Utils.correctToString((mon0).toString(10), 2, true)).append(".").append(Utils.correctToString((dt0.getDate()).toString(10), 2, true));
        if (dt0.getHours() > 0 || dt0.getMinutes() > 0) 
            res.append(" ").append(Utils.correctToString((dt0.getHours()).toString(10), 2, true)).append(":").append(Utils.correctToString((dt0.getMinutes()).toString(10), 2, true));
        if (dt0 !== dt1) {
            let mon1 = Utils.getMonth(dt1);
            res.append("-").append(dt1.getFullYear()).append(".").append(Utils.correctToString((mon1).toString(10), 2, true)).append(".").append(Utils.correctToString((dt1.getDate()).toString(10), 2, true));
            if (dt1.getHours() > 0 || dt1.getMinutes() > 0) 
                res.append(" ").append(Utils.correctToString((dt1.getHours()).toString(10), 2, true)).append(":").append(Utils.correctToString((dt1.getMinutes()).toString(10), 2, true));
        }
        let monc = Utils.getMonth(cur);
        res.append(" отн. ").append(cur.getFullYear()).append(".").append(Utils.correctToString((monc).toString(10), 2, true)).append(".").append(Utils.correctToString((cur.getDate()).toString(10), 2, true));
        if (cur.getHours() > 0 || cur.getMinutes() > 0) 
            res.append(" ").append(Utils.correctToString((cur.getHours()).toString(10), 2, true)).append(":").append(Utils.correctToString((cur.getMinutes()).toString(10), 2, true));
        res.append(")");
    }
}


module.exports = DateRelHelper