/**  
 * @since Last modified: 2024-07-01 16:26:19
 * @class PlainElem
 * @description The father of all plain elements.
 * @example
 * class xxxElement extends PlainElem;
 */

import { T_obj } from '../../../types/ax';
import Observe from '../modules/Observe';
import attrJoinVal from '../utils/attrJoinVal';
import deepClone from '../utils/deepClone';
import getAttrBool from '../utils/getAttrBool';
import getDataType from '../utils/getDataType';
import isNull from '../utils/isNull';
import plan from '../utils/plan';
import setAttr from '../utils/setAttr';
import storage from '../utils/storage';

class PlainElem extends HTMLElement {
    propsRaw: any;
    properties: any;
    propsProxy: any;
    observer: any;
    custAttrs: any;
    boolAttrs: any;
    reset: any;
    clear: any;
    wrapEl: any;
    isCmpt: any;
    plans: any;
    initialized: any;
    set: any;
    timestamp: any;
    constructor() {
        super();
        this.propsRaw = {};
        this.properties = {};
        this.isCmpt = true;
        this.plans = {};
        
        this.timestamp = new Date().getTime();

        
        this.reset = () => {
            
            this.innerHTML = '';
            
            this.fillWrap(this.propsRaw);
            
            this.restoreAttrs();
            
            this.render(this.propsRaw);
        }
        
        this.clear = () => {
            this.removeAttribute('value');
        }
        
        this.set = (key: string, val: any) => {
            setAttr(this, key, val);
            this.listen({ name: 'set', params: [key, this.getAttribute(key)] });
        }
        
        this.on('init', () => {
            
             
             this.setAttribute('customized', '');
            
            this.setCache();
        });
        this.on('reset', () => {
            
            this.clearCache();
        })
    }
    
    disconnectedCallback() {
    }
    
    adoptedCallback() {
    }
    
    restoreAttrs() {
        for (let k in this.propsProxy) {
            let valProxy = this.propsProxy[k],
                valRaw = this.propsRaw[k];
            
            if (valProxy !== valRaw) {
                if (isNull(valRaw)) {
                    
                    this.removeAttribute(k);
                } else if (valRaw === true) {
                    this.setAttribute(k, '');
                } else if (valRaw === false) {
                    this.removeAttribute(k);
                } else {
                    this.setAttribute(k, valRaw);
                }
            }
        }
    }
    createObs() {
        
        this.observer = new Observe(this.properties);
        this.propsProxy = this.observer.proxy;
    }
    
    getPropVal(cmpt: any, name: string, val: string) {
        
        
        
        return cmpt.boolAttrs.includes(name) ? getAttrBool(val) : val;
    }
    
    getHtmlVal(name: string = 'label', unformatted: boolean = true) {
        let val = unformatted ? this.textContent?.trim() : this.innerHTML;
        return val || this.getAttribute(name);
    }
    
    getInitPropsRaw(cmpt: any) {
        [...cmpt.custAttrs, ...cmpt.lazyAttrs].forEach((k: string) => {
            this.propsRaw[k] = this.getAttribute(k);
        });
        cmpt.boolAttrs.forEach((k: string) => {
            this.propsRaw[k] = getAttrBool(this.getAttribute(k));
        });
    }
    
    getInitPropsProxy() {
        for (let k in this.propsRaw) {
            this.propsProxy[k] = this.propsRaw[k]
        }
    }
    connectedRender(data?: T_obj) {
        this.render(data);
        this.initialized = true;
        this.listen({ name: 'init' });
    }
    
    listen({ name, params = [], cb }: { name?: string, params?: any[], cb?: Function } = {}) {
        name && this.plans.hasOwnProperty(name) ? this.emit(name, ...params) : null;
        if (name && this.propsProxy[`on-${name.toLowerCase()}`]) {
            
            let fn = new Function('return' + this.propsProxy[`on-${name.toLowerCase()}`])();
            fn && fn.call(this, ...params);
        }
        cb && cb.call(this, ...params);
    }
    on(type: string, handler: Function): any {
        plan.add(type, this, handler);
        return this;
    }
    emit(type: string, ...params: any): any {
        plan.do(type, this, ...params);
        return this;
    }
    off(type: string, handler: Function): any {
        plan.remove(type, this, handler,);
        return this;
    }
    setCache() {
        let storName = this.propsProxy['stor-name'],
            cache = storage.get(storName);
        if (!storName || !cache) { return this }
        
        let copyCache = deepClone(cache),
            dataCache = copyCache['data'],
            hasData = copyCache.hasOwnProperty('data');
        
        if (hasData) {
            
            dataCache ? setAttr(this, 'data', dataCache) : this.removeAttribute('data');
            Reflect.deleteProperty(copyCache, 'data');
        }
        for (let k in copyCache) {
            setAttr(this, k, copyCache[k]);
        }
    }
    updateCache(data: T_obj) {
        let storName = this.propsProxy['stor-name'];
        if (!storName) { return this }
        
        let cache = storage.get(storName) || {};
        Object.assign(cache, data);
        storage.set(storName, cache); console.log(this.properties['stor-name'], storage.get(storName))
    }
    clearCache() {
        let storName = this.propsProxy['stor-name'];
        if (!storName) { return this }
        storage.clear(storName);
    }
    
    render(data?: T_obj) {

    }
    
    fillWrap(data?: T_obj) {

    }

}
window.customElements.define("ax-plain", PlainElem);
export default PlainElem;
