import React, { useEffect, useMemo, useState } from "react";
import { useSelector } from "react-redux";
import { IfCondition } from "./IfCondition";
import { IfThen } from "./IfThen";
const objectPath = require("object-path");
/**
* Componente que inicia un contexto de contenido condicional (if)
* @name Reactor.Components.Templates.If
* @param {string} variable Nombre de la variable (o path), para enviar valores absolutos (o parametros) comenzar con comilla simple la cadena, es este caso no se requiere context
* @param {string} [context = "global"] Nombre del contexto a usar
* @param {string} value Valor de la condicion
* @param {LogicOperator} [operator = "="] Operador para comparar el valor con la variable
* @param {VariableType} [type = "text"] Tipo de variable que se va a comparar (tambien se puede setear en condition)
* @param {BooleanString} [debug = "false"] Si es true muestra en consola algunos datos de depuracion
* @class
* @example
<h6>if simple</h6>
<if variable="test_input" context="test_if" value="admin" operator="=" type="text">
contenido condicional <span>que aparece</span> solo si la condicion se cumple y no tiene then ni else
</if>
<h6>if then else</h6>
<if variable="test_input" context="test_if" value="admin" operator="=" type="text">
<then>
contenido condicional
</then>
<else>
contenido en caso contrario
</else>
</if>
<h6>if con valor absoluto y parametro</h6>
<if variable="'{{rolUsuario}}">
<condition value="admin">
esto aparece si es admin <br/>
</condition>
<condition value="user">
esto aparece si es user <br/>
</condition>
<else>
si no se cumplen condiciones <br/>
</else>
</if>
<h6>if conditions then else</h6>
<if variable="test_input" context="test_if">
<condition value="admin">
esto aparece si es admin <br/>
</condition>
<condition value="user">
esto aparece si es user <br/>
</condition>
<then>
esto aparece cuando se cumple alguna condicion <br/>
</then>
<else>
esto aparece si no se cumplen condiciones <br/>
</else>
</if>
<h6>if conditions else con numeros</h6>
<p>te convierte el tipo de la variable en numero </p>
<if variable="test_input" context="test_if" type="number">
<condition value="0" operator="<">
error el valor minimo el cero
</condition>
<condition value="0">
no hay ninguno
</condition>
<condition value="1">
es uno solo
</condition>
<condition value="5" operator="<=">
son varios
</condition>
<condition value="5" operator=">">
son muchos (mas de 5)
</condition>
<else>
numero invalido
</else>
</if>
<input_oqt change_var="test_input" context="test_if" var-value="test_input" placeholder="escribi admin, user, 0, 1, 5, 6 o cualquier otra cosa"/>
*/
export const If = ({variable, context = "global", children, type = "text", operator = "=", value, debug = "false"}) => {
const hookIf = useIf({variable, context, defaultType: type, debug: debug === "true"});
const elseOrConditions = useMemo(()=>{
if(debug === "true"){
console.log(children);
}
return children.some(c => c && typeof c === "object" && (c.type?.name === "IfElse" || c.type?.name === "IfCondition"))
}, [children]);
useEffect(()=>{
if(debug==="true"){
console.log("if properties", {variable, context, children, type, operator, value, debug});
}
}, [])
return (
<IfContext.Provider value={hookIf}>
{value
?<>
{elseOrConditions
? <><IfCondition {...{operator, type, value}}></IfCondition> {children}</>
: <IfCondition {...{operator, type, value}}>{children}</IfCondition>
}
</>
: children
}
</IfContext.Provider>
)
}
const useIf = ({
variable,
context = "global",
defaultType = "text",
debug
}) => {
const [conditions, setConditions] = useState([]);
const [matchs, setMatchs] = useState([]);
const [match, setMatch] = useState(null);
const varsContext = useSelector(state => state.app.variables[context]);
const varValue = useMemo(() => {
if(variable.startsWith("'")){
return variable.substring(1);
}
return objectPath.get(varsContext, variable)
}, [variable, varsContext]);
useEffect(()=>{
if(debug){
console.log("if refresh variable", {context, varsContext, variable})
}
//try{
const newMatchs = conditions
.map((c, index) => {
return evalMatch({value: evalValue({type: c.type, value: evalPath(c.path)}), operator: c.operator, propValue: c.value})
? index
: null
}).filter(c => c !== null);
if(debug){
console.log("if refresh matchs", {matchs: newMatchs, conditions, varValue})
}
const newMatch = (!newMatchs || newMatchs.length === 0)
? null
: conditions[[...newMatchs].sort()[0]].guid;
if(debug){
console.log("if refresh match", {matchs: newMatch, matchs: newMatchs, conditions, varValue})
}
setMatchs(newMatchs);
setMatch(newMatch);
/*}catch(e){
console.log(e);
}*/
}, [variable, context, defaultType, conditions, varValue]);
const addCondition = ({guid}) => {
if(debug){
console.log("if addCondition", {item: {guid}, conditions})
}
setConditions(conditions => [...conditions, {guid}]);
};
const updateCondition = ({guid, value, operator, path, type}) => {
if(debug){
console.log("if updateCondition", {item: {guid, value, operator, path, type}, conditions})
}
setConditions(conditions => conditions.map(c => c.guid === guid ? {...c, value, operator, path, type} : c));
};
const removeCondition = ({guid, value}) => {
if(debug){
console.log("if removeCondition", {item: {guid}, conditions})
}
setConditions(conditions => conditions.filter(c => c.guid !== guid));
};
const evalValue = ({type, value}) => {
switch (type || defaultType) {
case "text":
return `${value}`;
case "number":
try{
return parseFloat(value);
}catch(e){
try{
return parseInt(value);
}catch(e){
return 0;
}
};
case "boolean":
return value === "true" || value === "1" || value === true;
default:
return null;
}
}
const evalMatch = ({value, operator, propValue}) => {
if(debug){
console.log("if evalMatch", {value, operator, propValue})
}
switch (operator) {
case "=":
return propValue === value;
case "!=":
return propValue !== value;
case ">":
return value > propValue;
case "<":
return value < propValue;
case ">=":
return value >= propValue;
case "<=":
return value <= propValue;
case "startsWith":
return `${value}`.startsWith(propValue);
case "endsWith":
return `${value}`.endsWith(propValue);
case "??":
return value === null || value === undefined;
case "!!":
return value !== null && value !== undefined;
case "?":
return !value;
case "!":
return !!value;
default:
return false;
}
}
const evalPath = (path) => {
return typeof varValue === "object" ? objectPath.get(varValue, path) : varValue;
}
return {match, addCondition, removeCondition, updateCondition, evalValue};
}
const defIf = {
match: "",
addCondition: ({guid, value}) => {},
removeCondition: ({guid, value}) => {},
updateCondition: ({guid, value, operator, path}) => {},
evalValue: ({type, value}) => null,
}
export const IfContext = React.createContext(defIf);