;;; dag-draw-ascii-junctions.el --- Junction character enhancement for ASCII rendering -*- lexical-binding: t -*-

;; Copyright (C) 2024, 2025

;; Author: Generated by Claude
;; Keywords: internal

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;;; Commentary:

;; Junction Character Enhancement - ASCII Rendering Context
;;
;; BOUNDED CONTEXT: ASCII Rendering Context
;; UPSTREAM CONTEXT: GKNV Algorithm Context (via Coordinate Transform Layer)
;; LAYER: Visual Enhancement
;; AUTHORITY: CLAUDE.md "Implementation Principles: Junction characters for
;;            ASCII graphs"
;;
;; This module implements junction character enhancement for ASCII graph
;; rendering, as specified in CLAUDE.md "Implementation Principles: Junction
;; characters for ASCII graphs".
;;
;; Junction characters are CRITICAL for ASCII visual quality.  They transform basic
;; edge routing (─ │) into visually polished diagrams with proper semigraphics
;; at connection points (┬ ┴ ├ ┤ ┌ ┐ └ ┘ ┼).
;;
;; Five Junction Types (CLAUDE.md):
;; 1. Port Start/End - at node boundaries where edges begin/end
;; 2. Direction Changes - corners where edges turn
;; 3. Edge Joins - T-junctions where edges merge
;; 4. Edge Splits - T-junctions where edges separate
;; 5. Edge Crossings - where two edges cross (┼)
;;
;; Algorithm: Walk-based local context analysis
;; - Walk through grid positions
;; - Analyze connectivity in all 4 directions
;; - Determine junction type based on connection pattern
;; - Select appropriate semigraphic character
;;
;; Bounded Context: ASCII Rendering Context
;; - Input: Grid with basic edge characters (─ │)
;; - Output: Grid with enhanced junction characters
;; - Authority: CLAUDE.md junction specifications
;;
;; See CLAUDE.md section "Junction characters for ASCII graphs" for full
;; specifications.

;;; Code:

(require 'dag-draw-core)

;; Module created as part of Phase 1: DDD Refactoring
;; This file will contain all junction character logic extracted from dag-draw-ascii-grid.el

;;; Junction Character Selection

(defun dag-draw--get-enhanced-junction-char (context)
  "Determine the appropriate junction character based on CONTEXT.

CONTEXT is a plist containing junction information per CLAUDE.md specifications.
Expected keys include:
  `:type' - Symbol: junction type (port-start, port-end, direction-change, etc.)
  `:direction' - Symbol: directional information (up, down, left, right)
  `:from-direction' and `:to-direction' - Symbols: for direction changes
  `:main-direction' and `:branch-direction' - Symbols: for T-junctions

Returns the Unicode box-drawing character appropriate for the junction type,
or nil to keep the current character."
  (let ((junction-type (plist-get context :type)))
    (cond
     ;; Direction change junctions (CLAUDE.md: \"When edge requires direction change\")
     ((eq junction-type 'direction-change)
      (let ((from-dir (plist-get context :from-direction))
            (to-dir (plist-get context :to-direction)))
        (cond
         ((and (eq from-dir 'right) (eq to-dir 'down)) ?┐)  ; right→down corner
         ((and (eq from-dir 'left) (eq to-dir 'down)) ?┌)   ; left→down corner
         ((and (eq from-dir 'right) (eq to-dir 'up)) ?┘)    ; right→up corner
         ((and (eq from-dir 'left) (eq to-dir 'up)) ?└))))

     ;; Edge crossing junctions (CLAUDE.md: \"When two edges cross\")
     ((eq junction-type 'edge-cross) ?┼)  ; always use cross character

     ;; T-junction scenarios (CLAUDE.md example)
     ((eq junction-type 't-junction)
      (let ((main-dir (plist-get context :main-direction))
            (branch-dir (plist-get context :branch-direction)))
        (cond
         ((and (eq main-dir 'down) (eq branch-dir 'right)) ?├)   ; down + right branch
         ((and (eq main-dir 'down) (eq branch-dir 'left)) ?┤)    ; down + left branch
         ((and (eq main-dir 'right) (eq branch-dir 'down)) ?┬)   ; right + down branch
         ((and (eq main-dir 'right) (eq branch-dir 'up)) ?┴))))

     ;; Straight line: no junction needed, return nil to keep current character
     ((eq junction-type 'straight-line) nil)

     ;; Unknown/no junction: keep current character
     ((eq junction-type 'unknown) nil)

     ;; Fallback for any other unknown types
     (t nil))))












;;; Local Grid Context Analysis

(defun dag-draw--analyze-local-grid-junction-context (grid x y _current-char new-char)
  "Analyze grid context at position (X,Y) to determine junction type.

GRID is a 2D vector representing the ASCII character grid.
X and Y are integers representing grid coordinates.
CURRENT-CHAR is the character already at the position (or space).
NEW-CHAR is the character being drawn.

This implements the D5.6-D5.8 context analysis requirements:
- Check for adjacent edges in all 4 directions
- Determine junction type based on connectivity
- Build proper context plist for character selection

Returns a context plist suitable for `dag-draw--get-enhanced-junction-char'."
  (let* ((grid-height (length grid))
         (_grid-width (if (> grid-height 0) (length (aref grid 0)) 0))
         ;; Check all 4 directions for edge characters
         (has-up (dag-draw--has-edge-in-direction grid x y 'up))
         (has-down (dag-draw--has-edge-in-direction grid x y 'down))
         (has-left (dag-draw--has-edge-in-direction grid x y 'left))
         (has-right (dag-draw--has-edge-in-direction grid x y 'right))
         ;; Count total connections
         (connection-count (+ (if has-up 1 0) (if has-down 1 0)
                             (if has-left 1 0) (if has-right 1 0)))
         ;; Determine if new character indicates direction
         (_new-is-horizontal (memq new-char '(?─)))
         (_new-is-vertical (memq new-char '(?│))))

    ;; Build context plist based on connectivity pattern
    (cond
     ;; Direction change: 2 connections in perpendicular directions
     ((and (= connection-count 2)
           (or (and has-left has-down)
               (and has-right has-down)
               (and has-left has-up)
               (and has-right has-up)))
      ;; For corners, determine from/to based on which two directions have edges
      ;; The character maps to the turn direction regardless of edge flow direction
      (let ((from-dir (cond
                       ;; Horizontal incoming
                       (has-left 'right)    ; Edge from left means traveling right
                       (has-right 'left)    ; Edge from right means traveling left
                       ;; Vertical incoming
                       (has-up 'down)       ; Edge from above means traveling down
                       (has-down 'up)))     ; Edge from below means traveling up
            (to-dir (cond
                     ;; Vertical outgoing
                     (has-down 'down)
                     (has-up 'up)
                     ;; Horizontal outgoing
                     (has-right 'right)
                     (has-left 'left))))
        (list :type 'direction-change
              :from-direction from-dir
              :to-direction to-dir
              :has-up has-up
              :has-down has-down
              :has-left has-left
              :has-right has-right)))

     ;; T-junction: 3 connections
     ((= connection-count 3)
      (list :type 't-junction
            :main-direction (cond ((and has-up has-down) 'down)
                                 ((and has-left has-right) 'right)
                                 (t 'down))
            ;; Branch is the direction that's NOT part of the straight line
            :branch-direction (cond
                              ;; Vertical line (up-down), branch is horizontal
                              ((and has-up has-down)
                               (cond (has-left 'left)
                                     (has-right 'right)))
                              ;; Horizontal line (left-right), branch is vertical
                              ((and has-left has-right)
                               (cond (has-up 'up)
                                     (has-down 'down)))
                              ;; Shouldn't reach here if t-junction detection is correct
                              (t 'right))
            :has-up has-up
            :has-down has-down
            :has-left has-left
            :has-right has-right))

     ;; Cross: 4 connections
     ((= connection-count 4)
      (list :type 'edge-cross
            :has-up has-up
            :has-down has-down
            :has-left has-left
            :has-right has-right))

     ;; Straight line: 2 connections in same direction (not perpendicular)
     ((and (= connection-count 2)
           (or (and has-left has-right)   ; Horizontal line
               (and has-up has-down)))    ; Vertical line
      (list :type 'straight-line
            :has-up has-up
            :has-down has-down
            :has-left has-left
            :has-right has-right))

     ;; Default: return minimal context
     (t
      (list :type 'unknown
            :has-up has-up
            :has-down has-down
            :has-left has-left
            :has-right has-right)))))

(defun dag-draw--has-edge-in-direction (grid x y direction)
  "Check if there's an edge character in DIRECTION from (X,Y) on GRID.

GRID is a 2D vector representing the ASCII character grid.
X and Y are integers representing grid coordinates.
DIRECTION is a symbol: one of `up', `down', `left', or `right'.

Key insight: Direction matters! A horizontal line (─) only connects left/right,
a vertical line (│) only connects up/down.  Only junction characters connect
in multiple directions.  Node border corners are decorations, not connections.

Returns t if an edge character is found AND compatible with DIRECTION,
nil otherwise."
  (let* ((grid-height (length grid))
         (grid-width (if (> grid-height 0) (length (aref grid 0)) 0))
         (check-x (cond ((eq direction 'left) (- x 1))
                       ((eq direction 'right) (+ x 1))
                       (t x)))
         (check-y (cond ((eq direction 'up) (- y 1))
                       ((eq direction 'down) (+ y 1))
                       (t y)))
         ;; Direction-specific characters
         (vertical-chars (list ?│ ?\u25bc ?\u25b2))      ; │ ▼ ▲ (only connect up/down)
         (horizontal-chars (list ?─ ?\u25ba ?\u25c4))    ; ─ ► ◄ (only connect left/right)
         ;; Junction characters can connect in multiple directions
         (junction-chars (list ?┼ ?├ ?┤ ?┬ ?┴ ?+))
         ;; Note: Node border corners (?┌ ?┐ ?└ ?┘) are NOT included - they're decorations!
         (char-at-pos (if (and (>= check-x 0) (< check-x grid-width)
                              (>= check-y 0) (< check-y grid-height))
                         (aref (aref grid check-y) check-x)
                       nil)))

    ;; Check if character is compatible with the direction
    (cond
     ;; Checking vertical directions (up/down): only vertical chars and junctions
     ((or (eq direction 'up) (eq direction 'down))
      (and char-at-pos
           (or (memq char-at-pos vertical-chars)
               (memq char-at-pos junction-chars))))

     ;; Checking horizontal directions (left/right): only horizontal chars and junctions
     ((or (eq direction 'left) (eq direction 'right))
      (and char-at-pos
           (or (memq char-at-pos horizontal-chars)
               (memq char-at-pos junction-chars))))

     (t nil))))

;;; Junction Character Application (Priority 4: Integration)

(defun dag-draw--apply-junction-chars-to-grid (grid node-boundaries)
  "Apply junction characters throughout GRID, excluding NODE-BOUNDARIES.

GRID is a 2D vector representing the ASCII character grid (modified in place).
NODE-BOUNDARIES is a list of (x . y) cons cells marking node border
positions.

Walks through all grid positions, analyzes connectivity, and updates
characters.  This is the integration point for D5.1-D5.8 junction character
enhancement.  CLAUDE.md: `walks the edge in order to determine the
locally-relevant algorithm'

NODE-BOUNDARIES positions are excluded from junction enhancement to
prevent corruption of node box borders.  Arrow characters
\(▼ ▲ ► ◀) are never replaced."
  (when dag-draw-debug-output
    (message "DEBUG junction: Processing grid with %d boundaries" (length node-boundaries)))
  (let* ((grid-height (length grid))
         (grid-width (if (> grid-height 0) (length (aref grid 0)) 0))
         ;; Include ?+ as it's used as a fallback junction character
         (edge-chars '(?─ ?│ ?┼ ?┌ ?┐ ?└ ?┘ ?├ ?┤ ?┬ ?┴ ?+))
         ;; Arrow characters should NOT be replaced (CLAUDE.md: "the one possible exemption
         ;; is where an arrow is placed")
         (arrow-chars (list ?\u25bc ?\u25b2 ?\u25ba ?\u25c4))) ; ▼ ▲ ► ◄
    ;; Walk through entire grid
    (dotimes (y grid-height)
      (dotimes (x grid-width)
        (let ((current-char (aref (aref grid y) x)))
          ;; Skip if this position is a node boundary
          (unless (member (cons x y) node-boundaries)
            ;; Only process edge/junction characters, but NOT arrows
            (when (and (memq current-char edge-chars)
                      (not (memq current-char arrow-chars)))
              ;; Analyze local context and determine proper junction character
              (let* ((context (dag-draw--analyze-local-grid-junction-context
                             grid x y current-char current-char))
                     (junction-char (dag-draw--get-enhanced-junction-char context)))
                ;; Update grid with enhanced junction character
                (when junction-char
                  (aset (aref grid y) x junction-char))))))))))

(provide 'dag-draw-ascii-junctions)

;;; dag-draw-ascii-junctions.el ends here
