"use strict";
// -*- mode: typescript; indent-tabs-mode: nil; js-basic-offset: 4 -*-
//
// This file is part of Genie
//
// Copyright 2019-2020 The Board of Trustees of the Leland Stanford Junior 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>
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FORMAT_TYPES = exports.BaseFormattedObject = exports.isNull = void 0;
const string_interp_1 = __importDefault(require("string-interp"));
const thingtalk_1 = require("thingtalk");
const I18n = __importStar(require("../../i18n"));
function isNull(value) {
    // all false-y values except false itself are "null"
    if (value === undefined || value === null || value === '' || Number.isNaN(value))
        return true;
    // empty arrays are "null"
    if (Array.isArray(value) && value.length === 0)
        return true;
    // invalid dates are "null"
    if (value instanceof Date && isNaN(+value))
        return true;
    return false;
}
exports.isNull = isNull;
/**
 * Namespace for format objects.
 *
 * Classes in this namespace are not accessible directly, but objects
 * of this classes are returned by {@link Formatter} methods.
 *
 * @name FormatObjects
 * @namespace
 */
/**
 * The base class of all formatting objects.
 *
 * Formatting objects are created from spec objects provided in the `#_[formatted]`
 * function annotation.
 */
class BaseFormattedObject {
    /**
     * Replace all placeholders in this object, using the provided structured result.
     *
     * @param {Formatter} formatter - the formatter to use for replacement
     * @param {Object.<string,any>} argMap - the structured ThingTalk result with the values to substitute
     */
    replaceParameters(formatter, argMap) {
        for (const key in this) {
            if (key === 'type')
                continue;
            this[key] = formatter.replaceInString(this[key], argMap);
        }
    }
}
exports.BaseFormattedObject = BaseFormattedObject;
function localeCompat(locale) {
    return [I18n.get(locale).gettext, locale];
}
/**
 * A simple still picture.
 *
 * @alias FormatObjects~Picture
 * @extends FormatObjects~FormattedObject
 */
class Picture extends BaseFormattedObject {
    /**
     * Construct a new picture object.
     *
     * @param {Object} spec
     * @param {string} spec.url - the URL of the picture to display
     */
    constructor(spec) {
        super();
        /**
         * A string identifying the type of this formatted object. Always the value `picture`.
         *
         * @readonly
         * @type {string}
         */
        this.type = 'picture';
        this.url = spec.url;
    }
    isValid() {
        return !isNull(this.url);
    }
    toLocaleString(locale) {
        const [_, localestr] = localeCompat(locale);
        return string_interp_1.default(_("Picture: ${url}"), {
            url: this.url
        }, { locale: localestr }) || '';
    }
}
/**
 * A rich deep link (also known as a card).
 *
 * An RDL is expected to be displayed as a clickable card with optional
 * description and picture.
 *
 * @alias FormatObjects~RDL
 * @extends FormatObjects~FormattedObject
 */
class RDL extends BaseFormattedObject {
    /**
     * Construct a new RDL
     *
     * If displayTitle is unspecified but displayText is, displayText is moved to displayTitle.
     * If callback is not specified, it is set to the same value as webCallback.
     *
     * @param {Object} spec
     * @param {string} spec.displayTitle - the title of the link
     * @param {string} [spec.displayText] - the description associated with the link
     * @param {string} spec.webCallback - the link target
     * @param {string} [spec.callback] - a different link target, to use on plaforms where deep-linking is allowed (e.g. Android)
     * @param {string} [spec.pictureUrl] - a picture associated with this link
     */
    constructor(spec) {
        super();
        /**
         * A string identifying the type of this formatted object. Always the value `rdl`.
         *
         * @readonly
         * @type {string}
         */
        this.type = 'rdl';
        this.callback = spec.callback;
        this.webCallback = spec.webCallback;
        this.displayTitle = spec.displayTitle;
        this.displayText = spec.displayText;
        this.pictureUrl = spec.pictureUrl;
    }
    replaceParameters(formatter, argMap) {
        super.replaceParameters(formatter, argMap);
        if (!this.webCallback && this.callback)
            this.webCallback = this.callback;
        if (!this.callback && this.webCallback)
            this.callback = this.webCallback;
        if (!this.displayTitle && this.displayText) {
            this.displayTitle = this.displayText;
            this.displayText = undefined;
        }
        if (!this.displayTitle)
            this.displayTitle = this.webCallback;
        if (!this.pictureUrl)
            this.pictureUrl = undefined;
    }
    isValid() {
        return !isNull(this.webCallback);
    }
    toLocaleString(locale) {
        const [_, localestr] = localeCompat(locale);
        return string_interp_1.default(_("Link: ${title} <${link}>"), {
            title: this.displayTitle,
            link: this.webCallback
        }, { locale: localestr }) || '';
    }
}
/**
 * A map, with a single pin at the specified location (indicated as latitude, longitude)
 *
 * Whether the map is interactive (can be panned, zoomed) is implementation-dependent.
 *
 * The name `MapFO` is chosen to avoid confusion by JavaScript's builtin
 * [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
 * data structure.
 *
 * @alias FormatObjects~MapFO
 * @extends FormatObjects~FormattedObject
 */
class MapFO extends BaseFormattedObject {
    /**
     * Construct a new map format object.
     *
     * @param {Object} spec
     * @param {string|number} spec.lat - latitude; this can be a string with placeholders, or a number
     * @param {string|number} spec.lon - longitude; this can be a string with placeholders, or a number
     * @param {string} [spec.display] - a label for the pin (the name of the location being selected)
     */
    constructor(spec) {
        super();
        /**
         * A string identifying the type of this formatted object. Always the value `map`.
         *
         * @readonly
         * @type {string}
         */
        this.type = 'map';
        this.lat = spec.lat;
        this.lon = spec.lon;
        this.display = spec.display;
    }
    replaceParameters(formatter, argMap) {
        super.replaceParameters(formatter, argMap);
        this.lat = Number(this.lat);
        this.lon = Number(this.lon);
    }
    isValid() {
        return !isNull(this.lat) && !isNull(this.lon);
    }
    toLocaleString(locale) {
        const [_, localestr] = localeCompat(locale);
        return string_interp_1.default(_("Location: ${location}"), {
            location: new thingtalk_1.Builtin.Location(this.lat, this.lon, this.display)
        }, { locale: localestr }) || '';
    }
}
/**
 * A short notification sound from a predefined library.
 *
 * @alias FormatObjects~SoundEffect
 * @extends FormatObjects~FormattedObject
*/
class SoundEffect extends BaseFormattedObject {
    /**
     * Construct a new sound effect object.
     *
     * @param {Object} spec
     * @param {string} spec.name - the name of the sound, from the {@link http://0pointer.de/public/sound-theme-spec.html|Freedesktop Sound Theme Spec}
     *                             (with a couple Almond-specific extensions)
     */
    constructor(spec) {
        super();
        /**
         * A string identifying the type of this formatted object. Always the value `sound`.
         *
         * @readonly
         * @type {string}
         */
        this.type = 'sound';
        this.name = spec.name;
    }
    isValid() {
        return !isNull(this.name);
    }
    toLocaleString(locale) {
        const [_, localestr] = localeCompat(locale);
        return string_interp_1.default(_("Sound effect: ${name}"), {
            name: this.name
        }, { locale: localestr }) || '';
    }
}
/**
 * Audio/video display with controls
 *
 * @alias FormatObjects~Media
 * @extends FormatObjects~FormattedObject
*/
class Media extends BaseFormattedObject {
    /**
     * Construct a new media object.
     *
     * Whether the URL is audio or video will be identified
     * based on Content-Type, URL patterns and potentially
     * file extension.
     *
     * @param {Object} spec
     * @param {string} spec.url - the URL of the music/video to display
     */
    constructor(spec) {
        super();
        /**
         * A string identifying the type of this formatted object. Always the value `media`.
         *
         * @readonly
         * @type {string}
         */
        this.type = 'media';
        this.url = spec.url;
    }
    isValid() {
        return !isNull(this.url);
    }
    toLocaleString(locale) {
        const [_, localestr] = localeCompat(locale);
        return string_interp_1.default(_("Media: ${url}"), {
            url: this.url
        }, { locale: localestr }) || '';
    }
}
/**
 * A button that maps to a specific ThingTalk program, suggested to the user.
*/
class Button extends BaseFormattedObject {
    /**
     * Construct a new button object.
     *
     * @param {Object} spec
     * @param {string} spec.title - the title of the button to show to the user
     * @param {string} spec.code - the associated ThingTalk code (which can be a control command or a program)
     */
    constructor(spec) {
        super();
        /**
         * A string identifying the type of this formatted object. Always the value `media`.
         *
         * @readonly
         * @type {string}
         */
        this.type = 'button';
        this.title = spec.title;
        this.code = spec.code;
    }
    isValid() {
        return !isNull(this.title) && !isNull(this.code);
    }
    toLocaleString(locale) {
        const [_, localestr] = localeCompat(locale);
        return string_interp_1.default(_("Suggestion: ${title}"), {
            title: this.title
        }, { locale: localestr }) || '';
    }
}
exports.FORMAT_TYPES = {
    'picture': Picture,
    'rdl': RDL,
    'map': MapFO,
    'sound': SoundEffect,
    'media': Media,
    'button': Button,
};
//# sourceMappingURL=format_objects.js.map