// Import the Lasal Runtime base element into your component
// Import SigPolymer to use Polymer methods: html, afterNextRender, beforeNextRender, debouncer, timeOut
// or if your component's getter template() returns a HTML literal
import { LasalRuntimeSigElement, SigPolymer } from '../../sigmatek/sig-element/sig-element.js';


/****************************************************************************************************
 * @class LasalRuntimeSigControlColorpickerBase
 * @extends {LasalRuntimeSigElement}
 ****************************************************************************************************/
export default class LasalRuntimeSigControlColorpickerBase extends LasalRuntimeSigElement {
    
    /****************************************************************************************************
     * When the element is loaded, importMeta() assigns reference to the path from 
     * which an element was imported to its importPath property. 
     * Relative URLs in styles are automatically re-written to be relative to the importPath property. 
     * You can use [[importPath]] in your component to load url resources
     * 
     * @readonly
     * @static
     ****************************************************************************************************/
    static get importMeta() {
        return import.meta;
    }
    
    /****************************************************************************************************
     * Return the component style and template as a template literal
     *
     * @readonly
     * @static
     ****************************************************************************************************/
    static get template() {
        return SigPolymer.html`
    <style include="sig-element-css">
            :host {
                /* Basic styling of the component */
                color: var(--theme-sig-control-colorpicker-base-color, inherit);
                background-color: var(--theme-sig-control-colorpicker-base-background-color, transparent);
                border-width: var(--theme-sig-control-colorpicker-base-border-width, inherit);
                border-color: var(--theme-sig-control-colorpicker-base-border-color, inherit);
                border-style: var(--theme-sig-control-colorpicker-base-border-style, none);
                border-radius: var(--theme-sig-control-colorpicker-base-border-radius, none);

                /* Basic box model settings */
                display: block;
                position: absolute;
                overflow: hidden;

                height: auto;
                width: auto;

                z-index: 7;
            }

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

            .clearfix:after {
                @apply --clearfix
            }

            .sig-control-colorpicker-base {
                box-sizing: border-box;
                height: 100%;
                width: 100%;
                position: relative;
                border: 1px solid black;
                background-color: gray;
                padding-left: 10px;
                padding-right: 10px;
            }

            #colorSelectArea {
                display: flex;
                flex-wrap: wrap;
                justify-content: space-evenly;
            }

            .color {
                height: 30px;
                width: 50px;
                box-sizing: border-box;
                border: 1px solid black;
                margin: 1px
            }

            sig-control-input,
            sig-control-button {
                --theme-sig-element-top: 0;
                --theme-sig-element-left: 0;
                display: inline-block;
                position: relative;
                width: 50px;
                height: 20px;

            }

            sig-control-input {
                --theme-sig-element-height: var(--theme-sig-control-colorpicker-picker-input-height, 35px);
                --theme-sig-element-width: var(--theme-sig-control-colorpicker-picker-input-width, 80px);
                --theme-sig-control-input-background-color: var(--theme-sig-control-colorpicker-picker-input-background-color, rgba(255, 255, 255, 1));
                /*--theme-sig-control-input-value-background-color: var(--theme-sig-control-colorpicker-picker-input-value-background-color, rgba(215, 215, 215, 1));*/
                --theme-sig-control-input-value-active-color: var(--theme-sig-control-colorpicker-picker-input-value-active-color, rgba(40,50,45,1));
                --theme-sig-control-input-value-active-background-color: var(--theme-sig-control-colorpicker-picker-input-value-active-background-color, rgba(174, 200, 26, 1));
                --theme-sig-control-input-color: var(--theme-sig-control-colorpicker-picker-input-color, rgba(40,50, 45, 1));
                --theme-sig-control-input-border-color: var(--theme-sig-control-colorpicker-picker-input-border-color, rgba(40,50,45, 1));
                --theme-sig-control-input-border-width: var(--theme-sig-control-colorpicker-picker-input-border-width, 1px);
                --theme-sig-control-input-border-style: var(--theme-sig-control-colorpicker-picker-input-border-style, solid);
                --theme-sig-control-input-background-color-direction: var(--theme-sig-control-colorpicker-picker-input-background-color-direction, to bottom);
                --theme-sig-control-input-value-active-background-color-stop: var(--theme-sig-control-colorpicker-picker-input-value-active-background-color-stop
                    /* , rgba(174, 200, 26, 1) */
                    );
                --theme-sig-control-input-active-background-color-direction: var(--theme-sig-control-colorpicker-picker-input-active-background-color-direction, to bottom);
                --theme-sig-control-input-border-radius: var(--theme-sig-control-colorpicker-picker-input-border-radius, 3px);
                --theme-sig-control-input-border-color-direction: var(--theme-sig-control-colorpicker-picker-input-border-color-direction, to bottom);
                --theme-sig-control-input-shadow-size: var(--theme-sig-control-colorpicker-picker-input-shadow-size, 0px);
                --theme-sig-control-input-activ-border-color: var(--theme-sig-control-colorpicker-picker-input-activ-border-color
                    /* , inherit */
                    );
                --theme-sig-control-input-activ-border-color-direction: var(--theme-sig-control-colorpicker-picker-input-activ-border-color-direction, to bottom);
                --theme-sig-control-input-pressed-shadow-size: var(--theme-sig-control-colorpicker-picker-input-pressed-shadow-size, 0px);
                --theme-sig-control-input-value-text-align: var(--theme-sig-control-colorpicker-picker-input-value-text-align, center);
                --theme-sig-control-input-margin-leftright: var(--theme-sig-control-colorpicker-picker-input-margin-leftright, 0px);
                --theme-sig-control-input-margin-top: var(--theme-sig-control-colorpicker-picker-input-margin-top, 5px);
                --theme-sig-control-input-unit-text-align: var(--theme-sig-control-colorpicker-picker-input-unit-text-align, center);
                --theme-sig-control-input-unit-margin-leftright: var(--theme-sig-control-colorpicker-picker-input-unit-margin-leftright, 0px);
                --theme-sig-control-input-unit-margin-bottom: var(--theme-sig-control-colorpicker-picker-input-unit-margin-bottom, 5px);
                --theme-sig-control-input-unit-text-size: var(--theme-sig-control-colorpicker-picker-input-unit-text-size, inherit);
                --theme-sig-control-input-unit-color: var(--theme-sig-control-colorpicker-picker-input-unit-color, inherit);
            }
            #hexcontainer sig-control-input {
                --theme-sig-element-width: var(--theme-sig-control-colorpicker-picker-hex-input-width, var(--theme-sig-control-colorpicker-picker-input-width, 80px));
            }

            #colorpickermarker1 {
                position: absolute;
                box-sizing: border-box;
                background-color: blue;
                top: 0;
                left: 0;
                transform: translate3d(calc(0px - 50%), 0, 0);
                width: var(--theme-sig-control-colorpicker-base-marker-width, 20px);
                height: var(--theme-sig-control-colorpicker-base-marker-height, 20px);
                border: 1px solid black;
                border-radius: var(--theme-sig-control-colorpicker-picker-marker-border-radius, 999px);
            }

            #colorSelectArea {
                position: relative;
            }

            #canvas {
                /**todo*/
                /* position: absolute; */
                position: relative;
            }

            .hidden {
                display: none;
            }

            sig-control-button {
                margin-right: var(--theme-sig-control-colorpicker-picker-button-spacing, 10px) !important;
                --theme-sig-element-height: var(--theme-sig-control-colorpicker-picker-button-height, 50px);
                --theme-sig-element-width: var(--theme-sig-control-colorpicker-picker-button-width, 100px);
                --theme-sig-control-button-color: var(--theme-sig-control-colorpicker-picker-button-color, rgba(255, 255, 255, 1));
                --theme-sig-control-button-background-color: var(--theme-sig-control-colorpicker-picker-button-background-color, rgba(40, 50, 45, 1));
                --theme-sig-control-button-border-color: var(--theme-sig-control-colorpicker-picker-button-border-color, transparent);
                --theme-sig-control-button-border-width: var(--theme-sig-control-colorpicker-picker-button-border-width, 0px);
                --theme-sig-control-button-border-radius: var(--theme-sig-control-colorpicker-picker-button-border-radius, 3px);
                --theme-sig-control-button-border-style: var(--theme-sig-control-colorpicker-picker-button-border-style, none);
                --theme-sig-control-button-pressed-background-color: var(--theme-sig-control-colorpicker-picker-button-pressed-background-color, rgba(174, 200, 26, 1));
                --theme-sig-control-button-pressed-color: var(--theme-sig-control-colorpicker-picker-button-pressed-color, rgba(255, 255, 255, 1));
                --theme-sig-control-button-background-color-direction: var(--theme-sig-control-colorpicker-picker-button-background-color-direction, to bottom);
                --theme-sig-control-button-border-color-direction: var(--theme-sig-control-colorpicker-picker-button-border-color-direction, to bottom);
                --theme-sig-control-button-shadow-size: var(--theme-sig-control-colorpicker-picker-button-shadow-size, 0px);
                --theme-sig-control-button-text-align: var(--theme-sig-control-colorpicker-picker-button-text-align, center);
                --theme-sig-control-button-vertical-align: var(--theme-sig-control-colorpicker-picker-button-vertical-align, middle);
                --theme-sig-control-button-background-image: var(--theme-sig-control-colorpicker-picker-button-background-image, none);
                --theme-sig-control-button-background-repeat: var(--theme-sig-control-colorpicker-picker-button-background-repeat, no-repeat);
                --theme-sig-control-button-background-position-x: var(--theme-sig-control-colorpicker-picker-button-background-position-x, center);
                --theme-sig-control-button-background-position-y: var(--theme-sig-control-colorpicker-picker-button-background-position-y, center);
                --theme-sig-control-button-background-size: var(--theme-sig-control-colorpicker-picker-button-background-size, cover);
                --theme-sig-control-button-pressed-background-color-direction: var(--theme-sig-control-colorpicker-picker-button-pressed-background-color-direction, to bottom);
                --theme-sig-control-button-pressed-background-image: var(--theme-sig-control-colorpicker-picker-button-pressed-background-image, inherit);
                --theme-sig-control-button-pressed-border-color-direction: var(--theme-sig-control-colorpicker-picker-button-pressed-border-color-direction, to bottom);
                --theme-sig-control-button-pressed-shadow-size: var(--theme-sig-control-colorpicker-picker-button-pressed-shadow-size, 0px);
                --theme-sig-control-led-width: 10px;
                --theme-sig-control-led-height: 10px;
                --theme-sig-control-button-led-border-width: var(--theme-sig-control-colorpicker-picker-button-led-border-width, 1px);
                --theme-sig-control-button-led-border-color: var(--theme-sig-control-colorpicker-picker-button-led-border-color, rgba(0, 0, 0, 0));
                --theme-sig-control-button-led-border-radius: var(--theme-sig-control-colorpicker-picker-button-led-border-radius, 5px);
                --theme-sig-control-button-led-background-color: var(--theme-sig-control-colorpicker-picker-button-led-background-color, #ccc);
                --theme-sig-control-button-led-background-image: var(--theme-sig-control-colorpicker-picker-button-led-background-image, none);
                --theme-sig-control-button-led-pressed-background-color: var(--theme-sig-control-colorpicker-picker-button-led-pressed-background-color, #070);
                --theme-sig-control-button-led-pressed-background-image: var(--theme-sig-control-colorpicker-picker-button-led-pressed-background-image, none);
                --theme-sig-control-button-led-background-size: var(--theme-sig-control-colorpicker-picker-button-led-background-size, cover);
                --theme-sig-control-button-led-background-repeat: var(--theme-sig-control-colorpicker-picker-button-led-background-repeat, no-repeat);
                --theme-sig-control-button-led-margin-height: var(--theme-sig-control-colorpicker-picker-button-led-margin-height, 3px);
                --theme-sig-control-button-led-margin-width: var(--theme-sig-control-colorpicker-picker-button-led-margin-width, 3px);
            }

            label {
                color: var(--theme-sig-control-colorpicker-picker-label-color, rgba(42, 42, 42, 1));
                margin-right: 5px;
            }

            .table-cell {
                box-sizing: border-box;
                display: inline-flex;
                width: 33%;
                justify-content: flex-end;
            }
            #hexcontainer .table-cell {
                width:100%;
            }

            #canvasContainer {
                position: relative;
                width: 100%;
                top: 50%;
                transform: translateY(-50%);
            }
            #colorinputs {
                margin-bottom: var(--theme-sig-control-colorpicler-picker-colorinputs-margin-bottom, 10px);
            }
        </style>
        <div class="sig-control-colorpicker-base clearfix">
            <div id="colorSelectArea"></div>
            <div id="colorinputs">
                <div id="rgbcontainer">
                    <div class="table-cell">
                        <label></label>
                        <sig-control-input id="rgbred" type="numeric" limit-low="0" limit-high="255"></sig-control-input>
                    </div>
                    <div class="table-cell">
                        <label></label>
                        <sig-control-input id="rgbgreen" type="numeric" limit-low="0" limit-high="255"></sig-control-input>
                    </div>
                    <div class="table-cell">
                        <label></label>
                        <sig-control-input id="rgbblue" type="numeric" limit-low="0" limit-high="255"></sig-control-input>
                    </div>
                </div>
                <div id="hslcontainer">
                    <div class="table-cell">
                        <label></label>
                        <sig-control-input id="hslhue"  type="numeric" limit-low="0" limit-high="360"></sig-control-input>
                    </div>
                    <div class="table-cell">
                        <label></label>
                        <sig-control-input id="hslsaturation" type="numeric" limit-low="0" limit-high="1"></sig-control-input>
                    </div>
                    <div class="table-cell">
                        <label></label>
                        <sig-control-input id="hslvalue" type="numeric" limit-low="0" limit-high="1"></sig-control-input>
                    </div>
                </div>
                <div id="hexcontainer">
                    <div class="table-cell">
                        <label></label>
                        <sig-control-input id="hex" type="hex" limit-low="0" limit-high="255"></sig-control-input>
                    </div>
                </div>
            </div>
            <div id="controlButtons">
                <sig-control-button id="OK"></sig-control-button>
                <sig-control-button id="CANCEL"></sig-control-button>
                <sig-control-button id="SHOWCOLORINPUTS"></sig-control-button>
            </div>
        </div>
    `;
    }


    /****************************************************************************************************
     * define the custom tag name
     *
     * @readonly
     * @static
     ****************************************************************************************************/
    static get is() {
        return "sig-control-colorpicker-base";
    }

    /****************************************************************************************************
     * define the main properties of the component
     * reflected property names should always be lower case so it can be used directly on the element as an attribute
     *
     * @readonly
     * @static
     ****************************************************************************************************/
    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: String,
                value: '',
                observer: '_valueChanged'
            },
            colorsscheme: {
                type: String,
                value: ''
            },
            colors: {
                type: Array,
                value: () => { return [] }
            },
            targetinput: {
                type: Object,
                value: () => { return {} }
            },
            canvasheight: {
                type: String,
                value: "200",
            },
            canvaswidth: {
                type: String,
                value: "200",
            },
            markerwidth: {
                type: String,
                value: "20"
            },
            markerheight: {
                type: String,
                value: "20"
            },
            rgbred: {
                type: String,
                value: 'R'
            },
            rgbgreen: {
                type: String,
                value: 'G'
            },
            rgbblue: {
                type: String,
                value: 'B'
            },
            hex: {
                type: String,
                value: 'HEX #'
            },
            hslhue: {
                type: String,
                value: 'H'
            },
            hslsaturation: {
                type: String,
                value: 'S'
            },
            hslvalue: {
                type: String,
                value: 'V'
            },
            css: {
                type: Object,
                value: () => { return {} },
                observer: '_updateCSSProperties'
            },
            inputfont: {
                type: Object,
                value: () => { return {} },
                observer: '_updateInputFont',
            },
            buttonfont: {
                type: Object,
                value: () => { return {} },
                observer: '_updateButtonFont',
            },
            labelfont: {
                type: Object,
                value: () => { return {} },
                observer: '_updateLabelFont',
            },
        }
        return props;
    }

    /****************************************************************************************************
     * Called when the element has been created,
     * but before property values are set and local DOM is initialized. 
     *Creates an instance of LasalRuntimeSigControlColorpickerBase.
     ****************************************************************************************************/
    constructor() {
        super();
        this.grid = null;
        this.colorinputs = null;
        this.busy = false;
        this.parent = null;
        this.canvasContext = null;
        this.marker = null;
        this.mousedown = false;

        this.queue = [];
        this.queueBusy = false;


        this.colormap = [];
        this.colorhash = {};

        this.options = {};

        this.scaleFactor = undefined;
        this.canvasOffset = undefined;

        // needed since Q1/2021 release to receive changes of shadow dom elements
        this.notifyOnValueUpdatesInShadowDom = true;
    }

    /****************************************************************************************************
      * Called after property default values are set and local DOM is initialized.
     ****************************************************************************************************/
    ready() {
        super.ready();
    }

    /****************************************************************************************************
     * Builds the inputs, and the canvas
     * positions the control
     * adds event listeners
     ****************************************************************************************************/
    _draw() {
        this.colorinputs = this._buildColorInputs();
        if (!this.grid) {
            this.grid = this._buildColorSelectArea();
            this._setControlPosition();
            // this._addEventListeners();
        }
    }

    /****************************************************************************************************
     * Eventlisteners for 'static' content are added here
     * there is content that gets created dynamically. those listeners are added when the content is created
     ****************************************************************************************************/
    _addEventListeners() {

        // Event handler for the buttons
        this.sigAddEventListener('tap', (e) => {
            this._okButtonPointerdownHandler(e);
        }, 'polymerEvent', this.$.OK);
        this.sigAddEventListener('tap', (e) => {
            this._cancelButtonPointerdownHandler(e);
        }, 'polymerEvent', this.$.CANCEL);
        this.sigAddEventListener('tap', (e) => {
            this._showColorinputsButtonPointerdownHandler(e);
        }, 'polymerEvent', this.$.SHOWCOLORINPUTS);
    }

    /****************************************************************************************************
     * From sig-element
     ****************************************************************************************************/
    connectedCallback() {
        super.connectedCallback();
        this._addEventListeners();
    }

    /****************************************************************************************************
     * Handler for the OK controlbutton
     * Sets the parent value
     * closes the colorpucker
     ****************************************************************************************************/
    _okButtonPointerdownHandler() {
        this.parent.showvalue = this.value;
        this.parent.markerpos.colorpicker1act = this.parent.markerpos.colorpicker1last;
        this.parent._closeColorpicker();
    }

    /****************************************************************************************************
     * Handler for the CANCEL controlbutton
     * closes the colorpicker without setting the value
     ****************************************************************************************************/
    _cancelButtonPointerdownHandler() {
        this.parent._closeColorpicker();
    }

    /****************************************************************************************************
     * Handler for the SHOWCOLORINPUTS controlbutton
     * shows the rgb, hex, hsl inputs
     ****************************************************************************************************/
    _showColorinputsButtonPointerdownHandler() {
        this.$.colorinputs.classList.toggle('hidden');
        this._setControlPosition();
    }

    /****************************************************************************************************
     * Called when a rgb input changes its value 
     * @param {*} e
     * @returns
     ****************************************************************************************************/
    _rgbInputsChange(e) {
        if (this.busy) {
            return;
        }
        const rgbArray = this._getInputValues('rgbcontainer');
        this._updateInputs(rgbArray);
    }

    /****************************************************************************************************
     * Called when a hex input changes its value
     * @param {*} e
     * @returns
     ****************************************************************************************************/
    _hexInputsChange(e) {
        if (this.busy) {
            return;
        }
        const hexArray = this._getInputValues('hexcontainer');
        const rgbArray = this._hextorgb(hexArray);
        this._updateInputs(rgbArray);
    }

    /****************************************************************************************************
     * Calls when a hsl input changes its value
     * @param {*} e
     * @returns
     ****************************************************************************************************/
    _hslInputsChange(e) {
        if (this.busy) {
            return;
        }
        const hslArray = this._getInputValues('hslcontainer');
        const rgbArray = this._hsltorgb(hslArray);
        this._updateInputs(rgbArray);
    }

    /****************************************************************************************************
     * is called after an Input was changed
     * @param {Array} rgbArr The rgb value of the color in array form
     * converts the rgb value to hsl and hex, and sets the inputs to those values
     ****************************************************************************************************/
    _updateInputs(rgbArr) {
        /**
         * Make sure there is opacity in the rgbArray
         * */
        if (rgbArr.length === 3) {
            rgbArr.push(255)
        }

        this.busy = true;

        // Convert to a normal Array, since HEX has problems with uint8clampedarray
        let rgbArray = [];
        for (let color of rgbArr) {
            rgbArray.push(color)
        }

        //Convert to hsl
        const hslArray = this._rgbtohsl(rgbArray);

        //Convert to HEX
        const hexArray = this._rgbtohex(rgbArray);

        //Get the inputs
        const rgbInputs = this.$.rgbcontainer.querySelectorAll('sig-control-input');
        const hexInputs = this.$.hexcontainer.querySelectorAll('sig-control-input');
        const hslInputs = this.$.hslcontainer.querySelectorAll('sig-control-input');

        //If we have the inputs, set their values
        if (rgbInputs.length > 0) {
            rgbInputs[0].value = rgbArray[0];
            rgbInputs[1].value = rgbArray[1];
            rgbInputs[2].value = rgbArray[2];
        }
        if (hexInputs.length > 0) {
            /* hexInputs[0].value = hexArray[0];
            hexInputs[1].value = hexArray[1];
            hexInputs[2].value = hexArray[2]; */
            hexInputs[0].value = '' + hexArray[0] + hexArray[1] + hexArray[2];
        }
        if (hslInputs.length > 0) {
            hslInputs[0].value = hslArray[0];
            hslInputs[1].value = hslArray[1];
            hslInputs[2].value = hslArray[2];
        }

        //Convert the rgbArray to a rgba string
        let colString = '';
        if (rgbArray.length === 4) {
            colString = "rgba(" + rgbArray.join(',') + ')';
        } else {
            colString = "rgba(" + rgbArray.join(',') + ",1)";
        }

        //Set the preview (color that is shown in the input while the colorpicker is open)
        if (this.parent && this.parent.colorpickeropen) {
            this.parent.preview = colString;
        }

        //Set the value of the colorpicker
        this.value = rgbArray;

        this.busy = false;
    }

    /****************************************************************************************************
     * To check if something changed to one of the color array
     * @param {*} arr1
     * @param {*} arr2
     * @returns
     ****************************************************************************************************/
    _arraysEqual(arr1, arr2) {
        if (arr1.length !== arr2.length) {
            return false;
        }

        
        //   i = arr.length
        //   i-- => i is the last useable index of the array
          
        //   continues to go through the array until i == 0,
        //   then stops since that is false
         
        for (let i = arr1.length; i--;) {
            if (arr1[i] !== arr2[i]) {
                return false;
            }
        }

        return true;
    }

    /****************************************************************************************************
     * Handles the pointerdown event
     * @param {*} e
     ****************************************************************************************************/
    pointerDownHandler(e) {
        this.sigPreventDefault(e);

        //get the canvas offset in the pointer down handler
        //needs to be done, because this value can change if the colorpicker is opened in a windwow
        //it should be sufficient to only do this in the pointerDownHandler, since that is where all interaction starts
        this.canvasOffset = this.$.colorSelectArea.querySelector('canvas').getBoundingClientRect();
        this.mousedown = true;
        this._setColor(e);
    }
    
    /****************************************************************************************************
     * Handles the pointerup event
     * @param {*} e
     ****************************************************************************************************/
    pointerUpHandler(e) {
        this.sigPreventDefault(e);
        this.mousedown = false;
    }
   
    /****************************************************************************************************
     * Handles the pointermove event
     * @param {*} e
     ****************************************************************************************************/
    pointerMoveHandler(e) {
        this.sigPreventDefault(e);
        if (!this.mousedown) {
            return
        }

        this._setColor(e);
    }

    /****************************************************************************************************
     * Handles the pointerleave event
     * @param {*} e
     ****************************************************************************************************/
    pointerLeaveHandler(e) {
        this.sigPreventDefault(e);
        this.mousedown = false;
    }

    /****************************************************************************************************
     * Builds the Color Inputs
     * @returns
     ****************************************************************************************************/
    _buildColorInputs() {

        const colorinputs = this.$.colorinputs;

        //Get the Inputs and the labels
        const rgbInputs = this.$.rgbcontainer.querySelectorAll('sig-control-input');
        const rgblabels = this.$.rgbcontainer.querySelectorAll('label');

        const hexInputs = this.$.hexcontainer.querySelectorAll('sig-control-input');
        const hexlabels = this.$.hexcontainer.querySelectorAll('label');

        const hslInputs = this.$.hslcontainer.querySelectorAll('sig-control-input');
        const hsllabels = this.$.hslcontainer.querySelectorAll('label');

        //Set the Labels to the texts
        rgblabels[0].innerText = this.rgbred;
        rgblabels[1].innerText = this.rgbgreen;
        rgblabels[2].innerText = this.rgbblue;

        hexlabels[0].innerText = this.hex;

        hsllabels[0].innerText = this.hslhue;
        hsllabels[1].innerText = this.hslsaturation;
        hsllabels[2].innerText = this.hslvalue;

        if(this.options.firstOpened){
            //Add listeners to the value changed events of the inputs
            //If the value of an input changes, the other inputs need to be updated
            for (let i = 0; i < 3; i++) {
                this.sigAddEventListener('value-changed', this._rgbInputsChange.bind(this), 'jsEvent', rgbInputs[i]);
                this.sigAddEventListener('value-changed', this._hslInputsChange.bind(this), 'jsEvent', hslInputs[i]);
            }
    
            // only one hex input
            this.sigAddEventListener('value-changed', this._hexInputsChange.bind(this), 'jsEvent', hexInputs[0]);
        }

        return colorinputs;
    }

    /****************************************************************************************************
     * Shorthand to create an element
     * can set id, classlist, this.options(for sig-controls), and children
     * all in one line instead of several
     * @param {string} [name='div']
     * @param {string} [id='']
     * @param {*} [classList=[]]
     * @param {*} [options={}]
     * @param {*} [children=[]]
     * @returns
     ****************************************************************************************************/
    _createElement(name = 'div', id = '', classList = [], options = {}, children = []) {
        //creates the element of type 'name'
        const element = document.createElement(name);
        //sets the id
        element.id = id;
        //adds the classes that where specified in an array
        //classList.add(...[a,b,c]) === classList.add(a,b,c) which would add those 3 classes to the classList
        element.classList.add(...classList);
        //A way to add options
        //{height:200, width:200} would add element.height = 200; element.width=200 to the element
        for (let option in options) {
            if (option) {
                element[option] = options[option];
            }
        }
        //If the children array isn't empty, it is looped through here, and each child is appended to the element
        if (children.length > 0) {
            for (let child of children) {
                element.appendChild(child);
            }
        }
        return element;

    }

    /****************************************************************************************************
     * Gets all the input values that are in the inputarea
     * the input area is an id
     * @param {*} inputarea
     * @returns
     ****************************************************************************************************/
    _getInputValues(inputarea) {
        //inputarea is either 'rgbcontainer' 'hexcontainer' or 'hslcontainer'
        //From there the inputs are taken
        const inputs = this.colorinputs.children[inputarea].querySelectorAll('sig-control-input');
        let inputValues = [];
        for (let input of inputs) {
            //Each inputs value is added to the inputValues array
            //If the value should be empty, 0 is added to the array
            inputValues.push(input.value === '' ? '0' : input.value);
        }
        if(inputarea === 'hexcontainer') {
            inputValues = [inputValues[0].slice(0,2), inputValues[0].slice(2,4), inputValues[0].slice(4,6)];
        }
        return inputValues;
    }

    /****************************************************************************************************
     * Returns a Map with the color of each pixel of the canvas
     * Needed to set the Marker when something is set in the input fields for example
     * @param {*} canvas
     * @returns
     ****************************************************************************************************/
    _getColorMap(canvas) {
        const canvasContext = canvas.getContext('2d');
        // Uint8ClampedArray
        const imagedata = canvasContext.getImageData(0, 0, canvas.width, canvas.height).data;
        let stringifiedArray = [];
        for (let i = 0; i < imagedata.length; i += 4) {
            stringifiedArray.push(JSON.stringify(imagedata.slice(i, 4)));
        }
        return stringifiedArray;
    }

    /****************************************************************************************************
     * Calculates the remaining height for the colorselect area
     * Basically the height of the control minus the height of the control buttons
     * @returns
     ****************************************************************************************************/
    _calculateHeightForColorSelectArea() {
        //The bounds of the controlButtons
        //const controlButtonsBoundingClientRect = this.getControlBounds(this.$.controlButtons);
        const controlButtonsHeight = this.$.controlButtons.offsetHeight;
        //The scaled bounds of the parent needed for a simple scaling-factor calculation
        // const parentBoundingClientRect = this.options.parent.getBoundingClientRect();
        //The unscaled bounds of the parent
        // const parentControlBounds = this.options.parent.getControlBounds();
        //get the scaleFactor
        // const scaleFactor = parentControlBounds.height / parentBoundingClientRect.height;

        //The height of the ControlButtons
        //const controlButtonsHeight = controlButtonsBoundingClientRect.height;
        //Get the height left for the colorSelectArea
        //It is the height of the colorpicker minus the height of the control buttons
        const colorSelectAreaHeight = this.options.height - controlButtonsHeight;

        return colorSelectAreaHeight;
    }

    /****************************************************************************************************
     * returns the boundingClientRect for the inputs
     * is its own function because it is sometimes hidden, and therefore needs more queries
     * @returns
     ****************************************************************************************************/
    _getColorInputsBoundingBox() {
        let boundingBox;
        // Returns null if the element or one of its parents are hidden
        if (this.colorinputs.offsetParent === null) {
            this.colorinputs.classList.toggle('hidden');
            boundingBox = this.colorinputs.getBoundingClientRect();
            this.colorinputs.classList.toggle('hidden');
        } else {
            boundingBox = this.colorinputs.getBoundingClientRect();
        }
        return boundingBox;
    }

    /****************************************************************************************************
     * Returns a rgba color from an Array
     * @param {*} color
     * @returns
     ****************************************************************************************************/
    _getRGBA(color) {
        return `rgba(${color.slice(0,4).join(',')})`;
    }

    /****************************************************************************************************
     * Takes a rgb css value like rgb(10,10,10),
     * and returns an Array [10,10,10]
     * @param {*} cssRGB
     * @returns
     ****************************************************************************************************/
    _rgbArrayFromCSSValue(cssRGB) {
        // The Regex matches stuff between brackets,
        // Since that would include the brackets, we group the stuff within the brackets,
        // and can access it on position [1].
        // then the string is split, to get an arry with the rgb values 
        let rgbArray = cssRGB.match(/\((.*?)\)/)[1].split(',');
        return rgbArray;
    }

    /****************************************************************************************************
     * Takes a rgb array [10,10,10],
     * and returns a hex string 0a0a0a
     * @param {*} rgb
     * @returns
     ****************************************************************************************************/
    _rgbtohex(rgb) {
        let hex = rgb.map((number) => {
            let conv = parseInt(number).toString(16);
            return conv.length === 1 ? '0' + conv : conv;
        });
        return hex;
    }

    /****************************************************************************************************
     * https://stackoverflow.com/a/11508164
     * Takes a hex string ffaa31
     * and returns a rgb array [255,170,49]
     * @param {*} hex
     * @returns
     ****************************************************************************************************/
    _hexStringToRGB(hex) {
        var bigint = parseInt(hex, 16);
        var r = (bigint >> 16) & 255;
        var g = (bigint >> 8) & 255;
        var b = bigint & 255;

        return [r, g, b];
    }

    /****************************************************************************************************
     * gets a hex array
     * and returns a rgb array
     * @param {*} hex
     * @returns
     ****************************************************************************************************/
    _hextorgb(hex) {
        const r = parseInt(hex[0], 16) & 255;
        const g = parseInt(hex[1], 16) & 255;
        const b = parseInt(hex[2], 16) & 255;
        return [r, g, b];
    }

    /****************************************************************************************************
     * https://www.rapidtables.com/convert/color/rgb-to-hsl.html
     * takes a rgb array [10,11,12]
     * and returns a hsl array [210, 0.166666, 0.04705...]
     * @param {*} rgb
     * @returns
     ****************************************************************************************************/
    _rgbtohsl(rgb) {
        let r1 = rgb[0] / 255;
        let g1 = rgb[1] / 255;
        let b1 = rgb[2] / 255;

        let cMax = Math.max(r1, g1, b1);
        let cMin = Math.min(r1, g1, b1);

        let delta = cMax - cMin;
        let h, s, v;

        // Hue
        if (delta === 0) {
            h = 0;
        } else {
            switch (cMax) {
                case r1:
                    h = 60 * (((g1 - b1) / delta) % 6);
                    break;
                case g1:
                    h = 60 * (((b1 - r1) / delta) + 2);
                    break;
                case b1:
                    h = 60 * (((r1 - g1) / delta) + 4);
                    break;
            }
        }
        // otherwise I sometimes get negative h values
        if (h < 0) {
            h = 360 + h;
        }

        //Value
        v = (cMax + cMin) / 2;

        //Saturation
        switch (delta) {
            case 0:
                s = 0;
                break;
            default:
                s = delta / (1 - Math.abs(2* v - 1));
                break;
        }

        

        return [h, s, v];

    }

    /****************************************************************************************************
     * https://www.rapidtables.com/convert/color/hsl-to-rgb.html
     * takes a hsl array [210, 0.167,0.047] 
     * and returns a rgb array [9.98,10.98,11.98]
     * @param {*} hsl
     * @returns
     ****************************************************************************************************/
    _hsltorgb(hsl) {
        let h = hsl[0];
        let s = hsl[1];
        let v = hsl[2];

        let c = (1 - Math.abs(2*v - 1)) * s;

        let x = c * (1 - Math.abs((h / 60) % 2 - 1));
        let m = v - c / 2;

        let r1, g1, b1;
        if (h < 60) {
            r1 = c;
            g1 = x;
            b1 = 0;
        } else if (h < 120) {
            r1 = x;
            g1 = c;
            b1 = 0;
        } else if (h < 180) {
            r1 = 0;
            g1 = c;
            b1 = x;
        } else if (h < 240) {
            r1 = 0;
            g1 = x;
            b1 = c;
        } else if (h < 300) {
            r1 = x;
            g1 = 0;
            b1 = c;
        } else if (h < 360) {
            r1 = c;
            g1 = 0;
            b1 = x;
        }

        let r, g, b;
        r = (r1 + m) * 255;
        g = (g1 + m) * 255;
        b = (b1 + m) * 255;

        return [r, g, b];
    }

    /****************************************************************************************************
     * Converts from rgb/hsl/hex to rgb/hsl/hex
     * @param {*} from
     * @param {*} to
     * @param {*} val
     * @returns
     ****************************************************************************************************/
    _convertFromTo(from, to, val) {
        switch (from) {
            case 'rgb':
                switch (to) {
                    case 'rgb':
                        return val;
                        break;
                    case 'hex':
                        return this._rgbtohex(val);
                        break;
                    case 'hsl':
                        return this._rgbtohsl(val);
                        break;
                }
                break;
            case 'hex':
                switch (to) {
                    case 'rgb':
                        return val;
                        break;
                    case 'hex':
                        return this._rgbtohex(val);
                        break;
                    case 'hsl':
                        return this._rgbtohsl(val);
                        break;
                }
                break;
            case 'hsl':
                switch (to) {
                    case 'rgb':
                        return val;
                        break;
                    case 'hex':
                        return this._rgbtohex(val);
                        break;
                    case 'hsl':
                        return this._rgbtohsl(val);
                        break;
                }
                break;
        }
    }

    /****************************************************************************************************
     * Observer for the value property
     * sets the parents preview
     * @param {*} newval
     * @param {*} oldval
     ****************************************************************************************************/
    _valueChanged(newval, oldval) {
        if (this.parent) {
            this.parent.preview = newval;
        }
    }

    /****************************************************************************************************
     * Called when the Component should be shown
     * Sets Texts, Styles
     * Draws the component
     * Sets the position, and initializes the component
     * @param {*} options
     ****************************************************************************************************/
    _show(options) {

        this.options = options;
        this.style.display = 'initial';


        let styles = {};

        // If the markerwidth was set, make the marker that width
        if (options.markerwidth) {
            this.markerwidth = this.options.markerwidth;
            styles['--theme-sig-control-colorpicker-bar-marker-width'] = this.markerwidth + 'px';
        }
        // If the markerheight was set, make the marker that height
        if (options.markerheight) {
            this.markerheight = this.options.markerheight;
            this.markerwidth = this.options.markerwidth;
            styles['--theme-sig-control-colorpicker-bar-marker-height'] = this.markerheight + 'px';
        }
        //If there are text for the buttons, give the buttons that text
        if (options.controlTexts) {
            this.$.controlButtons.children.OK.sigbtntext = options.controlTexts.Ok;
            this.$.controlButtons.children.CANCEL.sigbtntext = options.controlTexts.Cancel;
            this.$.controlButtons.children.SHOWCOLORINPUTS.sigbtntext = options.controlTexts.Inputs;
            //Sets the Label Texts for the inputs
            Object.keys(options.controlTexts).forEach((key) => {
                this[key] = options.controlTexts[key];
            })
        }

        // Check if the bgimage for the buttons should be inherited from the OFF state
        // and set the buttons variable accordingly
        if (!options.btnInheritBgImgFromInactive) {
            const buttons = this.$.controlButtons.children;
            for (let button of buttons) {
                button.inheritbgimg = false;
            }
        }

        if(!this.options.showInputs) {
            this.$.SHOWCOLORINPUTS.style.setProperty('display', 'none');
        }

        //Set the styles for maker-width and -height
        this.updateStyles(styles);
        //Gets the styles from the parent element, that are ment for this element
        this.css = this._getStyles();
        // Draws the colorpicker
        this._draw();
        //Set the font for the labels
        this.labelfont = options.labelfont;
        //Set the font for the inputs
        this.inputfont = options.inputfont;
        //Set the font for the buttons
        this.buttonfont = options.buttonfont;
        //Hide the inputs
        this.$.colorinputs.classList.add('hidden');
        //Sets the controls position. Makes sure the control isn't out of bounds
        this._setControlPosition();
        //If the parent has a marker position 
        //Set the marker to that position
        //Then set the color to the color of those coordinates
        this._initializeOnOpen(options.parent.showvalue)


    }

    /****************************************************************************************************
     * Sets the Inputs to the parents value
     * Moves the Marker if the color can be found on the canvas
     * @param {*} value
     ****************************************************************************************************/
    _initializeOnOpen(value) {
        if (value) {
            let offsetX = 0;
            let offsetY = 0;

            //Check if the parent has a marker position, else set the offset to 0
            if (this.parent.markerpos.colorpicker1act) {
                offsetX = this.parent.markerpos.colorpicker1act.x || 0;
                offsetY = this.parent.markerpos.colorpicker1act.y || 0;
            }

            //Set the color to the color of the maker position
            this.queue.push({
                funct: '_setColor', params: [
                    {
                        offsetX,
                        offsetY: Number(offsetY) + Number(this.options.areapaddingtop)
                    }]
            });
            this._doQueue();
        }
    }
    
    /****************************************************************************************************
     * Sets the control position
     * Makes sure the control isn#t out of bounds
     * For dropdowns it also makes sure the dropdown isnt covering the input
     ****************************************************************************************************/
    _setControlPosition() {
        this.updateStyles({
            '--theme-sig-element-top': 0 + 'px',
            '--theme-sig-element-left': 0 + 'px'
        });
        // Should return the control Bounds of the screen
        // const screenBounds = this.closest('sig-app').getControlBounds();
        // Control Bounds of the Parent
        // const pControlBounds = this.options.parent.getControlBounds();
        // Control Bounds of the Control itself
        // const cControlBounds = this.getControlBounds();

        //Check if the colorpicker is a window or dropdown, and call the correct function
        if (this.options.type === 'window') {
            this._setControlPositionWindow();
        } else {
            this._setControlPositionDropdown();
        }
    }

    /****************************************************************************************************
     * Sets the Control Position depending on how the control is rotated
     ****************************************************************************************************/
    _setControlPositionDropdown() {
        this._setPosition();
    }

    /****************************************************************************************************
     * Set Position
     * From sig-control-enum-list.html
     * @param {*} position
     ****************************************************************************************************/
    _setPosition(position) {
        if (this.parent instanceof HTMLElement) {
            //Opacity to 0.01 so that the component isn't visible
            //NOT display: none or opacity 0 because then the control bounds woudl be 0
            this.style.opacity = '0.01';

            SigPolymer.afterNextRender(this, function (params) {
                let rotation = 0;
                let scaleFactorApp = {
                    x: 1,
                    y: 1
                };
                let scaleFactorParent = {
                    x: 1,
                    y: 1
                };
                let windowOffset = {
                    x: 0,
                    y: 0
                };

                //We need to use getBoundingClientRect(); no other performant way to get position relative to the viewport!
                const parentBounds = this.parent.getBoundingClientRect();
                // const pathRotation = parseInt(this.parent.realrotation, 10);
                // this._log("Path Rotation: " + pathRotation);
                if (this.isdesignmode) {
                    // In designmode we need to calculate the real rotation 
                    rotation = this._getRealRotation(parseInt(this._getPathRotation(this.parent, this.parent.rotation, 10)));
                } else {
                    // In the normal runtime we get the precalculated value
                    rotation = this._getRealRotation(parseInt(this.parent.realrotation, 10));
                }
                this._log("Real Rotation: " + rotation);

                // It seems that getBoundingClientRect() does not take the scaling
                // into account. So we need to calulcate the scalingfactor of the 
                // container in which the enum list is created to normalize the position!
                // No need to calculate this in the designer runtime, there is no scaling applied
                if (!this.isdesignmode) {
                    scaleFactorApp = this._getScaleFactor(this.parentNode, 0);
                    // If we run inside the window context we need to caluclate the window offset
                    if (this.parent.context.type === "window") {
                        const window = document.querySelector(`#${this.parent.context.instanceId} .${this.windowContainerClass}`);
                        if (window instanceof HTMLElement) {
                            const windowBounds = window.getBoundingClientRect();
                            windowOffset.x = (windowBounds.left / scaleFactorApp.x) - window.scrollLeft;
                            windowOffset.y = (windowBounds.top / scaleFactorApp.y) - window.scrollTop;
                        }
                    }
                }

                // We nedd to calculate the absolute scale factor of the enum parent control
                // so we can scale the enum optiolist entries
                // No need to calculate this in the designer runtime, there is no scaling applied
                if ((!this.isdesignmode) && (this.scalelist)) {
                    scaleFactorParent = this._getScaleFactor(this.parent, rotation);
                    this.$.list.style.transform = `scale( 1, ${scaleFactorParent.y})`;
                    this.$.list.style.transformOrigin = `0 0`;
                }

                // We need to remove the css vars for height/width while positioning the list, or the
                // container will get caluclated wrong if the options are updated while the list is visible!
                // This may cause some flickering on real slow devices :-(
                if (!this.hidden) {
                    this._removeDimension();
                }

                let top = 0,
                    left = 0,
                    height = this.offsetHeight * scaleFactorParent.y,
                    width = this.offsetWidth * scaleFactorParent.x;

                const pTop = parentBounds.top / scaleFactorApp.y;
                const pLeft = parentBounds.left / scaleFactorApp.x;
                const pHeight = parentBounds.height / scaleFactorApp.y;
                const pWidth = parentBounds.width / scaleFactorApp.x;
                const pBottom = parentBounds.bottom / scaleFactorApp.y;
                const pRight = parentBounds.right / scaleFactorApp.x;

                const offsetX = 1;
                const offsetY = 1;

                switch (position) {
                    case 'top':
                        if (rotation === 90) {
                            if (width < pHeight) {
                                width = pHeight;
                            }
                            left = pRight + offsetY;
                            top = pTop;
                        } else if (rotation === 180) {
                            if (width < pWidth) {
                                width = pWidth;
                            }
                            left = pLeft - (width - pWidth);
                            top = pBottom + offsetY;
                        } else if (rotation === 270) {
                            if (width < pHeight) {
                                width = pHeight;
                            }
                            left = pLeft - height - offsetY;
                            top = pTop - (width - pHeight);
                        } else { // all other angles
                            if (width < pWidth) {
                                width = pWidth;
                            }
                            left = pLeft;
                            top = pTop - height - offsetY;
                        }
                        break;
                    case 'left':
                        if (rotation === 90) {
                            left = pRight - height;
                            top = pTop - width - offsetX;
                        } else if (rotation === 180) {
                            left = pRight + offsetX;
                            top = pBottom - height;
                        } else if (rotation === 270) {
                            left = pLeft;
                            top = pBottom + offsetX;
                        } else { // all other angles
                            left = pLeft - width - offsetX;
                            top = pTop;
                        }
                        break;
                    case 'right':
                        if (rotation === 90) {
                            left = pRight - height;
                            top = pBottom + offsetX;
                        } else if (rotation === 180) {
                            left = pLeft - width - offsetX;
                            top = pBottom - height;
                        } else if (rotation === 270) {
                            left = pLeft;
                            top = pTop - width - offsetX;
                        } else { // all other angles
                            left = pRight + offsetX;
                            top = pTop;
                        }
                        break;
                    default: // bottom
                        if (rotation === 90) {
                            if (width < pHeight) {
                                width = pHeight;
                            }
                            left = pLeft - height - offsetY;
                            top = pTop;
                        } else if (rotation === 180) {
                            if (width < pWidth) {
                                width = pWidth;
                            }
                            left = pLeft - (width - pWidth);
                            top = pTop - height - offsetY;
                        } else if (rotation === 270) {
                            if (width < pHeight) {
                                width = pHeight;
                            }
                            left = pRight + offsetY;
                            top = pTop - (width - pHeight);
                        } else {
                            if (width < pWidth) {
                                width = pWidth;
                            }
                            left = pLeft
                            top = pBottom + offsetY;
                            break;
                        }
                }

                // We need to correct the position if the viewport is scrolled
                // or the context is window and we have a windows offset
                left += window.pageXOffset - windowOffset.x;
                top += window.pageYOffset - windowOffset.y;

                // const screenBounds = this.closest('sig-app').getControlBounds();
                // let screenBounds = this.closest('.jsPanel-content');
                // if(!screenBounds) {
                //     screenBounds = this.closest('sig-app');
                // }
                if (left < 0) {
                    left = 0;
                }
                // const screenBounds = this.closest('sig-app').getControlBounds();
                const screenBounds = {
                    width: this.closest('sig-app').style.width,
                    height: this.closest('sig-app').style.height
                }

                if (top > screenBounds.height) {
                    top = screenBounds.height - height;
                }
                if (left + width > screenBounds.width) {
                    left = screenBounds.width - width;
                }

                if (top < 0) {
                    top = 0;
                }
                if (left < 0) {
                    left = 0;
                }

                switch (rotation) {
                    case 0:
                    case 180:
                        if (top + height > screenBounds.height) {
                            top = screenBounds.height - height;
                        }
                        if (left + width > screenBounds.width) {
                            left = screenBounds.width - width;
                        }
                        break;
                    case 90:
                    case 270:
                        if (top > screenBounds.height) {
                            top = screenBounds.height - width;
                        }
                        if (left + height > screenBounds.width) {
                            left = screenBounds.width - height;
                        }
                        break;
                }
                // We only apply the rotation if it has changed
                // This is needed to make sure the translate gets updated 
                // correctly and avoid double paints
                if (rotation === this.oldRotation) {
                    this._setBounds(top, left, height, width);
                } else {
                    this._setBounds(top, left, height, width, rotation);
                }
                this._log(`Set bounds of "${this.id}" to "T: ${top}, L: ${left}, H: ${height}, W:${width}, R: ${rotation}deg"`);
                this.oldRotation = rotation;
                this.style.opacity = '1';
                this._makeVisible();
            });
        }
    }

    /****************************************************************************************************
     * returns the scaleFactor
     * @param {*} [control=this]
     * @param {number} [rotation=0]
     * @returns
     ****************************************************************************************************/
    _getScaleFactor(control = this, rotation = 0) {
        let scaleFactor = { x: 1, y: 1 };
        if (control instanceof HTMLElement) {
            if (rotation === 90 || rotation === 270) {
                scaleFactor.y = control.getBoundingClientRect().height / control.offsetWidth;
                scaleFactor.x = control.getBoundingClientRect().width / control.offsetHeight;
            } else {
                scaleFactor.y = control.getBoundingClientRect().height / control.offsetHeight;
                scaleFactor.x = control.getBoundingClientRect().width / control.offsetWidth;
            }
        }
        this._log(`Calculated scale factor for rotateion ${rotation} is X = "${scaleFactor.x}" Y = "${scaleFactor.y}`);
        return scaleFactor;
    }

    /****************************************************************************************************
     * returns the rotation
     * @param {number} [rotation=0]
     * @returns
     ****************************************************************************************************/
    _getRealRotation(rotation = 0) {
        return (rotation % 360 + 360) % 360;
    }

    /****************************************************************************************************
     * Removes the sig-element-width and -heigth
     ****************************************************************************************************/
    _removeDimension() {
        // We need to remove the applied the css vars to fallback default width
        this.style.removeProperty('--theme-sig-element-width');
        this.style.removeProperty('--theme-sig-element-height');
    }

    /****************************************************************************************************
     * If the element isn't visible, this function makes it visible
     ****************************************************************************************************/
    _makeVisible() {
        // We delay making the list visible till the first positioning is done. 
        // This avoids that the unpositioned list pops up for a second.
        if (this.style.visibility === 'hidden' && !this.isVisible) {
            this.sigSetTimeout(() => {
                this.style.removeProperty('visibility');
                this.isVisible = true;
            }, this.setVisibleDelay);
        }
    }

    /****************************************************************************************************
     * Gets the rotation for the element + the parents rotation
     * @param {*} control
     * @param {*} pathRotation
     * @returns
     ****************************************************************************************************/
    _getPathRotation(control, pathRotation) {
        let parent = control.parentNode;
        if (parent.tagName.toLowerCase() !== 'sig-composite-control') {
            return pathRotation;
        }
        let isCompositeContainer = true;
        let rotation = pathRotation;
        while (isCompositeContainer) {
            if (parent.tagName.toLowerCase() === 'sig-composite-container') {
                rotation += parseInt(parent.rotation, 10);
                this._log(`Parent: ${parent.id}: ${parent.rotation} / GES: ${rotation}`);
                parent = parent.parentNode;
            } else {
                isCompositeContainer = false;
            }
        }
        return rotation;
    }

    /****************************************************************************************************
     * Sets the control position inside a window
     * which should always be top: 0, left 0
     ****************************************************************************************************/
    _setControlPositionWindow() {
        let top, left
        this.updateStyles({
            '--theme-sig-element-top': 0 + 'px',
            '--theme-sig-element-left': 0 + 'px'
        });
        // Should return the control Bounds of the screen
        // const screenBounds = this.closest('sig-app')._getCurrentBounds();
        // Control Bounds of the Parent
        // const pControlBounds = this.options.parent.getControlBounds();
        // Control Bounds of the Control itself
        // const cControlBounds = this.getControlBounds();
        // If the colorpicker is opened in window mode, 
        // there is no need for special positioning
        if (this.options.type === 'window') {
            top = 0;
            left = 0;
            //this._sizeWindow(cControlBounds);
            this._sizeWindow({
                height: this.offsetHeight + 5,
                width: this.offsetWidth + 5
            })
            this._setWindowPosition();
        }

    }

    /****************************************************************************************************
     * If the colorpicker is shown in a window, 
     * set the window size correctly
     * @param {*} controlBounds
     ****************************************************************************************************/
    _sizeWindow(controlBounds) {
        // if(this.offsetParent === undefined || this.offsetParent === null) return;
        
        // Height of the window content
        this.offsetParent.style.height = controlBounds.height + 1 + 'px';

        this.offsetParent.style.width = controlBounds.width + 1 + 'px';
        // Width of the window
        this.offsetParent.offsetParent.style.width ='';
        this.offsetParent.offsetParent.style.height = '';
        this.offsetParent.offsetParent.resize();
    }

    /****************************************************************************************************
     * If the colorpicker is shown in a window
     * make sure the window is inside the bounds of the display
     ****************************************************************************************************/
    _setWindowPosition() {
        const window = this.offsetParent.offsetParent;
        //const windowBounds = this.getControlBounds(window);
        const screenBounds = {
            width: this.closest('sig-app').style.width,
            height: this.closest('sig-app').style.height
        }
        /* const wTop = windowBounds.top;
        const wLeft = windowBounds.left;
        const width = windowBounds.width;
        const height = windowBounds.height; */
        const wTop = window.offsetTop;
        const wLeft = window.offsetLeft;
        const width = window.offsetWidth;
        const height = window.offsetHeight
        let top = wTop;
        let left = wLeft;

        if (top + height > screenBounds.height) {
            top = screenBounds.height - height;
        }
        if (left + width > screenBounds.width) {
            left = screenBounds.width - width;
        }
        if (top < 0) {
            top = 0;
        }
        if (left < 0) {
            left = 0;
        }

        window.style.top = top + 'px';
        window.style.left = left + 'px';
    }

    /****************************************************************************************************
     * Helper function in case variables change before the component is ready to handle them
     * Implements a queue, that runs command
     * @returns
     ****************************************************************************************************/
    async _doQueue() {
        // return if the queue is already working
        if (this.queueBusy) {
            return;
        }
        this.queueBusy = true;

        //As long as there are elements in the queue, the queue runs
        while (this.queue.length > 0) {
            //get the first command in the queue
            const command = this.queue.shift()
            //execute command with the params
            await this[command.funct](...command.params);
        }


        this.queueBusy = false;
    }

    /****************************************************************************************************
     * Gets the Styles from the parent, that are relevant for the Control
     * taken from sig-control-list
     * @returns
     ****************************************************************************************************/
    _getStyles() {
        if (this.parent instanceof HTMLElement) {
            const cssObj = {};
            try { //There is no api to get all set css vars :-/, so we need to parse them
                const properties = this.parent.style.cssText.split(';');
                properties.forEach(property => {
                    if (property.trim().startsWith('--theme-sig-control-colorpicker-picker-')) {
                        const cssProp = property.split(':')[0].trim();
                        // We need to get the value via getPropertyValue for use in the designer
                        // because file paths containg a ":" will split incorrect!
                        cssObj[cssProp] = this.parent.style.getPropertyValue(cssProp);
                    }
                });
            } catch (error) {
                this._log(error.message, 'ERROR');
            }
            return cssObj;
        }
    }
    
    /****************************************************************************************************
     * Observer for the css property
     * Updates the style
     * @param {*} newval
     * @param {*} oldval
     ****************************************************************************************************/
    _updateCSSProperties(newval, oldval) {
        if (oldval !== undefined && newval instanceof Object) {
            if (Object.keys(newval).length > 0) {
                this.updateStyles(newval);
            }
        }
    }

    /****************************************************************************************************
     * Obsrever for the inputfont property
     * @param {*} newVal
     * @returns
     ****************************************************************************************************/
    _updateInputFont(newVal) {
        if (!newVal) {
            return;
        }
        const inputs = this.$.colorinputs.querySelectorAll('sig-control-input');
        inputs.forEach((input) => {
            this._applyFontObjectToNode(input, newVal);
        })

    }

    /****************************************************************************************************
     * Obsrever for the buttonfont property
     * @param {*} newVal
     * @returns
     ****************************************************************************************************/
    _updateButtonFont(newVal) {
        if (!newVal) {
            return;
        }
        const buttons = this.$.controlButtons.querySelectorAll('sig-control-button');
        buttons.forEach((button) => {
            this._applyFontObjectToNode(button, newVal);
        })

    }

    /****************************************************************************************************
     * Obsrever for the labelfont property
     * @param {*} newVal
     * @returns
     ****************************************************************************************************/
    _updateLabelFont(newVal) {
        if (!newVal) {
            return;
        }
        const labels = this.$.colorinputs.querySelectorAll('label');
        labels.forEach((label) => {
            this._applyFontObjectToNode(label, newVal);
        })
    }

    /****************************************************************************************************
     * Converts the coors from the Gesture Event e.detail.x e.detail.y to
     * the pointerevents offsetX offsetY
     * @param {*} e
     * @param {*} [canvasOffset=this.canvasOffset]
     * @returns
     ****************************************************************************************************/
    _convertGestureEventCoordsToUseableCoords(e, canvasOffset = this.canvasOffset) {
        // Get the scaöeFactpr of we haven't already
        if (!this.scaleFactor) {
            this.scaleFactor = this._getScaleFactor();
        }

        if (!canvasOffset) {
            this.canvasOffset = {};
            const canvas = e.target.parentNode.querySelector('canvas');
            const canvasBounds = canvas.getBoundingClientRect();
            this.canvasOffset.x = canvasBounds.x;
            this.canvasOffset.y = canvasBounds.y;

            canvasOffset = this.canvasOffset;
        }

        // The coordinates from the event
        const eX = e.detail.x;
        const eY = e.detail.y;

        // get the relative scaled coords
        const offsetX = eX - canvasOffset.x;
        const offsetY = eY - canvasOffset.y

        // converted into unscaled coordinates
        const convX = offsetX / this.scaleFactor.x;
        const convY = offsetY / this.scaleFactor.y;

        // Set the events offsetX and offsetY properties
        e.offsetX = convX;
        e.offsetY = convY;

        return e;
    }
}
customElements.define(LasalRuntimeSigControlColorpickerBase.is, LasalRuntimeSigControlColorpickerBase);
