"use strict";
// -*- mode: typescript; indent-tabs-mode: nil; js-basic-offset: 4 -*-
//
// This file is part of Genie
//
// Copyright 2019-2021 The Board of Trustees of the Leland Stanford Junior University
//           2019 National Taiwan University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Giovanni Campagna <gcampagn@cs.stanford.edu>
//         Elvis Yu-Jing Lin <r06922068@ntu.edu.tw> <elvisyjlin@gmail.com>
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.interrogativePronoun = exports.isTimeEntity = exports.isLocationEntity = exports.isHumanEntity = exports.makeDateRangeFilter = exports.makeAndFilter = exports.makeFilter = exports.typeToStringSafe = exports.isSameFunction = exports.makeDomainIndependentFilter = exports.makeInputParamSlot = void 0;
const assert_1 = __importDefault(require("assert"));
const thingtalk_1 = require("thingtalk");
function typeToStringSafe(type) {
    if (type instanceof thingtalk_1.Type.Array)
        return 'Array__' + typeToStringSafe(type.elem);
    else if (type instanceof thingtalk_1.Type.Entity)
        return 'Entity__' + type.type.replace(':', '__');
    else if (type instanceof thingtalk_1.Type.Measure)
        return 'Measure_' + type.unit;
    else if (type instanceof thingtalk_1.Type.Enum)
        return 'Enum__' + type.entries.join('__');
    else
        return String(type);
}
exports.typeToStringSafe = typeToStringSafe;
function makeInputParamSlot(slot, value, tpLoader) {
    const vtype = value.getType();
    const ptype = slot.type;
    if (!thingtalk_1.Type.isAssignable(ptype, vtype, {}, tpLoader.entitySubTypeMap))
        return null;
    return { schema: slot.schema, ptype: slot.type,
        ast: new thingtalk_1.Ast.InputParam(null, slot.name, value) };
}
exports.makeInputParamSlot = makeInputParamSlot;
function makeDomainIndependentFilter(pname, op, value) {
    return { schema: null, ptype: value.getType(),
        ast: new thingtalk_1.Ast.AtomBooleanExpression(null, pname, op, value, null) };
}
exports.makeDomainIndependentFilter = makeDomainIndependentFilter;
function makeFilter(tpLoader, slot, op, value, negate = false) {
    const vtype = value.getType();
    const ptype = slot.type;
    // XXX url filters?
    if (ptype instanceof thingtalk_1.Type.Entity && ptype.type === 'tt:url')
        return null;
    if (op === 'contains') {
        if (ptype === thingtalk_1.Type.RecurrentTimeSpecification) {
            if (!(vtype.isTime || vtype.isDate))
                return null;
        }
        else {
            if (!(ptype instanceof thingtalk_1.Type.Array))
                return null;
            const elem = ptype.elem;
            if ((vtype.isEnum && elem.isEnum) || (vtype.isEntity && elem.isEntity)) {
                if (!thingtalk_1.Type.isAssignable(vtype, elem, tpLoader.entitySubTypeMap))
                    return null;
            }
            else if (!elem.equals(vtype)) {
                return null;
            }
        }
        if (vtype.isString)
            op = 'contains~';
    }
    else if (op === 'in_array') {
        if (vtype.equals(new thingtalk_1.Type.Array(thingtalk_1.Type.String)) && ptype.isEntity)
            op = 'in_array~';
        else if (!vtype.equals(new thingtalk_1.Type.Array(ptype)))
            return null;
    }
    else {
        // note: we need to use "isAssignable" instead of "equals" here
        // to handle enums and entities correctly
        if ((vtype.isEnum && ptype.isEnum) || (vtype.isEntity && ptype.isEntity)) {
            if (!thingtalk_1.Type.isAssignable(vtype, ptype, tpLoader.entitySubTypeMap))
                return null;
        }
        else if (!ptype.equals(vtype)) {
            return null;
        }
        if (op === '==' && vtype.isString)
            op = '=~';
    }
    let ast = new thingtalk_1.Ast.BooleanExpression.Atom(null, slot.name, op, value);
    if (negate)
        ast = new thingtalk_1.Ast.BooleanExpression.Not(null, ast);
    return { schema: slot.schema, ptype, ast };
}
exports.makeFilter = makeFilter;
function makeAndFilter(tpLoader, slot, op, values, negate = false) {
    if (values.length !== 2)
        return null;
    if (values[0].equals(values[1]))
        return null;
    const operands = [
        makeFilter(tpLoader, slot, op, values[0]),
        makeFilter(tpLoader, slot, op, values[1])
    ];
    if (operands[0] === null || operands[1] === null)
        return null;
    let ast = new thingtalk_1.Ast.BooleanExpression.And(null, [operands[0].ast, operands[1].ast]);
    if (negate)
        ast = new thingtalk_1.Ast.BooleanExpression.Not(null, ast);
    return { schema: slot.schema, ptype: slot.type, ast };
}
exports.makeAndFilter = makeAndFilter;
function makeDateRangeFilter(tpLoader, slot, values) {
    if (values.length !== 2)
        return null;
    const operands = [
        makeFilter(tpLoader, slot, '>=', values[0]),
        makeFilter(tpLoader, slot, '<=', values[1])
    ];
    if (operands[0] === null || operands[1] === null)
        return null;
    const ast = new thingtalk_1.Ast.BooleanExpression.And(null, [operands[0].ast, operands[1].ast]);
    return { schema: slot.schema, ptype: slot.type, ast };
}
exports.makeDateRangeFilter = makeDateRangeFilter;
function isHumanEntity(type) {
    if (type instanceof thingtalk_1.Type.Entity)
        return isHumanEntity(type.type);
    if (type instanceof thingtalk_1.Type.Array)
        return isHumanEntity(type.elem);
    if (typeof type !== 'string')
        return false;
    if (['tt:contact', 'tt:username', 'org.wikidata:human'].includes(type))
        return true;
    if (type.startsWith('org.schema') && type.endsWith(':Person'))
        return true;
    return false;
}
exports.isHumanEntity = isHumanEntity;
function isLocationEntity(type) {
    if (type === thingtalk_1.Type.Location)
        return true;
    if (type instanceof thingtalk_1.Type.Array)
        return isLocationEntity(type.elem);
    // FIXME: other types that can be asked by "where" question (e.g., organization)
    return false;
}
exports.isLocationEntity = isLocationEntity;
function isTimeEntity(type) {
    if (type.isDate)
        return true;
    if (type.isTime)
        return true;
    if (type.isRecurrentTimeSpecification)
        return true;
    return false;
}
exports.isTimeEntity = isTimeEntity;
function interrogativePronoun(type) {
    if (isHumanEntity(type))
        return 'who';
    if (isLocationEntity(type))
        return 'where';
    if (isTimeEntity(type))
        return 'when';
    // FIXME: other interrogative pronouns (e.g., "how" for health condition, "how much" for price)
    return 'what';
}
exports.interrogativePronoun = interrogativePronoun;
function isSameFunction(fndef1, fndef2) {
    assert_1.default(fndef1);
    assert_1.default(fndef2);
    if (fndef1 === fndef2)
        return true;
    return fndef1.qualifiedName === fndef2.qualifiedName;
}
exports.isSameFunction = isSameFunction;
//# sourceMappingURL=utils.js.map