import React from 'react';
import type { LogicNode } from '../../utils/strategyTypes';
import { nodeToText, type DefType } from '../../utils/strategyEditorShared';
import { getStrategyIndicatorDisplayName } from '../../utils/strategyPaletteStorage';
import { getIndicatorPaletteCategory } from './paletteCategories';
import {
collectEditorBranches,
normalizeStartCombineOp,
type EditorConditionState,
} from '../../utils/strategyConditionSerde';
import { formatStartCandleLabel } from '../../utils/strategyStartNodes';
interface Props {
buyCondition: LogicNode | null;
sellCondition: LogicNode | null;
buyEditorState?: EditorConditionState;
sellEditorState?: EditorConditionState;
orphanCount: number;
def: DefType;
}
function renderCondition(node: LogicNode, def: DefType): React.ReactNode {
const full = nodeToText(node, def);
const ind = node.condition?.indicatorType ?? '';
const indLabel = getStrategyIndicatorDisplayName(ind);
const prefix = `${indLabel} -`;
const tail = indLabel && full.startsWith(prefix) ? full.slice(prefix.length) : full;
const cat = getIndicatorPaletteCategory(ind);
return (
<>
(
{indLabel ? (
<>
{indLabel}
{tail ? {tail.startsWith(' ') ? tail : ` ${tail}`} : null}
>
) : (
{full || '?'}
)}
)
>
);
}
function renderNode(node: LogicNode, def: DefType): React.ReactNode {
if (node.type === 'CONDITION') return renderCondition(node, def);
if (node.type === 'AND') {
const children = node.children ?? [];
if (children.length === 0) return (빈 AND);
return (
<>
(
{children.map((child, i) => (
{i > 0 ? AND : null}
{renderNode(child, def)}
))}
)
>
);
}
if (node.type === 'OR') {
const children = node.children ?? [];
if (children.length === 0) return (빈 OR);
return (
<>
(
{children.map((child, i) => (
{i > 0 ? OR : null}
{renderNode(child, def)}
))}
)
>
);
}
if (node.type === 'NOT') {
const child = node.children?.[0];
return child ? (
<>
NOT
{renderNode(child, def)}
>
) : (
(빈 NOT)
);
}
if (node.type === 'TIMEFRAME') {
const inner = node.children?.[0];
const types = node.candleTypes?.length
? node.candleTypes
: [node.candleType ?? '1m'];
const tf = types.length > 1
? types.map(formatStartCandleLabel).join(' · ')
: formatStartCandleLabel(types[0]);
return inner ? (
<>
[{tf}]
{' '}
{renderNode(inner, def)}
>
) : (
[{tf} 빈 조건]
);
}
return null;
}
function renderSignalBranches(
editorState: EditorConditionState | undefined,
fallbackRoot: LogicNode | null,
def: DefType,
): React.ReactNode {
const branches = editorState
? collectEditorBranches(editorState)
: fallbackRoot
? [{ candleType: '1m', root: fallbackRoot }]
: [];
if (branches.length === 0) {
return (연결된 조건 없음);
}
if (branches.length === 1) {
const { candleType, candleTypes, root } = branches[0];
if (!root) return (연결된 조건 없음);
const tfLabel = candleTypes && candleTypes.length > 1
? candleTypes.map(formatStartCandleLabel).join(' · ')
: formatStartCandleLabel(candleType);
return (
<>
[{tfLabel}]
{' '}
{renderNode(root, def)}
>
);
}
const combineOp = normalizeStartCombineOp(editorState?.startCombineOp);
const combineClass = combineOp === 'AND' ? 'se-formula-logic--and' : 'se-formula-logic--or';
return (
<>
(
{branches.map((branch, i) => (
{i > 0 ? (
{combineOp}
) : null}
{branch.root ? (
<>
[{formatStartCandleLabel(branch.candleType)}]
{' '}
{renderNode(branch.root, def)}
>
) : (
[{formatStartCandleLabel(branch.candleType)} 빈 조건]
)}
))}
)
>
);
}
export default function LogicExpressionPreview({
buyCondition,
sellCondition,
buyEditorState,
sellEditorState,
orphanCount,
def,
}: Props) {
const hasBody = buyCondition || sellCondition
|| (buyEditorState && collectEditorBranches(buyEditorState).some(b => b.root))
|| (sellEditorState && collectEditorBranches(sellEditorState).some(b => b.root));
return (
{!hasBody && (
(연결된 조건을 구성하세요)
)}
{(buyCondition || buyEditorState) && (
[매수]
{' '}
{renderSignalBranches(buyEditorState, buyCondition, def)}
)}
{(sellCondition || sellEditorState) && (
[매도]
{' '}
{renderSignalBranches(sellEditorState, sellCondition, def)}
)}
{orphanCount > 0 && (
{'// 미연결 요소 '}{orphanCount}{'개 — 전략 코드 미포함'}
)}
);
}