/**
 * -------------------------------------------------------------------------
 *
 * Scripts for the client side of your website.
 *
 * -------------------------------------------------------------------------
 *
 * @package    MimimiFramework
 * @subpackage Examples / Five Viewers site
 * @license    GPL-2.0
 *             https://opensource.org/license/gpl-2-0/
 * @copyright  2022 MiMiMi Community
 *             https://mimimi.software/
 *
 * -------------------------------------------------------------------------
 */

    const NO_AVATAR_IMAGE = document.querySelector (
                                'html > head > link[rel="stylesheet"][href$="/css/styles.css"]'
                            ) .href
                              .replace ( /css\/styles\.css$/,
                                         'images/no-avatar.png' );

    /**
     * ---------------------------------------------------------------------
     */

    const findDates   = ( ) => document.querySelectorAll ( '[data-date]:not([data-date=""])'     );
    const findTimes   = ( ) => document.querySelectorAll ( '[data-time]:not([data-time=""])'     );
    const findAvatars = ( ) => document.querySelectorAll ( '.avatar > img'                       );
    const findPollers = ( ) => document.querySelectorAll ( '[data-poller]:not([data-poller=""])' );

    /**
     * ---------------------------------------------------------------------
     *
     * Extracts the date from a string.
     *
     * ---------------------------------------------------------------------
     */

    const extractDate = ( date ) => {
        const pattern = new RegExp   ( '^(\\d{4})-(\\d{2})-(\\d{2}).*$', '' ),
              year    = date.replace ( pattern, '$1' );
        if ( year != date ) {
            let month = date.replace ( pattern, '$2' );
                month = parseInt     ( month );
            if ( month > 0 && month <= 12 ) {
                const  day   = date.replace ( pattern, '$3' ),
                       names = [ 'January'  ,
                                 'February' ,
                                 'March'    ,
                                 'April'    ,
                                 'May'      ,
                                 'June'     ,
                                 'July'     ,
                                 'August'   ,
                                 'September',
                                 'October'  ,
                                 'November' ,
                                 'December' ],
                       name  = names [ month - 1 ];
                return parseInt ( day ) + ' ' + name + ' ' + year;
            }
        }
        return '';
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Extracts the time from a string.
     *
     * ---------------------------------------------------------------------
     */

    const extractTime = ( date ) => {
        const  pattern = new RegExp   ( '^\\d{4}-\\d{2}-\\d{2}[^\\d]+(\\d{2}):(\\d{2}).*$', '' ),
               hour    = date.replace ( pattern, '$1' ),
               minute  = date.replace ( pattern, '$2' );
        return hour + ':' + minute;
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Fixes chanell dates.
     *
     * ---------------------------------------------------------------------
     */

    const fixDates = ( ) => {
        const nodes = findDates ( );
        nodes.forEach (
            ( node ) => {
                const attr  = 'data-date',
                      value = node.getAttribute ( attr       ),
                      date  = extractDate       ( value      );
                if ( date ) {
                    node.setAttribute           ( attr, date );
                }
            }
        );
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Fixes message times.
     *
     * ---------------------------------------------------------------------
     */

    const fixTimes = ( ) => {
        const nodes = findTimes ( );
        nodes.forEach (
            ( node ) => {
                const attr  = 'data-time',
                      value = node.getAttribute  ( attr             ),
                      date  = extractDate        ( value            );
                if ( date ) {
                    const time = extractTime     ( value            );
                    node.setAttribute            ( attr,       time );
                    node.parentNode.setAttribute ( 'data-day', date );
                }
            }
        );
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Fixes avatars with no photo.
     *
     * ---------------------------------------------------------------------
     */

    const fixAvatars = ( ) => {
        const nodes = findAvatars ( );
        nodes.forEach (
            ( node ) => {
                const attr  = 'src',
                      value = node.getAttribute ( attr );
                if ( value == '' ) {
                    node.src = NO_AVATAR_IMAGE;
                }
            }
        );
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Starts a polling.
     *
     * ---------------------------------------------------------------------
     */

    const startPollers = ( ) => {
        const nodes = findPollers ( );
        nodes.forEach (
            ( node ) => { pollingFor ( node ); }
        );
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Requests the API for a command.
     *
     * ---------------------------------------------------------------------
     *
     * It self-refreshes every N seconds by specifying its own name as the
     * last parameter of the "loadDocument" function.
     *
     * ---------------------------------------------------------------------
     */

    const pollingFor = ( node ) => {
        const module = getPollerUrl          ( node, 'data-poller'      );
        if ( module ) {
            const version = getPollerUrl     ( node, 'data-version'     ),
                  command = getPollerUrl     ( node, 'data-command'     ),
                  options = getPollerOptions ( node                     ),
                  timer   = getPollerTime    ( node                     ),
                  success = getPollerUrl     ( node, 'data-success', '' ),
                  url     = 'polls' + module + version + command + options;
            setTimeout (
                function ( ) {
                    loadDocument ( url, success, node, pollingFor );
                }, timer * 1000
            );
        }
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Retrieves a poller URL segment.
     *
     * ---------------------------------------------------------------------
     */

    const getPollerUrl = ( node, attr, prefix = '/' ) => {
        let value = node.getAttribute ( attr );
            value = value == null
                           ? ''
                           : value.replace ( /[^a-z0-9_]/gi, '' );
        return ( value == '' ? '' : prefix ) + value;
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Retrieves a poller options.
     *
     * ---------------------------------------------------------------------
     */

    const getPollerOptions = ( node, def = '' ) => {
        const trail    = '[\\s.\\/\\\\]+',
              pattern1 = new RegExp ( '^' + trail      , '' ),
              pattern2 = new RegExp (       trail + '$', '' );
        let   value = node.getAttribute ( 'data-options' );
              value = '/' + ( value === null
                                      ? def
                                      : value.replace ( pattern1, '' ) );
        return value.replace ( pattern2, '' );
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Retrieves a poller timer.
     *
     * ---------------------------------------------------------------------
     */

    const getPollerTime = ( node, def = 30 ) => {
        let value = node.getAttribute ( 'data-timer' );
            value = parseInt ( value === null ? def : value );
        return isNaN ( value ) || value < 1 ? 1 : value;
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Loads a text document.
     *
     * ---------------------------------------------------------------------
     */

    const loadDocument = ( url, successCallback = null, to = null, finalCallback = null ) => {
        if ( to ) {
            to.innerText = '';
            to.classList.remove ( 'error-format'     );
            to.classList.remove ( 'error-absent'     );
            to.classList.remove ( 'error-connection' );
            to.classList.remove ( 'idle'             );
            to.classList.add    ( 'loading'          );
        }
        fetch (
            url, { method: 'GET',
                   mode:   'cors',
                   cache:  'no-cache' }
        ) .then (
            function ( response ) {
                switch ( response.status ) {
                    case 200:
                         response.text ( ) .then (
                                               function ( body ) {
                                                   if ( successCallback ) {
                                                       to.classList.remove ( 'loading' );
                                                       window[ successCallback ] ( body, to );
                                                   } else if ( to ) {
                                                       to.classList.remove ( 'loading' );
                                                       to.innerText = body;
                                                   }
                                               }
                                           ) .catch (
                                               function ( ) {
                                                   if ( to ) {
                                                       to.classList.remove ( 'loading'      );
                                                       to.classList.add    ( 'error-format' );
                                                   }
                                               }
                                           );
                         break;
                    case 404:
                    default:
                         if ( to ) {
                             to.classList.remove ( 'loading'      );
                             to.classList.add    ( 'error-absent' );
                         }
                }
            }
        ) .catch (
            function ( ) {
                if ( to ) {
                    to.classList.remove ( 'loading'          );
                    to.classList.add    ( 'error-connection' );
                }
            }
        ) .finally (
            function ( ) {
                if ( finalCallback ) finalCallback ( to, url );
            }
        );
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Displays a result of the "random" command.
     *
     * ---------------------------------------------------------------------
     */

    function displayRandom ( json, node ) {
        const entry = JSON.parse ( json );
        node.innerHTML = renderMessage ( entry );
                         fixTimes      (       );
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Renders a channel message.
     *
     * ---------------------------------------------------------------------
     */

    const renderMessage = ( entry ) => {
        if ( typeof entry.title != 'undefined' ) {
            return '<section class="message">' +
                       ( typeof entry.image != 'undefined'
                             && entry.image != ''
                                            ? '<figure class="splash">' +
                                                  '<img src="' + entry.image + '" alt="" loading="lazy" decoding="async">' +
                                              '</figure>'
                                            : '' ) +
                       '<div class="info" data-title="' + entry.title + '" data-time="' + entry.created + '">' +
                           entry.text +
                           ( typeof entry.credits != 'undefined'
                                                  ? '<a href="' + entry.credits + '" class="btn" rel="nofollow noopener noreferrer" tabindex="-1" target="_blank">' +
                                                        entry.credits_label +
                                                    '</a>'
                                                  : '' ) +
                       '</div>' +
                       '<div class="title">' + entry.name + '</div>' +
                   '</section>';
        }
        return 'No messages!';
    };

    /**
     * ---------------------------------------------------------------------
     *
     * Start all scripts.
     *
     * ---------------------------------------------------------------------
     */

    fixDates     ( );
    fixTimes     ( );
    fixAvatars   ( );
    startPollers ( );
