/**
 * Lego Ladder — Core Type Definitions
 *
 * Ladder logic is a graphical programming language used in PLCs.
 * Programs are organized into RUNGS evaluated left-to-right, top-to-bottom.
 * Each rung is a boolean expression that determines whether output COILS activate.
 */

// ─── Element Types ────────────────────────────────────────────────

/** A contact — the basic boolean element. Reads an input condition. */
export type ContactType = 'NO' | 'NC';

export type EdgeType = 'RISING' | 'FALLING' | 'BOTH';

export interface Contact {
  type: 'contact';
  /** Normally Open (NO) = passes when true. Normally Closed (NC) = passes when false. */
  contactType: ContactType;
  /** Address of the input this contact reads (e.g., 'I:0.0', 'SENSOR:dist') */
  address: string;
  /** Optional comparison: value must satisfy this to be true. */
  condition?: ComparisonCondition;
  /** When set, contact only passes for one scan on the edge. */
  edgeType?: EdgeType;
}

export type ComparisonOp = '==' | '!=' | '>' | '<' | '>=' | '<=';

export interface ComparisonCondition {
  op: ComparisonOp;
  /** Constant value to compare against (from sensor, hardcoded threshold, etc.) */
  value: number;
}

/** A coil — an output that gets energized when the rung logic is true. */
export type CoilType = 'OTE' | 'OTL' | 'OTU' | 'OUTPUT' | 'SET' | 'RESET' | 'TOGGLE' | 'LATCH' | 'UNLATCH';
// OTE = Output Energize (standard coil)
// OTL = Output Latch (SET alias)
// OTU = Output Unlatch (RESET alias)
// LATCH = once energized, stays on until UNLATCH (separate from SET which is momentary)
// UNLATCH = clears a LATCH

export interface Coil {
  type: 'coil';
  coilType: CoilType;
  /** Address of the output (e.g., 'Q:0.0', 'MOTOR:B') */
  address: string;
  /** Optional: target value for the output (motor speed, brightness, etc.) */
  value?: number;
}

/** A timer element — delays output activation or deactivation. */
export type TimerType = 'TON' | 'TOF' | 'TP' | 'RTO';
// TON = On-Delay (waits preset time before energizing)
// TOF = Off-Delay (stays energized for preset time after input goes false)
// TP = Pulse (pulses output for preset time when input goes true)
// RTO = Retain On-Delay (like TON but accumulated time persists when input goes false)

export interface Timer {
  type: 'timer';
  timerType: TimerType;
  /** Unique timer instance ID */
  instanceId: string;
  /** Preset time in milliseconds */
  preset: number;
  /** Internal accumulated time */
  accumulated: number;
  /** Optional: reset address — when true, resets RTO accumulated time to 0 */
  resetAddress?: string;
}

/** A counter element — counts transitions. */
export type CounterType = 'CTU' | 'CTD' | 'CTUD';
// CTU = Count Up
// CTD = Count Down
// CTUD = Count Up/Down

export interface Counter {
  type: 'counter';
  counterType: CounterType;
  /** Unique counter instance ID */
  instanceId: string;
  /** Preset count value */
  preset: number;
  /** Current count value */
  current: number;
  /** Optional: reset address — when true, resets counter to 0 */
  resetAddress?: string;
  /** Overflow bit — set when ACC > 9999 */
  ov?: boolean;
  /** Underflow bit — set when ACC < 0 */
  und?: boolean;
}

/** A logic gate element. */
export type GateType = 'AND' | 'OR' | 'XOR' | 'NOT' | 'NAND' | 'NOR';

export interface LogicGate {
  type: 'gate';
  gateType: GateType;
  /** Number of inputs for multi-input gates */
  inputs: string[];
  /** Instance output address */
  outputAddress: string;
}

/** A logic branch — allows for nested AND/OR logic within a rung. */
export interface Branch {
  type: 'branch';
  /** Unique ID for the branch element */
  id: string;
  /** The logic used to combine paths within this branch */
  logic: 'AND' | 'OR';
  /** The paths within this branch. Each path is a series of elements. */
  paths: RungElement[][];
}

/** A math element — performs arithmetic on two inputs. */
export type MathOperator = 'ADD' | 'SUB' | 'MUL' | 'DIV';

export interface MathElement {
  type: 'math';
  operator: MathOperator;
  inputA: string;
  inputB: string;
  outputAddress: string;
}

/** One-Shot contact — fires true for exactly one scan when input goes from false to true. */
export interface OneShot {
  type: 'oneshot';
  address: string;
  edgeType: 'RISING' | 'FALLING'; // which edge triggers
}

/** MOV (Move) instruction — copies value from source to destination. */
export interface MoveElement {
  type: 'move';
  source: string;     // source address
  destination: string; // destination address
}

/** Scale (SLC) instruction — scales input from one range to another. */
export interface ScaleElement {
  type: 'scale';
  inputAddress: string;  // raw input
  destination: string;   // scaled output
  inMin: number;         // input min
  inMax: number;         // input max
  outMin: number;        // output min
  outMax: number;        // output max
}

/** NOP (No Operation) — placeholder/separator. */
export interface NoOp {
  type: 'noop';
  comment?: string; // optional label
}

/** Standalone Compare instruction — compare two values, output boolean. */
export interface CompareElement {
  type: 'compare';
  op: ComparisonOp;
  inputA: string;
  inputB: string;
  outputAddress: string; // boolean result address
}

/** Any element that can appear in a rung. */
export type RungElement = Contact | Coil | Timer | Counter | LogicGate | Branch | MathElement
  | OneShot | MoveElement | ScaleElement | NoOp | CompareElement;

// ─── Rung & Program ──────────────────────────────────────────────

/** A single rung of ladder logic. */
export interface Rung {
  id: string;
  /** Display label for this rung */
  label?: string;
  /** Elements in series (AND logic) — left to right. Last element is the output coil. 
   * Can include Branch elements for nested logic. */
  series: RungElement[];
  /** Whether this rung is enabled */
  enabled: boolean;
}

/** The complete ladder program. */
export interface Program {
  name: string;
  rungs: Rung[];
  /** Cycle time in ms — how often rungs are re-evaluated */
  cycleTime: number;
  /** When true, math operations truncate to integer (32-bit DINT mode) */
  integerMode?: boolean;
}

// ─── I/O Model ────────────────────────────────────────────────────

/** An input — a value read from sensors, buttons, or internal state. */
export interface Input {
  address: string;
  value: boolean | number;
  /** Raw value before any threshold comparison */
  rawValue?: number;
}

/** An output — a value written to motors, LEDs, or internal state. */
export interface Output {
  address: string;
  value: boolean | number;
  /** When was this output last updated (cycle number) */
  lastUpdated: number;
}

/** Internal memory bit — used for intermediate logic state. */
export interface MemoryBit {
  address: string;
  value: boolean | number;
}

// ─── Engine State ────────────────────────────────────────────────

/** Current engine state. */
export interface EngineState {
  /** Current cycle number */
  cycle: number;
  inputs: Map<string, Input>;
  outputs: Map<string, Output>;
  memory: Map<string, MemoryBit>;
  timers: Map<string, Timer>;
  counters: Map<string, Counter>;
  /** Previous scan cycle input values — used for edge detection. */
  previousInputs: Map<string, boolean | number>;
  running: boolean;
}