/** This file is part of Reactor.
 *  Copyright (c) 2021-2025 Kedron Holdings LLC, All Rights Reserved.
 *  Reactor is not public domain or open source. Distribution or derivative works are expressly prohibited.
 */

export const version = 25293;

import api from '/client/ClientAPI.js';

import * as Common from './reactor-ui-common.js';
const isEmpty = Common.isEmpty;

import { _T } from './i18n.js';

import "/common/util.js";  /* global util */

class EntityPicker {
    constructor() {
        this.$picker = $( 'div#entitypicker' );
        $( '.modal-title', this.$picker ).text( _T( 'Choose Entity' ) );
        $( 'label[for="ep-filter-name"]', this.$picker ).text( _T( 'Name Contains:' ) );
        $( 'button#ep-filter-clear span', this.$picker ).text( _T( [ '#ep-filter-clear', 'Clear Filters' ] ) );
        $( 'label#ep-filter-label', this.$picker ).text( _T( 'Filters:' ) );
        $( 'div#ep-col-entity', this.$picker ).text( _T(['#ep-col-entity','Entity']) );
        $( 'div#ep-col-id', this.$picker ).text( _T(['#ep-col-id','ID']) );
    }

    showFilteredEntities() {
        let $tbody = $( 'div.ep-list-body', this.$picker );
        $tbody.empty();

        const ctrl_filter = $( 'select#ep-filter-ctrl', this.$picker ).val() || "";
        localStorage.setItem( 'entitypicker_filter_ctrl', ctrl_filter );
        const ctrl_re = isEmpty( ctrl_filter ) ? false : new RegExp( `^${ctrl_filter}>`, "i" );
        const grp_filter = $( 'select#ep-filter-group', this.$picker ).val() || "";
        localStorage.setItem( 'entitypicker_filter_grp', grp_filter );
        const grp_entity = isEmpty( grp_filter ) ? false : api.getEntity( grp_filter );
        const cap_filter = $( 'select#ep-filter-cap', this.$picker ).val() || "";
        localStorage.setItem( 'entitypicker_filter_cap', cap_filter );
        const name_filter = $( 'input#ep-filter-name', this.$picker ).val() || "";
        localStorage.setItem( 'entitypicker_filter_name', name_filter );
        let name_re = util.stringOrRegexp( name_filter.trim() );
        if ( "string" === typeof name_re ) {
            name_re = name_re.toLocaleLowerCase();
        }

        let filtered = api.getEntities();
        try {
            filtered = filtered.filter( e => {
                if ( ! ( isEmpty( cap_filter ) || e.hasCapability( cap_filter ) ) ) {
                    return false;
                }
                if ( ctrl_re && null === e.getCanonicalID().match( ctrl_re ) ) {
                    return false;
                }
                if ( name_re ) {
                    if ( name_re instanceof RegExp && ! ( name_re.test( e.getName() ) || name_re.test( e.getID() ) ) ) {
                        // Regular expression, but doesn't match name or ID.
                        return false;
                    } else if ( "string" === typeof name_re ) {
                        // String
                        if ( ! ( e.getName().toLocaleLowerCase().includes( name_re ) || e.getID().startsWith( name_re ) ) ) {
                            return false;
                        }
                    }
                }
                if ( grp_entity && ! grp_entity.hasMember( e ) ) {
                    return false;
                }
                return true;
            });
        } catch ( err ) {
            console.error( "Unable to filter entities: ", err.message );
            console.error( err );
        }

        const self = this;
        filtered.sort( function( a, b ) {
            return a.getName().localeCompare( b.getName(), undefined, { sensitivity: 'base' } );
        }).forEach( function( entity ) {
            const eid = entity.getCanonicalID();
            const $row = $( '<div class="row row-cols-2"></div>' ).data( 'entity', eid ).attr( 'data-entity', eid )
                .on( 'click.entitypicker', function( ev ) {
                    let $el = $( ev.currentTarget );
                    self.$picker.modal( 'hide' );
                    self.resolve( { id: $el.data( 'entity' ), name: $( 'div.re-entity-name', $el ).text() } );
                })
                .appendTo( $tbody );
            $( '<div class="col re-entity-name"></div>' )
                .text( entity.getName() )
                .appendTo( $row );
            $( '<div class="col"></div>' ).text( eid ).appendTo( $row );
        });
    }

    handleEntityListFilterChange( /* event */ ) {
        this.showFilteredEntities();
    }

    populateFilterMenus() {
        let handler = this.handleEntityListFilterChange.bind( this );
        let $mm = $( 'select#ep-filter-ctrl', this.$picker )
            .empty()
            .off( 'change.entitypicker' )
            .on( 'change.entitypicker', handler );
        $( `<option value="">${_T('(any controller)')}</option>` ).appendTo( $mm );
        const t = api.getControllers();
        t.forEach( (n, ix) => {
            t[ix] = api.getController( n );
        });
        t.sort( (a,b) => {
            return (a.name || a.id).localeCompare( b.name || b.id, undefined, { sensitivity: 'base' } );
        });
        t.forEach( n => {
            $( '<option></option>' ).val( n.id ).text( n.name ? `${n.name} (${n.id})` : n.id )
                .appendTo( $mm );
        });
        $mm.val( localStorage.getItem( 'entitypicker_filter_ctrl' ) || "" );

        $mm = $( 'select#ep-filter-group', this.$picker )
            .empty()
            .off( 'change.entitypicker' )
            .on( 'change.entitypicker', handler );
        $( `<option value="">${_T('(any group)')}</option>` ).appendTo( $mm );
        let gl = api.getEntities().filter( e => e.getType() === "Group" );
        gl.sort( (a, b) => {
            return a.getName().localeCompare( b.getName(), undefined, { sensitivity: 'base' } );
        });
        gl.forEach( e => {
            let title = e.getName() + " (" + e.getCanonicalID() + ")";
            $( '<option></option>' ).val( e.getCanonicalID() ).text( title )
                .appendTo( $mm );

        });
        $mm.val( localStorage.getItem( 'entitypicker_filter_grp' ) || "" );

        $mm = $( 'select#ep-filter-cap', this.$picker )
            .empty()
            .off( 'change.entitypicker' )
            .on( 'change.entitypicker', handler );
        $( `<option value="">${_T('(any capability)')}</option>` ).appendTo( $mm );
        let cap = {};
        api.getEntities().forEach( e => {
            let c = e.getCapabilities() || [];
            c.forEach( capName => {
                cap[ capName ] = true;
            });
        });
        cap = Object.keys( cap );
        cap.sort();
        cap.forEach( capName => {
            /* x_vera_svc_micasaverde_com_SceneControllerLED1 => x_vera_..._SceneControlledLED1 */
            let abbrev = capName.length <= 24 ? capName : capName.replace( /^(x_[^_]+_).{4,}(_[^_]+)$/i, "$1...$2" );
            $( '<option></option>' ).val( capName ).text( abbrev ).attr( 'title', capName )
                .appendTo( $mm );
        });
        $mm.val( localStorage.getItem( 'entitypicker_filter_cap' ) || "" );

        $( 'input#ep-filter-name', this.$picker )
            .val( localStorage.getItem( 'entitypicker_filter_name' ) || "" )
            .off( 'keyup.entitypicker' )
            .on( 'keyup.entitypicker', handler );

        const self = this;

        $( 'button#ep-filter-clear', this.$picker )
            .off( 'click.entitypicker' )
            .on( 'click.entitypicker', function() {
                $( 'select#ep-filter-ctrl', self.$picker ).val( "" );
                $( 'select#ep-filter-group', self.$picker ).val( "" );
                $( 'select#ep-filter-cap', self.$picker ).val( "" );
                $( 'input#ep-filter-name', self.$picker ).val( "" );
                self.showFilteredEntities();
            });

        $( 'button[data-dismiss="modal"]', this.$picker )
            .off( 'click.entitypicker' )
            .on( 'click.entitypicker', function() {
                // console.log("Close button hit");
                self.resolve( null );
            });
    }

    pick( /* options */ ) {
        const self = this;
        this.promise = new Promise( ( resolve ) => {
            self.resolve = resolve;
            this.populateFilterMenus();
            this.showFilteredEntities();
            this.$picker.modal( 'show' );
            /* Allow drag by header */
            this.$picker.draggable({
                handle: 'div.modal-header'
            });
        }).finally( () => {
            // console.log("In EntityPicker.pick finally()");
            this.$picker.modal( 'hide' );
        });
        return this.promise;
    }

    handlePickerControlHit( event ) {
        /* Open the dialog */
        let $ig = $( event.currentTarget ).closest( 'div.entity-selector' );
        this.pick().then( result => {
            /* Post result to fields */
            if ( null !== result ) {
                $( 'span.ep-entityname', $ig ).text( result.name );
                $( 'span.ep-entityid', $ig ).text( result.id );
                $( 'input.selectedentity', $ig ).val( result.id );
                $ig.trigger( 'change', result );
            }
            return result; /* keep passing */
        });
    }

    getPickerControl( id, val, name ) {
        let $ig = $( '<div class="entity-selector d-flex flex-row flex-nowrap align-items-start"></div>' );
        $ig.data( 'entitypicker', this );
        $( '<div class="re-es-pick"><span class="re-es-text"><i class="bi bi-caret-right-fill me-1 text-primary"></i><span class="ep-entityname"></span></span></div>' )
            .on( 'click.reactor', this.handlePickerControlHit.bind( this ) )
            .appendTo( $ig );
        let $col = $( '<div class="re-es-eid"></div>' )
            .on( 'click.reactor', this.handlePickerControlHit.bind( this ) )
            .appendTo( $ig );
        $( '<input type="hidden" class="form-control form-control-sm selectedentity bg-transparent" autocomplete="off" readonly>' )
            .val( val || "" )
            .attr( 'id', id || Common.getUID( 'ep-' ) )
            .appendTo( $col );
        $( '<span class="ep-entityid"></span>' )
            .text( val || "---" )
            .appendTo( $col );

        this.setEntity( $ig, val, name );
        return $ig;
    }

    setEntity( $selector, id, name ) {
        $( 'span.ep-entityname', $selector ).text( name || _T(['#opt-choose','Click to select']) );
        $( 'span.ep-entityid', $selector ).text( id || "---" );
        $( 'input.selectedentity', $selector ).val( id || "" );
    }

    getEntity( $selector ) {
        return $( 'input.selectedentity', $selector ).val() || "";
    }
}

const sys_picker = new EntityPicker();

export default sys_picker; /* Singleton */
