
    IjA                    r   U d Z ddlmZ ddlZddlmZmZmZm	Z	m
Z
 dZded<   dZded	<   dd
Z	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 d dZeedd	 	 	 	 	 	 	 	 	 	 	 d!dZy)"a  W20A2: review HTML overlay emitter.

Pure rendering function. Takes a base_location_dossier (W20A) + a
geometry-candidates dict (W20A2 ``floor_plan_geometry_readback``) and
returns a self-contained HTML string.

The HTML is a **review / result-visibility surface**, not a production
UI. It is served via the user's mandated external HTTP server pattern
(``python -m http.server <port> --bind 0.0.0.0``); never opened via
``open <path>``.

Contract:
  - static template / code carries no scenario-specific literal tokens.
  - runtime payload may display source-provided labels / shot text
    verbatim because that content is data, not contract.
  - read-only: no fetch / POST / mutation against any pipeline state.
  - deterministic: identical input produces byte-identical output.
    )annotationsN)AnyDictListOptionalTuple<   intDEFAULT_CELL_PX   DEFAULT_GRID_PADDING_PXc                B    t        j                  t        |       d      S )NT)quote)_htmlescapestr)values    a/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/floor_plan_review_html.py_escr      s    <<E
$//    c                B    |||z  z   |dz  z   }|| |z  z   |dz  z   }||fS )u;   Convert (row, col) → SVG (x, y) center pixel coordinates.    )rowcolcell_pxpadxys         r   _cell_centerr    #   s;     	cGmgl*AcGmgl*Aa4Kr   c                @    |dz  ||z  z   }|dz  | |z  z   }d| d| dS )Nr   z<svg viewBox="0 0  zm" xmlns="http://www.w3.org/2000/svg" class="fp-review-svg" role="img" aria-label="floor plan review overlay">r   )	grid_rows	grid_colsr   r   widthheights         r   	_svg_openr'   ,   sF     !Gi'))E1Wy7**F
UG1VH -= 	>r   c                      	 y)zArrow-head marker definition. Must sit INSIDE the <svg> root so
    SVG references like ``marker-end="url(#fp-arrow-head)"`` resolve.
    z<defs><marker id="fp-arrow-head" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L0,10 L10,5 z" fill="#b91c1c"/></marker></defs>r   r   r   r   	_svg_defsr)   9   s    
	r   c                b   g }|j                  d| d| d||z   d| |z   d	       t        | dz         D ].  }|||z  z   }|j                  d| d| d	|||z  z    d
| d	       0 t        |dz         D ].  }|||z  z   }|j                  d| d| d	| d
|| |z  z    d	       0 t        |       D ]1  }|||z  z   |dz  z   dz   }|j                  d|dz
   d| d| d       3 t        |      D ].  }|||z  z   |dz  z   }|j                  d| d|dz
   d| d       0 dj                  |      S )z=Grid lines + row/col labels (rendered INSIDE the open <svg>).z	<rect x="" y="z	" width="z
" height="z3" fill="#fafafa" stroke="#999" stroke-width="1.5"/>   z
<line x1="" y1="" x2="" y2="z(" stroke="#d0d0d0" stroke-width="0.75"/>r      z	<text x="z/" text-anchor="end" font-size="10" fill="#666">z</text>   z2" text-anchor="middle" font-size="10" fill="#666">
)appendrangejoin)	r#   r$   r   r   linesrr   cr   s	            r   _svg_grid_bodyr9   E   s    E	LL
C5cU)I,?+@ Aw&' (<	= 9q=!!g+VA3 'W,,-VA3 756	
 " 9q=!!g+6# '#VC)g"556 756	
 " 9!g+1,q0ayaS )<<=3gG	
  9!g+1,s%ay )??@cJ	
  99Ur   c                    | d   | d   }}t        ||||      \  }}dddddj                  |d	      }	d
t        |       d| d| d|	 d| d|dz    dt        |       dS )z+Render a base marker dot keyed on its kind.r   r,   r   r   r   r   z#1e6aa1z#d97706z#0b8060z#7c3aed)base_structural_unitbase_openingbase_persistent_fixturebase_persistent_furniturez#444z<g class="fp-marker fp-marker-z"><circle cx="" cy="z" r="11" fill="z<" stroke="#fff" stroke-width="1.5" opacity="0.92"/><text x="r+   r0   zD" text-anchor="middle" font-size="11" fill="#fff" font-weight="600"></text></g>)r    getr   )
cellnumberkindr   r   r   r   r   r   fills
             r   _svg_marker_dotrG   t   s     AwQCCS'sCDAq )!#,%.	
 
c$ 	 )d 5cs/$ 83eAE7 #8<. 	r   c                v    | d   | d   }}t        ||||      \  }}d| d| d| d|dz
   d	t        |       d
S )Nr   r,   r;   z'<g class="fp-camera-point"><circle cx="r@   zX" r="18" fill="none" stroke="#1e6aa1" stroke-width="2" stroke-dasharray="3 2"/><text x="r+      u<   " text-anchor="middle" font-size="9" fill="#1e6aa1">cam · urA   )r    r   )rC   unit_markerr   r   r   r   r   r   s           r   _svg_camera_pointrK      sm     AwQCCS'sCDAqcs #3eAF8 $004[0A/B C		r   c           	         | d   }| d   }t        |d   |d   ||      \  }}t        |d   |d   ||      \  }}d| d| d| d	| d
	S )Ncamera_celllook_at_cellr   r,   r;   z(<g class="fp-direction-arrow"><line x1="r-   r.   r/   z[" stroke="#b91c1c" stroke-width="1.4" opacity="0.55" marker-end="url(#fp-arrow-head)"/></g>)r    )	recordr   r   camtgtx1y1x2y2s	            r   _svg_direction_arrowrV      s{     
C

 Cc!f#a&'sKFBc!f#a&'sKFBDrd&F2$ 7	r   c                   ddl }| d   }t        |d   |d   ||      \  }}t        | d         }t        | j	                  dd            }t        | j	                  dd            }	|dk(  r|	dk(  ry	|j                  |	|      }
|j                  |d
z        }|dz  }|||j                  |
|z
        z  z   }|||j                  |
|z
        z  z   }|||j                  |
|z         z  z   }|||j                  |
|z         z  z   }ddddj	                  | d   d      }d	}d| v rdt        | d          d}dt        | d          d| d| d| d|dd|dd|dd|dd| d| dS )u  Render a view-cone footprint at the camera cell, **rotated to
    the candidate direction** (``dx``, ``dy``) recorded on the cone.

    The cone is a symmetric isoceles polygon with apex at the camera
    cell and axis pointing toward the look-at cell. Lens enum drives
    the fov°. Code never picks a final camera or look-at — every
    cone is a candidate, one per (direction × lens). The HTML layer
    only visualizes the candidate; LLM-owned decisions remain
    downstream.
    r   NrM   r,   r;   fov_degdxdy g       @r   z#10b981z#3b82f6z#a855f7)widenormal	telephoto	lens_enumz#888look_at_unitz data-look-at-unit=""z$<g class="fp-view-cone fp-view-cone-z><polygon points=",r"   z.1fz" fill="z" opacity="0.08" stroke="z/" stroke-width="0.8" stroke-opacity="0.6"/></g>)
mathr    r
   floatrB   atan2radianscossinr   )rO   r   r   rc   rP   r   r   fovdx_griddy_gridthetahalflengthe1xe1ye2xe2ycolorlook_at_labels                      r   _svg_view_cone_markerru      s     

CCFASIDAq
fY
 C FJJtQ'(GFJJtQ'(G!|1 JJw(E<<c	"Dq[F
ftxx--
-C
ftxx--
-C
ftxx--
-C
ftxx--
-C 
c&
v&	 

 M"4~(>#?"@B 	 /tF;4G/H.I/ 3as!C9Ac#Yas)1SIXeW -' 		r   c                t    |sydj                  d |D              }dt        |        dt        |       d| dS )Nr[   c              3  :   K   | ]  }d t        |       d  yw)z<li>z</li>Nr   ).0ds     r   	<genexpr>z%_diagnostics_block.<locals>.<genexpr>   s     ?;ad47)5);   z'<details class="fp-diag" open><summary>z (z)</summary><ul>z</ul></details>)r5   r   len)titlediagnosticsitemss      r   _diagnostics_blockr      sI     GG?;??E
1$u+
SoeWO	Er   c                   | syg d}g }t        |       D ]  }| |   }|j                  dt        |       dt        |j                  d             dt        |j                  d             dt        |j                  d             dt        |j                  d             dt        |j                  d	             dt        |j                  d
             dt        |j                  d             dt        |j                  d             d        dj	                  d |D              }d| ddj	                  |       dS )Nz/<p class="fp-empty">no per-bg render facts.</p>)	bg_idtarget_unitsdominant_unituse	base_refs	transientcleanapplies_to_shotsdepends_on_bgz<tr><td>z	</td><td>target_unit_marker_numbers"dominant_target_unit_marker_numberuse_numbered_elements base_marker_numbers_to_reference$transient_marker_numbers_to_describeclean_background_expectedr   r   z
</td></tr>r[   c              3  :   K   | ]  }d t        |       d  yw)z<th>z</th>Nrx   )ry   hs     r   r{   z&_per_bg_facts_table.<locals>.<genexpr>  s     ?w!$tAwiu-wr|   z#<table class="fp-table"><thead><tr>z</tr></thead><tbody>z</tbody></table>)sortedr3   r   rB   r5   )per_bgheadersrowsr   facts	head_htmls         r   _per_bg_facts_tabler      sh    @
G Du;-  		">?@A B		"FGHI J		"9:;< =		"DEFG H		"HIJK L		"=>?@ A		"4567 8		/234 5
	
   ?w??I	[ !''$- !	r   zW20A2 review)r   r   page_title_prefixc           
     x	   | j                  dd      }|j                  d      xs ddg}t        |d         t        |d         }}| j                  d      }	| j                  di       j                  d	      }
g }|j                  t        ||||
             |j                  t	                      |j                  t        ||||
             |j                  dg       D ]  }|j                  t        |||             ! |j                  dg       D ]  }|j                  t        |||             ! |j                  di       j                         D ]6  \  }}	 t        |      }|D ]   }|j                  t        ||||             " 8 |j                  di       }i }| j                  d      xs g D ]$  }	 |j                  dd      |t        |d         <   & |j                         D ]B  \  }}	 t        |      }|j                  |d      }|j                  t        |||||             D |j                  d       dj                  |      }t        d|j                  d      xs g       }t        d|j                  d      xs g       }t        d| j                  d      xs g       }t        | j                  d      xs i       }d}|	rd t!        |	       d!}| d"| }d#}g }|j                  d$t!        |       d%       |j                  d&       |j                  d't!        |j                  d(             d)       |j                  d*| d+| d)       |
r|j                  d,t!        |
       d)       |j                  d-       |j                  d.       |j                  |       |j                  d/       |j                  |       |j                  d0       |j                  d1       |j                  |       |j                  d2       |j                  |       |j                  |       |j                  |       dj                  |      } d3t!        |       d4| d5|  d6S # t        t        f$ r Y 5w xY w# t        t        f$ r Y w xY w# t        t        f$ r Y w xY w)7z)Return a self-contained HTML page string.fp_id?	grid_size
   r   r,   fp_image_pathdwelling_identitystandard_of_living_band)r#   r$   r   r   view_cone_records)rO   r   r   direction_vector_recordscamera_cell_candidates_per_unit)rC   rJ   r   r   marker_cellsbase_marker_inventorybase_layer_decisionr[   rD   )rC   rD   rE   r   r   z</svg>r2   zgeometry diagnosticsr   )r~   r   z"wall/door invalidation diagnostics"wall_door_invalidation_diagnosticszdossier diagnosticsper_bg_render_facts_by_bg_id)r   z.<p class="fp-image-path">fp_image_path: <code>z</code></p>u
    · fp_id=a  
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
       Roboto, Helvetica, Arial, sans-serif; margin: 24px;
       color: #222; background: #f7f7f9; }
h1 { font-size: 18px; margin: 0 0 6px; }
h2 { font-size: 13px; margin: 16px 0 6px; color: #555; }
.fp-meta { font-size: 12px; color: #666; margin-bottom: 10px; }
.fp-meta code { background: #ececef; padding: 1px 4px; border-radius: 3px; }
.fp-pills { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 12px; }
.fp-pill { background: #1e6aa1; color: #fff; font-size: 11px;
           padding: 2px 8px; border-radius: 10px; }
.fp-pill.note { background: #d97706; }
.fp-pill.warn { background: #b91c1c; }
.fp-pill.muted { background: #6b7280; }
.fp-review-svg { background: #fff; border: 1px solid #ddd;
                 box-shadow: 0 1px 3px rgba(0,0,0,0.06);
                 max-width: 100%; height: auto; }
.fp-diag { margin: 10px 0; background: #fff;
           padding: 8px 12px; border: 1px solid #e5e5e5;
           border-radius: 4px; font-size: 12px; }
.fp-diag summary { cursor: pointer; font-weight: 600; }
.fp-diag ul { margin: 6px 0 4px 18px; padding: 0; }
.fp-table { border-collapse: collapse; font-size: 11px;
            background: #fff; margin-top: 6px; }
.fp-table th, .fp-table td { border: 1px solid #ddd;
                             padding: 3px 6px; text-align: left;
                             vertical-align: top; }
.fp-table th { background: #ececef; }
.fp-image-path { font-size: 12px; color: #555; }
.fp-empty { font-size: 12px; color: #777; font-style: italic; }
.legend { font-size: 11px; color: #555; margin-top: 4px; }
.legend span { display: inline-block; width: 10px; height: 10px;
               border-radius: 50%; vertical-align: middle;
               margin-right: 4px; }
z<h1>z</h1>z<div class="fp-pills">z'<span class="fp-pill">readback_status: readback_statusz</span>z!<span class="fp-pill note">grid:    ×z"<span class="fp-pill muted">band: uH   <span class="fp-pill warn">NOT a final BG — review surface only</span>z</div>u'   <h2>geometry overlay (10×10 grid)</h2>uo  <p class="legend"><span style="background:#1e6aa1"></span> unit · <span style="background:#d97706"></span> opening · <span style="background:#0b8060"></span> fixture · <span style="background:#7c3aed"></span> furniture · dashed ring = candidate camera cell · red arrow = candidate direction vector · green/blue/purple cone = wide/normal/telephoto view cone.</p>z<h2>per-bg render facts</h2>z<h2>diagnostics</h2>zC<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>z</title><style>z</style></head><body>z</body></html>
)rB   r
   r3   r'   r)   r9   ru   rV   r   	TypeError
ValueErrorrK   rG   r5   r   r   r   )!dossiergeometryr   r   r   r   gridr   colsr   standard_of_livingpartsrO   unit_str	cell_listrJ   rC   r   kind_by_numberentrynum_strrD   rE   svg_str	diag_geom	diag_walldiag_dossierper_bg_tablefp_image_htmlr~   css
body_parts	body_htmls!                                    r   render_review_htmlr   %  s	    KK%E<<$0RDT!Ws47|$DKK0M',001JK  E	LL$S  
LL	LL$S 
 ,,2B7*7
 	 8 ,,92>)7
 	 ?  (||)2 eg )	h-K DLL*'	     <<3L%'N45;;	3899%r4N3uX/0 < &++-	\F !!&"-_
 	 . 
LLiiG #$LL/52I #2LL!EFL"I &#KK.4"L '{{9:@bL
 M-()6 	
 !!E73E"CH JT%[M/0./
1./0
1	: 
+D6D6A 0&'(1	
  hm$?@g	
 45l#,-i i l#		*%I	((,U} 5 +	k :& 		& :& 		
 :& 		s6   Q:=!R7R&:RRR#"R#&R98R9)r   r   returnr   )
r   r
   r   r
   r   r
   r   r
   r   zTuple[int, int])
r#   r
   r$   r
   r   r
   r   r
   r   r   )r   r   )rC   	List[int]rD   r
   rE   r   r   r
   r   r
   r   r   )
rC   r   rJ   r
   r   r
   r   r
   r   r   )rO   Dict[str, Any]r   r
   r   r
   r   r   )r~   r   r   z	List[str]r   r   )r   zDict[str, Dict[str, Any]]r   r   )r   r   r   r   r   r
   r   r
   r   r   r   r   )__doc__
__future__r   htmlr   typingr   r   r   r   r   r   __annotations__r   r   r    r'   r)   r9   rG   rK   rV   ru   r   r   r   r   r   r   <module>r      s  $ #  3 3
  !  !0$'.1

"%
03
:=

	,, , 	,
 
, 	,^
  	
  
 	8
  	
 
 	(  
	
 	&66 6 
	6
 	6r		 )		&(&&Z #&+KK K 	K
 
K K 	Kr   