import json,copy
from pathlib import Path
p=Path(__file__).resolve().parent
s=json.loads((p/'chr19.json').read_text()); prov=json.loads((p/'chr19.extras.provenance.json').read_text()); tree=json.loads((p/'chr19.sets.obsSets.json').read_text())['tree']
base='https://users.abdenlab.org/voy/vitessce_cvh/hepg2_sci/chr19/'
s['name']='chr19 · how the liver handles cholesterol — HepG2 rE2G elements'
s['description']='HepG2 ENCODE rE2G ENCFF960GTR / ENCSR411RGL, GRCh38 chr19. Companion to the supplied Gosling cholesterol windows and colors. Scores are predicted rE2G link scores (0 = unreported link), not expression. ClinVar counts are positional overlaps ([POS-1, POS-1+len(REF))) with clinvar_hg38_all.vcf.gz and do not establish regulatory effects. CYP2A6 is absent from this matrix; its supplied locus window contains no elements.'
f=s['datasets'][0]['files']; f[:]=[v for v in f if v['fileType']!='obsSets.json']
for key,label in [('map','Chromosome map'),('links','Link landscape')]:
 f.append(dict(fileType='obsEmbedding.csv',url=base+f'chr19.{key}.embedding.csv',coordinationValues=dict(obsType='element',embeddingType=label),options=dict(obsIndex='element_id',obsEmbedding=['x','y'])))
f.append(dict(fileType='obsSets.json',url=base+'chr19.sets.obsSets.json',coordinationValues=dict(obsType='element')))
old=s['coordinationSpace']; c={k:copy.deepcopy(old[k]) for k in ['dataset','obsType','featureType','featureValueType','obsLabelsType','obsHighlight','featureHighlight']}
c.update(featureSelection={'A':None,'four':[v for ids in prov['gene_feature_ids'].values() for v in ids],'classes':None,'clinvar':None},obsSetSelection={'A':[['Gosling columns',n['name']] for n in tree[0]['children'][:4]]},obsSetExpansion={'A':[['Gosling columns']]},obsSetColor={'A':[dict(path=[g['name'],n['name']],color=n['color']) for g in tree for n in g['children']]},obsColorEncoding={'A':'cellSetSelection'},embeddingType={'map':'Chromosome map','land':'Link landscape'},embeddingObsSetLabelsVisible={'map':False,'land':True},embeddingObsSetPolygonsVisible={'A':False},embeddingObsRadiusMode={'A':'manual'},embeddingObsRadius={'map':3,'land':2},embeddingObsOpacityMode={'A':'auto'},heatmapZoomX={'rows':0},heatmapZoomY={'rows':0},heatmapTargetY={'rows':0},heatmapTargetX={k:0 for k in ['scores','classes','clinvar']},featureValueColormap={k:'plasma' for k in ['scores','classes','clinvar']},featureValueColormapRange={'scores':[0,1],'classes':[0,1],'clinvar':[0,.5]})
s['coordinationSpace']=c
common=dict(dataset='A',obsType='A'); sets=dict(obsSetSelection='A',obsSetColor='A',obsSetExpansion='A'); gene=dict(featureType='A',featureValueType='A',featureSelection='A',featureHighlight='A')
def panel(component,xywh,scopes,**props):
 return dict(component=component,coordinationScopes={**common,**scopes},props=props,**dict(zip(['x','y','w','h'],xywh)))
guide='''**HepG2 predicted regulatory links on chr19** — companion to the four Gosling windows and colors.

Each dot is one rE2G element. **Chromosome map:** x = midpoint (Mb); height = log₂(1 + ClinVar count), scaled. **Link landscape:** UMAP of normalized gene-score profiles; proximity indicates similar predicted targets.

Click a gene to color both maps by its link score and update the violin. Select element sets to return to set colors and filter the heatmaps. The dot plot uses LDLR, APOE and TM6SF2 (full feature IDs).

**Source limitation:** CYP2A6 is absent from this matrix and the supplied CYP2A6–2B6 locus contains no elements. Its ±1 Mb neighborhood remains available. Missing data do not imply zero biological activity.

Scores are links, not expression; ClinVar overlaps do not establish regulatory effects. Gosling correspondence is by windows/colors, not automatic cross-application synchronization.'''
l=[panel('description',(0,0,3,4),{},title='chr19 · how the liver handles cholesterol',description=guide,descriptionType='markdown')]
for key,xywh,title in [('map',(3,0,6,4),'Chromosome map · position on chr19 × ClinVar burden'),('land',(9,0,3,4),'Link landscape · similar predicted targets')]:
 sc={**gene,**sets,'obsHighlight':'A','embeddingType':key,'obsColorEncoding':'A','embeddingObsSetLabelsVisible':key,'embeddingObsSetPolygonsVisible':'A','embeddingObsRadiusMode':'A','embeddingObsRadius':key,'embeddingObsOpacityMode':'A','featureValueColormap':'scores','featureValueColormapRange':'scores','obsLabelsType':['clinvar_count','ccre_matches'] if key=='map' else ['clinvar_count']}
 l.append(panel('scatterplot',xywh,sc,title=title,observationsLabelOverride='element'))
for key,ft,xywh,title,labels in [('classes','classes',(0,4,2,5),'cCRE class',['ccre_matches','ccre_count']),('clinvar','clinvar',(2,4,2,5),'ClinVar records',['clinvar_count']),('scores','A',(4,4,5,5),'rE2G link score · element × gene',['ccre_count','clinvar_count'])]:
 sc={**sets,'featureType':ft,'featureValueType':ft,'featureSelection':ft,'featureHighlight':ft,'obsHighlight':'A','obsLabelsType':labels,'heatmapZoomX':'rows','heatmapZoomY':'rows','heatmapTargetY':'rows','heatmapTargetX':key,'featureValueColormap':key,'featureValueColormapRange':key,'obsColorEncoding':'A'}
 props=dict(transpose=False,title=title,observationsLabelOverride='regulatory element' if key=='classes' else '\u00a0')
 if key!='scores':props['variablesLabelOverride']='cCRE class' if key=='classes' else 'ClinVar annotation'
 l.append(panel('heatmap',xywh,sc,**props))
l.extend([panel('obsSets',(9,4,3,4),{**sets,'obsColorEncoding':'A'},title='Element sets'),panel('featureList',(9,8,3,4),{**gene,'obsColorEncoding':'A'},title='Genes',variablesLabelOverride='gene'),panel('dotPlot',(0,9,3,3),{**gene,**sets,'featureSelection':'four','featureValueColormap':'scores'},title='Locus elements × three available genes',transpose=False),panel('obsSetFeatureValueDistribution',(3,9,3,3),{**gene,**sets},title='Pick a gene → its link scores per set'),panel('obsSetSizes',(6,9,3,3),sets,title='Elements per set')])
s['layout']=l
if (p/'chr19-cholesterol.vitessce.json').exists():
 raise FileExistsError('Companion config already exists; keep it or explicitly archive it before rebuilding.')
(p/'chr19-cholesterol.vitessce.json').write_text(json.dumps(s,indent=2,ensure_ascii=False)+'\n')
