import { LasalRuntimeSigElement, SigPolymer } from '../../sigmatek/sig-element/sig-element.js';

/**
 * @class
 * @classdesc Implements and exports the sig-control-checkbox component.
 * @extends LasalRuntimeSigElement
 * 
 * @property {String} checkboxvaluetype - Handle datatype like normal or bit32.
 * @property {String} checkmarkimg - The image used for the checkmark.
 */
export class LasalRuntimeSigCheckboxElement extends LasalRuntimeSigElement {

    /**
     * Returns the component's registered tag name.
     * @readonly
     * @static
     * @returns {String} The control's tag name.
     * @memberof LasalRuntimeSigCheckboxElement
     */
    static get is() {
        return "sig-control-checkbox";
    }

    /**
     * Returns the import path URL used by Polymer.
     * @readonly
     * @static
     * @returns {String} The import path URL.
     * @memberof LasalRuntimeSigCheckboxElement
     */
    static get importMeta() { return import.meta; }

    /**
     * Returns the HTML literal of the component.
     * @readonly
     * @static
     * @returns {HTMLLIElement} The template literal of the component.
     * @memberof LasalRuntimeSigCheckboxElement
     */
    static get template() {
        return SigPolymer.html`
            <style include="sig-element-css">
            :host {
                /* Basic styling of the component */
                color: inherit;
                background-color: var(--theme-sig-control-checkbox-background-color, inherit);
                background-image: none;
                background-repeat: repeat;
                border-width: var(--theme-sig-control-checkbox-border-width, 0);
                border-color: var(--theme-sig-control-checkbox-border-color, inherit);
                border-style: var(--theme-sig-control-checkbox-border-style, none);
                border-radius: var(--theme-sig-control-checkbox-border-radius, 0);

                /* Basic box model settings */
                display: block;
                position: absolute;
        
                margin: 0;
                padding: 0;
                overflow: hidden;
            }

            :host * {
                /* Reset margin and paddings for child elements and turn of text selection */
                @apply --notextselect;
                margin: 0;
                padding: 0;
            }

            .clearfix:after {
                /* Import the global clearfix css mixin */
                @apply --clearfix;
            }

            .sig-control-checkbox {
                /* should be relative for use in designer */
                position: relative;
                width: 100%;
                height: 100%;
                cursor: pointer;
            }

            .checkbox {
                position: absolute;
                border-color: var(--theme-sig-control-checkbox-checkmark-border-color, inherit);
                border-width: var(--theme-sig-control-checkbox-checkmark-border-width, 2px);
                border-style: var(--theme-sig-control-checkbox-checkmark-border-style, none);
                border-radius: var(--theme-sig-control-checkbox-checkmark-border-radius, 0);
                width: var(--theme-sig-control-checkbox-checkmark-size, 16px);
                height: var(--theme-sig-control-checkbox-checkmark-size, 16px);
                cursor: pointer;
                text-align: center;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                margin: auto !important;
                line-height: var(--theme-sig-control-checkbox-checkmark-size, 16px);
            }

            .checkbox {
                background-color: var(--theme-sig-control-checkbox-checkmark-background-color-inactive, inherit);
            }

            :host([checked="1"]) .checkbox {
                background-color: var(--theme-sig-control-checkbox-checkmark-background-color, inherit);
                border-color: var(--theme-sig-control-checkbox-checkmark-active-border-color, transparent);
            }

            :host([isdesignmode]) .checkbox,
            :host([isdesignmode]) .sig-control-checkbox {
                cursor: unset;
            }

            #image {
                display: none;
                pointer-events: none;
            }

            :host([checked="1"]) #image,
            picture {
                display: initial;
                position: absolute;
                top: 0;
                left: 0;
                height: 100%;
                width: 100%;
                box-sizing: border-box;
                padding: var(--theme-sig-control-checkbox-checkmark-image-padding, 0);
                background-origin: content-box;
                background-repeat: no-repeat;
                background-size: 100% 100%;
            }
        </style>
        <div class="sig-control-checkbox clearfix" on-tap="_handleTap">
            <div class="checkbox">
                <object type="[[mimetypec]]" data="[[ccheckmarkimg]]" id="image">
                </object>
            </div>
        </div>
        `;
    }

    /****************************************************************************************************
    * Returns the defined Polymer properties of the component.
    * @readonly
    * @static
    * @returns {Object} The defined Polymer properties.
    * @memberof LasalRuntimeSigCheckboxElement
    * 
    * @property {Number}   value             - Defines the value property of the component.
    * @property {Number}   checked           - Defines if the checkbox is checked.
    * @property {String}   ccheckmarkimg     - Image to show active state of checkbox
    * @property {Boolean}  preview           - Preview setting for designer
    ****************************************************************************************************/
    static get properties() {
        // property names should always be lower case so we
        // can use them directly on the element as an attribute
        let props = {
            value: {
                type: Number,
                value: 0,
                reflectToAttribute: true,
                observer: '_stateChanged',
                notify: true
            },
            checked: {
                type: Number,
                value: 0,
                reflectToAttribute: true,
                notify: true
            },            
            ccheckmarkimg: {
                type: String,
                computed: '_cCheckmarkImg(checkmarkimg, mimetypec)'
            },
            mimetypec: {
                type: String,
                computed: '_cGetImageMimeType(checkmarkimg)'
            },
            preview: {
                type: Boolean,
                value: false,
                observer: '_previewChanged'
            }
        }
        return props;
    }

    /****************************************************************************************************
     * Creates an instance of LasalRuntimeSigCheckboxElement.
     * @constructor
     * @memberof LasalRuntimeSigCheckboxElement
     ****************************************************************************************************/
    constructor() {
        super();
        this.checkboxvaluetype = 'normal';
        this.checkmarkimg = '';
        this.detectedExtension = '';
        this.loadedExtension = '';

        this.addPredefProps([{
            src: 'value',
            dest: 'state',
            type: window.sigApi.SIG_CONST.PROP_PREDEF_PROPS_TYPE_STATE
        }]);

        this.sigAddRequiredProperty('value');
    }

    /**
     * This callback is called after property values are set and local DOM is initialized.
     * @returns  {Undefined} Undefined
     * @memberof LasalRuntimeSigCheckboxElement
    */
    ready() {
        super.ready();        
        this.isReady = true;

        if ((this.propsReady) || (this.isdesignmode)) {
            this._updateButtonState(this.value);            
        }
    }

    /****************************************************************************************************
    * called when values are ready
    *
    * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    sigOnRequiredPropertiesReady() {
        super.sigOnRequiredPropertiesReady();
        this.propsReady = true;
        
        if (this.isReady) {
            this._updateButtonState(this.value);            
        }
    }    

    /****************************************************************************************************
     * This callback runs when the component is connected in the DOM.
     * @returns {Undefined} Undefined
     * @memberof LasalRuntimeSigCheckboxElement
     ****************************************************************************************************/
    connectedCallback() {
        super.connectedCallback();

        //Waiting until the image has been loaded, then color it.
        this.sigAddEventListener('load', () =>  this._colorSvg(), 'jsEvent', this.$.image);
    }

    /****************************************************************************************************
    * shows the state according to the passed parameter
    *
    * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    _updateButtonState(newstate) {
        if (!this.isdesignmode) {           
            const newVal = parseFloat(newstate);
            if (newVal === 0 || isNaN(newVal)) {
                this.checked = 0;
            } else {
                this.checked = 1;
            }
        } else {
            // update preview correctly if a datapoint or datapoint scheme has been selected (otherwise it would be displayed as unchecked)
            this._previewChanged();
        }
    }

    /****************************************************************************************************
     * A method that is executed every time the checkbox is tapped. It toggles the value property of the checkbox.
     * @private
     * @param {Event} event JavaScript tap event.
     * @returns {Undefined} Undefined
     * @memberof LasalRuntimeSigCheckboxElement
     ****************************************************************************************************/
    _handleTap(event) {

        if (!this.isdesignmode) {

            /**---------------------------------------------------------------------------------------*/
            /** normal mode */
            /**---------------------------------------------------------------------------------------*/
            if (this.checkboxvaluetype === "normal") {
                
                if (parseFloat(this.value) !== 0) {
                    // reset bit
                    this.value = 0;
                } else {
                    // set bit
                    this.value = 1;
                }              

            /**---------------------------------------------------------------------------------------*/
            /** bit 32 mode */
            /**---------------------------------------------------------------------------------------*/
            } else {
                
                // Get datapoint-name with station of checkbox: "0:Objectname.Servername.BitXX"
                // Necessary to get datapoint each time in case a datapoint scheme is used.
                const prop = sigApi.properties.getProperty(this.id, 'value');
                let dp, dpName;
                if(prop) {
                    if(prop.getDatapoint) {
                        dp = prop.getDatapoint(); 
                    } else if(prop.getScheme) {
                        dp = prop.getSelectedProp().getDatapoint();
                    }
                    
                    if(dp){
                        dpName = dp.getFullNameWithStation();
                    }
                }

                /** Dataserver is a Bit */
                /**---------------------------------------------------------------------------------------*/
                if (dp && (dp.getTypeId() === window.sigApi.SIG_CONST.TYPE_ID_BIT_ITEM)) {

                    // Get Bit-number from datapoint
                    const dpNameSvr = dpName.slice(0, dpName.lastIndexOf("."));
                    const bitNr = dp.getId() - sigApi.datapointManager.getDatapoint(dpNameSvr).getId();

                    let newValue;
                    // Set value and MSB for Bit32
                    if (parseFloat(this.value) !== 0) {
                        // reset bit
                        newValue = 0 << 31;
                        if (bitNr !== 32) {
                            newValue = newValue | (1 << (bitNr - 1));
                        }
                    } else {
                        // set bit
                        newValue = 1 << 31 | (1 << (bitNr - 1));
                    }
                    this.dispatch = true;
                    sigApi.datapointManager.writeDataPoint(dpNameSvr, (newValue >>> 0));
                            
                /** Dataserver is not a Bit */
                /**---------------------------------------------------------------------------------------*/
                } else {
                    if (parseFloat(this.value) !== 0) {
                        // reset value
                        this.value = 0;
                    } else {
                        // set value
                        this.value = 1;
                    }
                }
            }
            this._dispatchEvent('tap');
        }
    }

    /****************************************************************************************************
     * An observer method that is fired every time the property 'value' changed.
     * @private
     * @param {Number} newstate The new value of the value property.
     * @param {Number} oldstate The old value of the value property.
     * @returns {Undefined} Undefined
     * @memberof LasalRuntimeSigCheckboxElement
     ****************************************************************************************************/
    _stateChanged(newstate, oldstate) {
        if ((!this.isdesignmode) && (oldstate !== undefined)) {
            const newVal = parseFloat(newstate);
            if (newVal === 0 || isNaN(newVal)) {
                this.checked = 0;
            } else {
                this.checked = 1;
            }
            this._dispatchEvent('value-changed');
        }
    }

    /****************************************************************************************************
     * Calculates the checkmark image;
     *
     * @return {String} the image path, or the default image path
     * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    _cCheckmarkImg(checkmarkimg, mimetype){    
        
        // if there's no image
        if(!checkmarkimg || checkmarkimg === '' || checkmarkimg === 'none') {
            // use the default checkmark image
            this.loadedExtension = 'svg';
            return this.importPath + 'default.svg'
        } else {
            // Remove the query string, if there is any
            //checkmarkimg = checkmarkimg.split('?')[0];
            let filename = checkmarkimg.split('?')[0];

            // Extract the file extension
            const actExtension = filename.split('.').pop().toLowerCase();

            // if the mimetype is not set yet or we have now a different extension
            if ((!mimetype) || (mimetype === "") || ((this.detectedExtension !== "") && (actExtension !== this.detectedExtension))) {
                // try again later (after mimetype computation)
                return "";
            } else {
                this.loadedExtension = actExtension;
                return checkmarkimg;
            }
        }    
    }

    /****************************************************************************************************
     * Applies the color property to the pointer image
     *
     * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    _colorSvg() {
        if (this.loadedExtension === 'svg') {
            const contentDocument = this.$.image.contentDocument;
            if(contentDocument) {
                const style = getComputedStyle(this);
                const color = style.getPropertyValue('--theme-sig-control-checkbox-checkmark-color');
                const paths = contentDocument.querySelectorAll('path,svg g, svg g *');
                for(const path of paths) {
                    path.setAttribute('fill', color);
                    path.style.fill = color;
                }

                const img = contentDocument.querySelector('img');
                if(img) {
                    img.style.setProperty('height', '100%');
                    img.style.setProperty('width', '100%');
                }
            }
        }
    }

    /****************************************************************************************************
     * Called when a CSS Variable is changed
     *
     * @param {object} cssObj an Object containing the changed CSS variables, and their values
     * @param {boolean} [doBoundsCheck=false] only relevant in designmode, leave as false
     * @return {*} 
     * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    sigApplyCSSValue(cssObj, doBoundsCheck = false) {
        super.sigApplyCSSValue(cssObj, doBoundsCheck);
        // Nothing special to do if we aren't connected yet
        // The first coloring happens after the svg is loaded
        // this function is first called before the connected callback.
        if(!this.isConnected) return;
        if(cssObj['--theme-sig-control-checkbox-checkmark-color']) {
            this._colorSvg();
        }
    }

    /****************************************************************************************************
     * Called when changing the preview value in the designer
     *
     * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    _previewChanged() {
        if(this.isdesignmode) {
            if (this.preview) {
                this.checked = 1;
            } else {
                this.checked = 0;
            }
        }
    }

    /****************************************************************************************************
     * Called for checking if a event should be dispatched.
     *
     * @param {*} event
     * @return {boolean} true to dispatch event
     * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    shouldDispatchInternalEvent(event) {
        
        // necessary for this.checkboxvaluetype === "bit32", otherwise no event is dispatched
        if ((this.dispatch === true) && (event === 'value-changed')) {
            this.dispatch = false;
            return true;
        } else {
            return super.shouldDispatchInternalEvent(event);
        }
    }

    /****************************************************************************************************
     * Detect the right image MIME type based on the file extension
     *
     * @param {String} checkmarkimg Filename string to check the extension
     * @return {String} returns the MIME type
     * @memberof LasalRuntimeSigCheckboxElement
    ****************************************************************************************************/
    _cGetImageMimeType(checkmarkimg) {
        let actExtension = '';
        if(!checkmarkimg || checkmarkimg === '' || checkmarkimg === 'none') {
            return '';
        } else {
            // Remove the query string, if there is any
            checkmarkimg = checkmarkimg.split('?')[0];

            // Extract the file extension
            actExtension = checkmarkimg.split('.').pop().toLowerCase();
        }

        let ActMIMEType;

        // Determine the MIME type based on the extension
        switch (actExtension) {
            case 'svg': 
                ActMIMEType = 'image/svg+xml';
                break;

            case 'jpg':
            case 'jpeg':
                ActMIMEType = 'image/jpeg';
                break;

            case 'png':
                ActMIMEType = 'image/png';
                break;

            case 'gif':
                ActMIMEType = 'image/gif';
                break;

            case 'bmp':
                ActMIMEType = 'image/bmp';
                break;

            case 'webp':
                ActMIMEType = 'image/webp';
                break;

            case 'ico':
                ActMIMEType = 'image/x-icon';
                break;

            case 'tif':
            case 'tiff':
                ActMIMEType = 'image/tiff';
                break;

            case '':
                ActMIMEType = '';
                break;

            default:
                ActMIMEType = 'image/svg+xml';
        }

        // remember the detected file extension (might change, then we need to get the MIMEType again first before loading the new source)
        this.detectedExtension = actExtension;

        return ActMIMEType;
    }
}
customElements.define(LasalRuntimeSigCheckboxElement.is, LasalRuntimeSigCheckboxElement);
