export class KERNEL_DIR {
    constructor() {

    }

    static getSrc(customCode, nodeLogic, geometryLength, afferentNodesCount, efferentStart, efferentNodesCount) {
        let outputArr = ["dir", "posXYZW", "dataB", "dataF", "dataG", "dataH"];
        let returnStr = 'return [vec4(currentDir, 1.0), vec4(currentPos.x, currentPos.y, currentPos.z, 1.0), currentDataB, currentDataF, currentDataG, currentDataH];';

        return ["x", outputArr,
            // head
            `float tanh(float val) {
                float tmp = exp(val);
                float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);
                return tanH;
            }
            float sigm(float val) {
                return (1.0 / (1.0 + exp(-val)));
            }`,

            // source
            `float nodeId = data[x].x;
            vec2 xGeometry = get_global_id(nodeId, uBufferWidth, ${geometryLength.toFixed(1)});


            vec3 currentPos = posXYZW[xGeometry].xyz;
            vec3 currentDir = dir[xGeometry].xyz;


            vec4 currentDataB = dataB[xGeometry];
            vec4 currentDataF = dataF[xGeometry];
            vec4 currentDataG = dataG[xGeometry];
            vec4 currentDataH = dataH[xGeometry];

            currentDir = vec3(0.0, 0.0, 0.0);

            
            vec3 atraction = vec3(0.0, 0.0, 0.0);
            float acumAtraction = 1.0;
            float acumChilds = 0.0;
            vec3 repulsion = vec3(0.0, 0.0, 0.0);

            vec3 force = vec3(0.0, 0.0, 0.0);


            float foutput0 = 0.0;            
            float foutput1 = 0.0;            
            float foutput2 = 0.0;            
            float foutput3 = 0.0;            
            float foutput4 = 0.0;            
            float foutput5 = 0.0;            
            float foutput6 = currentDataH.z;
            
            float foutputH = 0.0;
            float foutputI = 0.0;
            float foutputJ = 0.0;
            float foutputK = 0.0;
            float foutputL = 0.0;
            float foutputM = 0.0;
            float foutputN = 0.0;
            

            if(nodeId < nodesCount && enableTrain == 0.0) {
                float currentActivationFn = 0.0;
                vec2 xGeomCurrent = get_global_id(nodeId, uBufferWidth, ${geometryLength.toFixed(1)});
                for(int n=0; n < 4096; n++) {
                    if(float(n) >= nodesCount) {break;}
                    if(float(n) != nodeId) {
                        vec2 xGeomOpposite = get_global_id(float(n), uBufferWidth, ${geometryLength.toFixed(1)});


                        vec2 xAdjMatCurrent = get_global_id(vec2(float(n), nodeId), widthAdjMatrix);
                        vec2 xAdjMatOpposite = get_global_id(vec2(nodeId, float(n)), widthAdjMatrix);

                        vec4 pixAdjMatACurrent = adjacencyMatrix[xAdjMatCurrent];
                        vec4 pixAdjMatAOpposite = adjacencyMatrix[xAdjMatOpposite];

                        vec4 pixAdjMatBCurrent = adjacencyMatrixB[xAdjMatCurrent];
                        vec4 pixAdjMatBOpposite = adjacencyMatrixB[xAdjMatOpposite];
                        
                        vec4 pixAdjMatCCurrent = adjacencyMatrixC[xAdjMatCurrent];
                        vec4 pixAdjMatCOpposite = adjacencyMatrixC[xAdjMatOpposite];
                        
                        vec4 pixAdjMatDCurrent = adjacencyMatrixD[xAdjMatCurrent];
                        vec4 pixAdjMatDOpposite = adjacencyMatrixD[xAdjMatOpposite];


                                                                    
                        ${/* pixAdjMatACurrent */''}
                        float currentLayerNum = pixAdjMatACurrent.x;
                        float currentWeight = pixAdjMatACurrent.z;
                        float currentIsParent = pixAdjMatACurrent.w;            
                        ${/* pixAdjMatAOpposite */''}
                        float oppositeLayerNum = pixAdjMatAOpposite.x;
                        float oppositeWeight = pixAdjMatAOpposite.z;
                        float oppositeIsParent = pixAdjMatAOpposite.w;
            
            
                        ${/* pixAdjMatBCurrent */''}
                        float currentCostA = pixAdjMatBCurrent.y;
                        float inputArg0 = pixAdjMatBOpposite.y;
                        ${/* pixAdjMatBOpposite */''}            
            
                        ${/* pixAdjMatCCurrent */''}
                        float currentCostB = pixAdjMatCCurrent.x;
                        float currentCostC = pixAdjMatCCurrent.y;
                        float currentCostD = pixAdjMatCCurrent.z;
                        float currentCostE = pixAdjMatCCurrent.w;     
                        float inputArg1 = pixAdjMatCOpposite.x;
                        float inputArg2 = pixAdjMatCOpposite.y;
                        float inputArg3 = pixAdjMatCOpposite.z;
                        float inputArg4 = pixAdjMatCOpposite.w;         
                        ${/* pixAdjMatCOpposite */''}                        
                        
                        ${/* pixAdjMatDCurrent */''}
                        float currentCostF = pixAdjMatDCurrent.x;
                        float currentCostG = pixAdjMatDCurrent.y;         
                        float inputArg5 = pixAdjMatDOpposite.x;
                        float inputArgNodeType = pixAdjMatDOpposite.y;
                        ${/* pixAdjMatDOpposite */''}
                        
                        
            
                        ${/* dataB Current */''}            
                        ${/* dataB Opposite */''}                        
                        float oppositeNetOutputA = dataB[xGeomOpposite].z;                        
                        float oppositeNetOutputB = dataF[xGeomOpposite].x;                    
                        float oppositeNetOutputC = dataF[xGeomOpposite].z;                    
                        float oppositeNetOutputD = dataG[xGeomOpposite].x;                    
                        float oppositeNetOutputE = dataG[xGeomOpposite].z;                    
                        float oppositeNetOutputF = dataH[xGeomOpposite].x;                    
                        float oppositeNetOutputG = dataH[xGeomOpposite].z;
            
            
                        ${/* pos & dir Current */''}
                        ${/* vec3 currentPos = posXYZW[xGeomCurrent].xyz; */''}
                        ${/* vec3 currentDir = dir[xGeomCurrent].xyz; */''}            
                        ${/* pos & dir Opposite */''}
                        vec3 oppositePos = posXYZW[xGeomOpposite].xyz;
                        vec3 oppositeDir = dir[xGeomOpposite].xyz;
            
                        ${/* dir / dist to opposite */''}
                        vec3 dirToOpposite = (oppositePos-currentPos);
                        vec3 dirToOppositeN = normalize(dirToOpposite);
            
                        float dist = distance(oppositePos, currentPos); ${/* near=0.0 ; far=1.0 */''}
                        float distN = max(0.0,dist)/100000.0;
            
                        float mm = 10000000.0;
                        float m1 = 400000.0/mm;
                        float m2 = 48.0/mm;
                        if(currentIsParent == 1.0) {
                            ${((nodeLogic !== undefined) ? nodeLogic : '')}
                            
                            acumChilds += 1.0;
                            
                            atraction += dirToOppositeN*max(1.0, distN*abs(oppositeWeight)*(m1/2.0));
                            repulsion += -dirToOppositeN*max(1.0, (1.0-distN)*abs(oppositeWeight)*(m2/2.0));
                            acumAtraction += 1.0;
                        } else if(currentIsParent == 0.5) {
                            ${/* foutputH += currentCostA;
                            foutputI += currentCostB;
                            foutputJ += currentCostC;
                            foutputK += currentCostD;
                            foutputL += currentCostE;
                            foutputM += currentCostF;
                            foutputN += currentCostG; */''}
                            
                            atraction += dirToOppositeN*max(1.0, distN*abs(currentWeight)*m1);
                            repulsion += -dirToOppositeN*max(1.0, (1.0-distN)*abs(currentWeight)*m2);
                            acumAtraction += 1.0;
                        }
            
                        repulsion += -dirToOppositeN*max(1.0, (1.0-distN)*abs(currentWeight)*(m2/8.0));
                        acumAtraction += 1.0;
                    }
                }
                
                float vndm = (viewNeuronDynamics == 1.0) ? foutput0 : 1.0;
                force += (atraction/acumAtraction)*abs(vndm);
                force += (repulsion/acumAtraction)*abs(vndm);
                currentDir += force;
                
                
                float currentBiasNode = dataB[xGeomCurrent].x;
                
                ${KERNEL_DIR.efferentNodesStr(afferentNodesCount, efferentStart, efferentNodesCount)}
                
                currentDataB = vec4(currentDataB.x, foutputH, foutput0, foutputI);
                currentDataF = vec4(foutput1, foutputJ, foutput2, foutputK);
                currentDataG = vec4(foutput3, foutputL, foutput4, foutputM);
                currentDataH = vec4(foutput5, foutputN, foutput6, 0.0);
            }

            ${((customCode !== undefined) ? customCode : '')}

            if(enableDrag == 1.0) {
                if(nodeId == idToDrag) {
                    currentPos = vec3(MouseDragTranslationX, MouseDragTranslationY, MouseDragTranslationZ);
                }
            }

            currentPos += currentDir;
            if(only2d == 1.0) {
                currentPos.y = 0.0;
            }

            ${returnStr}`];
    };

    static efferentNodesStr(afferentNodesCount, efferentStart, efferentNodesCount) {
        let arrUniformsCount = Math.ceil(afferentNodesCount/64);
        /////////////////////////////////////////////////
        // OUTPUT
        /////////////////////////////////////////////////
        let str = `
            if(nodeId < afferentNodesCount && acumChilds == 0.0) {                
                    vec2 idA = get_global_id(nodeId, afferentNodesTWidth, 1.0);
                    vec4 tA = afferentNodesT[idA];
                    foutput0 = tA.r;
                    
                    vec2 idB = get_global_id(nodeId+(afferentNodesCount), afferentNodesTWidth, 1.0);
                    vec4 tB = afferentNodesT[idB];
                    foutput1 = tB.r;
                    
                    vec2 idC = get_global_id(nodeId+(afferentNodesCount*2.0), afferentNodesTWidth, 1.0);
                    vec4 tC = afferentNodesT[idC];
                    foutput2 = tC.r;
                    
                    vec2 idD = get_global_id(nodeId+(afferentNodesCount*3.0), afferentNodesTWidth, 1.0);
                    vec4 tD = afferentNodesT[idD];
                    foutput3 = tD.r;
                    
                    vec2 idE = get_global_id(nodeId+(afferentNodesCount*4.0), afferentNodesTWidth, 1.0);
                    vec4 tE = afferentNodesT[idE];
                    foutput4 = tE.r;
                    
                    vec2 idF = get_global_id(nodeId+(afferentNodesCount*5.0), afferentNodesTWidth, 1.0);
                    vec4 tF = afferentNodesT[idF];
                    foutput5 = tF.r;
                    
                    vec2 idG = get_global_id(nodeId+(afferentNodesCount*6.0), afferentNodesTWidth, 1.0);
                    vec4 tG = afferentNodesT[idG];
                    foutput6 = tG.r;
                }`;

        /////////////////////////////////////////////////
        // ERROR
        /////////////////////////////////////////////////
        /*for(let n=(efferentStart); n < (efferentStart+efferentNodesCount); n++) {
            let cond = (n===efferentStart) ? "if" : "else if" ;
            str += `
            ${cond}(nodeId == `+n.toFixed(1)+`) {
                if(freezeOutput == 0.0) {
                    foutputA = netChildInputSumA;
                    foutputB = netChildInputSumB;
                    foutputC = netChildInputSumC;
                    foutputD = netChildInputSumD;
                    foutputE = netChildInputSumE;
                    foutputF = netChildInputSumF;
                    foutputG = netChildInputSumG;
                } else {
                    foutputA = currentDataB.z;
                    foutputB = currentDataF.x;
                    foutputC = currentDataF.z;
                    foutputD = currentDataG.x;
                    foutputE = currentDataG.z;
                    foutputF = currentDataH.x;
                    foutputG = currentDataH.z;
                }               
                netParentErrorWeightA = efferentNodesA[`+Math.round(n-efferentStart)+`];
                netParentErrorWeightB = efferentNodesB[`+Math.round(n-efferentStart)+`];
                netParentErrorWeightC = efferentNodesC[`+Math.round(n-efferentStart)+`];
                netParentErrorWeightD = efferentNodesD[`+Math.round(n-efferentStart)+`];
                netParentErrorWeightE = efferentNodesE[`+Math.round(n-efferentStart)+`];
                netParentErrorWeightF = efferentNodesF[`+Math.round(n-efferentStart)+`];
                netParentErrorWeightG = efferentNodesG[`+Math.round(n-efferentStart)+`];
            }`;
        }*/
        return str;
    };
}
global.KERNEL_DIR = KERNEL_DIR;
module.exports.KERNEL_DIR = KERNEL_DIR;