(function(PCG, glob){
    var svgNS = 'http://www.w3.org/2000/svg';

// I am too lazy to do DOM manually / anyway this solution is optimal enough

    var setters = {
        cls: function(el) {
            return function(cls) {
                if( el.tagName.toLowerCase() === 'svg' ){
                    el.setAttribute( 'class', cls );
                }else{
                    el.className = D.cls.apply(D, arguments);
                }
            }
        },
        attr: function(el, attrName) {
            return function(val) {
              if(val !== void 0 && val !== false){
                el.setAttribute( attrName, val );
              }else{
                el.removeAttribute(attrName)
              }
            }
        }
    };


    var used = {
        cls: true, className: true, 'class': true,
        attr: true, style: true, renderTo: true,
        prop: true,
        on: true
    };
// ~jsx h function
    var domEl = function( type, cfg ){

        if(typeof type === 'function'){
            return type(cfg);
        }
        cfg = cfg || {};
        var cls = cfg.cls || cfg['class'] || cfg.className,
            style = cfg.style,

            attr = cfg.attr || {},
            prop = cfg.prop,
            on = cfg.on || {},
            renderTo = cfg.renderTo,
            el = cfg.el || document.createElement( type ),
            classList = el.classList;

        var i, _i, name;

        for(i in cfg)
            if( cfg.hasOwnProperty(i)){
                name = i.toLowerCase();
                if(name in used)
                    continue;

                if(name.substr(0, 2) === 'on'){
                    on[ name.substr( 2 ) ] = cfg[ i ];
                }else{
                    attr[i] = cfg[i];
                }

            }

        if( cls ){
            if(typeof cls === 'function'){
                cls(setters.cls(el));
            }else{
                setters.cls(el)(cls);
            }
            //if(el.className !== cls)debugger
            //cls.split( ' ' ).forEach( function( clsItem ){ classList.add( clsItem ); });
        }

        if( style ){
            PCG.apply( el.style, style );
        }

        for( i in attr ){
            if(attr.hasOwnProperty( i )){
                if( typeof attr[ i ] === 'function' ){
                    attr[ i ]( setters.attr( el, i ) );
                }else{
                    setters.attr( el, i )( attr[ i ] );
                }
            }
        }

        for( i in prop ){
            prop.hasOwnProperty( i ) && ( el[ i ] = prop[ i ] );
        }

        for( i in on ){
            on.hasOwnProperty( i ) && el.addEventListener( i, on[ i ] );
        }

        for( i = 2, _i = arguments.length; i < _i; i++ ){
            var child = arguments[ i ];
            D.appendChild( el, child );
        }

        if( renderTo ){
            D.appendChild( renderTo, el );
        }

        return el;
    };

    var D = PCG.D = {
        svg: null,
        label: null,
        div: null,
        span: null,
        path: null,
        canvas: null,
        input: null,
        textarea: null,
        tBody: null,
        tHead: null,
        th: null,
        td: null,
        tr: null,
        Text: function( val ){ return document.createTextNode( val );}
    };
    'div,span,input,label,canvas,span,textarea,table,tr,td,th,tBody,tHead'.split( ',' ).forEach( function( name ){
        D[ name ] = function(){
            return domEl.apply( null, [ name ].concat([].slice.call(arguments)))
        };
    } );

    'svg,path,circle'.split( ',' ).forEach( function( name ){
        D[ name ] = function( cfg){
            if( !cfg ){
                cfg = {};
            }
            cfg.el = document.createElementNS( svgNS, name );
            cfg.el.setAttribute( 'xmlns', svgNS );
            return domEl.apply( null, [ null ].concat([].slice.call(arguments)))
        };
    } );

    D.html = function(cfg){
      var el = domEl('div', cfg);
      el.innerHTML =  [].slice.call(arguments,1).join('\n');
      return el;
    };
    D.h = domEl;
    D.removeChildren = function(el){
        var subEl;
        while((subEl = el.lastChild)){
            el.removeChild(subEl);
        }
    };

    D.appendChild = function(el, subEl){
        var type = typeof subEl;

        if(subEl === null){
            return ;
        }
        if( type !== 'object' ){
            if( type === 'function' ){
                var tmp = D.span( {} );
                el.appendChild( tmp );
                subEl( function(){
                    D.removeChildren( tmp );
                    D.appendChild( tmp, [].slice.call( arguments ) )
                } )
            }else if( subEl !== void 0 && subEl !== false && subEl !== null ){
                el.appendChild( D.Text( subEl ) );
            }
        }else if('dom' in subEl){
            el.appendChild( subEl.dom );
        }else if( Array.isArray(subEl) ){
            subEl.forEach(subEl => D.appendChild( el, subEl ) );
        }else{
            el.appendChild( subEl );
        }
    };
    D.join = function(arr, delimiter){
        var out = [], isFn = typeof delimiter === 'function';

        for( var i = 0, _i = arr.length - 1; i < _i; i++ ){
            out.push(arr[i], isFn?delimiter(i):delimiter);
        }
        if(i < _i+1)
            out.push(arr[i]);
        return out;
    };

    D.cls = function() {
        var out = [], i = 0, _i = arguments.length, token, tmp, key;
        for(;i<_i;i++){
            token = arguments[i];
            if(typeof token === 'string'&& token){
                out.push( token );
            }else if(typeof token === 'object'){
                if(Array.isArray(token)){
                    tmp = D.cls.apply(null, token);
                    tmp && out.push( token );
                }else{
                    for(key in token){
                        token[key] && out.push( key );
                    }
                }
            }else if(typeof token === 'function'){

            }
        }
        return out.join(' ');
    };
    D.escapeCls = function(cls) {
        return (cls+'').replace(/[^a-zA-Z0-9\-_]/g,'');
    };

    var _construct = function(ctor, cfg) {
        //if it is not an arrow function
        if('prototype' in ctor && ctor.prototype.constructor === ctor){
            var cls = new ctor(cfg);
            return 'dom' in cls ? cls.dom : cls;
        }else{
            return ctor(cfg);
        }
    };

    var usage = {};
    var populate = function(name, construct) {
        var tokens = name.split('.'),
          last = tokens.pop(),
          first = tokens.shift();

        // ES 6 consts are not in global scope. So we can not just add vars to window
        var pointer = first?
          new Function('g', 'return typeof '+first+' !== "undefined"?'+first+':(glob["'+first+'"] = {})')(glob)
          :glob;

        tokens.reduce(function(pointer, token, i, full) {
            return pointer[token] || (pointer[token] = {});
        }, pointer)[last] = construct;

        return construct;
    };
    D.declare = function(name, ctor) {
        var uses;
        if(name in usage){
            console.log(`${name} declaration updated. Usage count: ${usage[name].instances.length}`)
            usage[ name ].ctor = ctor;
            uses = usage[ name ].instances;
            for( var i = 0, _i = uses.length; i < _i; i++ ){
                var u = uses[ i ], d = u.dom;
                u.dom = _construct(ctor, u.cfg);
                if(d.parentNode){
                    d.parentNode.replaceChild( u.dom, d )
                }
            }
        }else{
            console.log(`${name} declared`)
            uses = (usage[ name ] = {ctor: ctor, instances: []}).instances;
        }
        return populate(name, function construct (cfg) {
            var dom = _construct(ctor, cfg);
            uses.push({dom: dom, cfg: cfg});
            return dom;
        });
    }
})(window['PCG'], typeof window !== "undefined" ? window :
  typeof WorkerGlobalScope !== "undefined" ? self :
    typeof global !== "undefined" ? global :
    typeof GLOBAL !== "undefined" ? GLOBAL :
      Function("return this;")());