import { useState } from 'react'; import { Plus, Minus, Trash2, ChevronDown, ChevronUp } from 'lucide-react'; import type { BurstSegment, IntraProcessPolicy, Thread, ThreadModel } from '@core/types'; import { generateId } from '../shared/processUtils'; import { deriveThreadColor } from '../shared/threadUtils'; import IconButton from '../shared/IconButton'; interface ThreadEditorProps { threads: Thread[]; threadModel: ThreadModel; threadSchedulingPolicy: IntraProcessPolicy; threadQuantum: number; kernelThreadCount: number; processColor: string; onChange: (threads: Thread[]) => void; onModelChange: (model: ThreadModel) => void; onPolicyChange: (policy: IntraProcessPolicy) => void; onQuantumChange: (q: number) => void; onKernelCountChange: (k: number) => void; } const MODEL_OPTIONS: { value: ThreadModel; label: string; desc: string }[] = [ { value: 'MANY_TO_ONE', label: 'Many-to-One', desc: 'N threads → 1 kernel. I/O bloqueia todo o processo (green threads).', }, { value: 'ONE_TO_ONE', label: 'One-to-One', desc: 'N threads → N kernel threads. Bloqueios são independentes (pthreads).', }, { value: 'MANY_TO_MANY', label: 'Many-to-Many', desc: 'N threads → M kernel threads (M ≤ N). Slots liberados em I/O.', }, ]; const POLICY_OPTIONS: { value: IntraProcessPolicy; label: string }[] = [ { value: 'FCFS', label: 'FCFS' }, { value: 'RR', label: 'Round Robin' }, { value: 'PRIORITY', label: 'Priority' }, ]; const inputCls = 'w-full rounded-md bg-slate-700 border border-slate-600 text-slate-200 text-xs px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-blue-500'; const labelCls = 'block text-[10px] text-slate-400 mb-0.5'; // ── Burst editor interna de cada thread ─────────────────────────────────── const ThreadBurstEditor = ({ bursts, onChange, }: { bursts: BurstSegment[]; onChange: (b: BurstSegment[]) => void; }) => { const addBurst = () => { const lastType = bursts[bursts.length - 1]?.type ?? 'io'; const nextType: BurstSegment['type'] = lastType === 'cpu' ? 'io' : 'cpu'; onChange([...bursts, { type: nextType, duration: 2 }]); }; const removeBurst = (idx: number) => { if (bursts.length <= 1) return; onChange(bursts.filter((_, i) => i !== idx)); }; const updateDuration = (idx: number, val: number) => { onChange(bursts.map((b, i) => (i === idx ? { ...b, duration: Math.max(1, val) } : b))); }; return (
{bursts.map((b, idx) => (
{b.type} updateDuration(idx, Number(e.target.value))} className="w-14 rounded bg-slate-700/60 border border-slate-600/50 text-slate-200 text-[10px] px-1.5 py-0.5 focus:outline-none focus:ring-1 focus:ring-blue-500" /> ticks removeBurst(idx)} disabled={bursts.length <= 1} className="ml-auto hover:text-red-400" >
))}
); }; // ── Card de thread individual ───────────────────────────────────────────── const ThreadCard = ({ thread, index, processColor, onUpdate, onRemove, canRemove, }: { thread: Thread; index: number; processColor: string; onUpdate: (t: Thread) => void; onRemove: () => void; canRemove: boolean; }) => { const [expanded, setExpanded] = useState(false); const color = deriveThreadColor(processColor, index); return (
onUpdate({ ...thread, name: e.target.value })} className="flex-1 bg-transparent text-slate-200 text-xs font-medium focus:outline-none focus:ring-1 focus:ring-blue-500 rounded px-1" aria-label={`Thread ${index + 1} name`} /> T{index + 1} {/* Priority */} onUpdate({ ...thread, priority: Math.min(10, Math.max(1, Number(e.target.value))) })} className="w-10 rounded bg-slate-700 border border-slate-600 text-slate-200 text-[10px] px-1 py-0.5 focus:outline-none focus:ring-1 focus:ring-blue-500 text-center" aria-label={`Thread ${index + 1} priority`} />
{expanded && (
onUpdate({ ...thread, bursts })} />
)}
); }; // ── ThreadEditor principal ──────────────────────────────────────────────── const ThreadEditor = ({ threads, threadModel, threadSchedulingPolicy, threadQuantum, kernelThreadCount, processColor, onChange, onModelChange, onPolicyChange, onQuantumChange, onKernelCountChange, }: ThreadEditorProps) => { const addThread = () => { const newThread: Thread = { tid: `t_${generateId()}`, name: `T${threads.length + 1}`, priority: 5, bursts: [{ type: 'cpu', duration: 3 }], }; onChange([...threads, newThread]); }; const updateThread = (idx: number, updated: Thread) => { onChange(threads.map((t, i) => (i === idx ? updated : t))); }; const removeThread = (idx: number) => { onChange(threads.filter((_, i) => i !== idx)); }; const selectedModel = MODEL_OPTIONS.find(m => m.value === threadModel); return (
{/* Modelo de threads */}
{selectedModel && (

{selectedModel.desc}

)}
{/* Kernel thread count — apenas MANY_TO_MANY */} {threadModel === 'MANY_TO_MANY' && (
onKernelCountChange(Math.min(threads.length || 1, Math.max(1, Number(e.target.value)))) } className={inputCls} aria-label="Kernel thread count" />
)} {/* Política intra-processo */}
{threadSchedulingPolicy === 'RR' && (
onQuantumChange(Math.max(1, Number(e.target.value)))} className={inputCls} aria-label="Thread quantum" />
)}
{/* Lista de threads */}
{threads.map((thread, idx) => ( updateThread(idx, updated)} onRemove={() => removeThread(idx)} canRemove={threads.length > 1} /> ))}
{threads.length === 0 && (
Nenhuma thread. Clique em "+ Thread" para adicionar.
)}
); }; export default ThreadEditor;