import cairosvg,math
W,H=1600,900
def txt(x,y,s,size=16,anchor="middle",weight="normal",fill="#111"):
    return f'<text x="{x}" y="{y}" font-family="Arial,Helvetica,sans-serif" font-size="{size}" text-anchor="{anchor}" font-weight="{weight}" fill="{fill}">{s}</text>'
def leader(x1,y1,x2,y2):
    return f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="#333" stroke-width="1.5"/><circle cx="{x2}" cy="{y2}" r="3" fill="#333"/>'
def vagues(x,y,w,n,col="#a8c4dc"):
    return "".join(f'<path d="M {x+i*w/n} {y} q {w/n/4} -6 {w/n/2} 0 q {w/n/4} 6 {w/n/2} 0" stroke="{col}" stroke-width="2" fill="none"/>' for i in range(n))
def eolienne(x,y,h,r):
    s=[f'<rect x="{x-2}" y="{y}" width="4" height="{h}" fill="#444"/>',f'<circle cx="{x}" cy="{y}" r="3" fill="#333"/>']
    for a in (0,120,240):
        ax=x+r*math.sin(math.radians(a));ay=y-r*math.cos(math.radians(a))
        s.append(f'<line x1="{x}" y1="{y}" x2="{ax:.0f}" y2="{ay:.0f}" stroke="#333" stroke-width="2.5"/>')
    return "".join(s)
def fleche(x1,y1,x2,y2,col="#e07b00"):
    a=math.atan2(y2-y1,x2-x1);l=12
    p1=(x2-l*math.cos(a-0.4),y2-l*math.sin(a-0.4));p2=(x2-l*math.cos(a+0.4),y2-l*math.sin(a+0.4))
    return f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{col}" stroke-width="4" stroke-dasharray="10,6"/><polygon points="{x2},{y2} {p1[0]:.0f},{p1[1]:.0f} {p2[0]:.0f},{p2[1]:.0f}" fill="{col}"/>'
def regle(x,ybas,yhaut,hmax,pas):
    s=[f'<line x1="{x}" y1="{ybas}" x2="{x}" y2="{yhaut}" stroke="#17415f" stroke-width="3"/>']
    n=int(hmax/pas)
    for i in range(n+1):
        y=ybas-(ybas-yhaut)*i/n
        s.append(f'<line x1="{x-8}" y1="{y:.0f}" x2="{x+8}" y2="{y:.0f}" stroke="#17415f" stroke-width="2"/>')
        s.append(txt(x+12,y+4,f"{int(i*pas):,} m".replace(","," "),11,"start","bold","#17415f"))
    s.append(txt(x,ybas+22,"Règle des hauteurs entre les deux bassins",11,"middle","bold","#17415f"))
    return "".join(s)
def dessin(sky):
    taille=3 if sky=="A" else sky
    s=[f'<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{H}" viewBox="0 0 {W} {H}">',f'<rect width="{W}" height="{H}" fill="#eef3f7"/>']
    mer=(taille==3)
    s.append('<polygon points="0,700 480,700 700,260 900,180 1150,120 1600,100 1600,830 0,830" fill="#a89a7c"/>')
    s.append('<polygon points="900,180 1150,120 1600,100 1600,140 1160,160 910,215" fill="#7f9a6a"/>')
    s.append('<polygon points="480,700 700,260 735,262 520,700" fill="#8c7b5c"/>')
    if mer:
        s.append('<rect x="0" y="700" width="480" height="130" fill="#3b7fb5"/>')
        s.append(vagues(10,720,460,10,"#9fc3e0"))
        s.append(txt(240,790,"La mer : bassin bas de la boucle salée, jamais pompée vers le lac",13,"middle","bold","#fff"))
    else:
        s.append('<polygon points="0,700 480,700 520,700 520,730 0,730" fill="#b5c49f"/>')
    s.append('<rect x="0" y="830" width="1600" height="70" fill="#17415f"/>')
    for i in range(12):
        x=960+i*50
        s.append(f'<line x1="{x}" y1="{145-i*2}" x2="{x+8}" y2="{125-i*2}" stroke="#2f6fb5" stroke-width="2" stroke-dasharray="3,3"/>')
    s.append('<path d="M 930 178 L 1590 108" stroke="#2c7fb8" stroke-width="5" fill="none"/>')
    s.append(txt(1260,80,"L'impluvium Pacha et la source artésienne remplissent le lac gratuitement" if sky=="A" else "L'impluvium Pacha et le cours d'eau Gold remplissent le lac gratuitement",14,"middle","bold"))
    s.append(txt(1260,98,"La Sky y ajoute l'eau remontée du lac du bas quand l'électricité ne vaut presque rien",12,"middle"))
    s.append('<ellipse cx="1080" cy="235" rx="165" ry="44" fill="#1b4f7a" stroke="#0a3d62" stroke-width="2"/>')
    s.append('<path d="M 922 250 Q 1080 300 1238 250" stroke="#f2c14e" stroke-width="3" fill="none" stroke-dasharray="8,5"/>')
    s.append(vagues(945,228,270,8))
    s.append('<rect x="908" y="218" width="12" height="46" fill="#8a6a3e"/>')
    s.append(txt(1080,262,"réserve Sky",11,"middle","bold","#f2c14e"))
    s.append(txt(1240,300 if sky!="A" else 305,"Le lac du haut : la pluie au-dessus, la réserve Sky en dessous",14,"middle","bold"))
    s.append(txt(1240,318 if sky!="A" else 323,{2:"8 millions de m³ de pluie + 6,4 millions de réserve, digue rehaussée : 4,7 GWh gardés pour l'hiver",3:"4 millions de m³ de pluie + 12,8 millions de réserve d'eau douce : 47 GWh gardés pour l'hiver"}[taille],12,"middle"))
    s.append('<path d="M 935 268 L 935 330 L 860 330" stroke="#5b8fb5" stroke-width="5" fill="none"/>')
    s.append('<rect x="730" y="330" width="130" height="18" rx="4" fill="#8a6a3e"/>')
    s.append('<rect x="842" y="268" width="56" height="30" rx="3" fill="#e07b00"/>')
    s.append(txt(870,288,"relevage",10,"middle","bold","#fff"))
    s.append(fleche(860,330,880,300))
    s.append(fleche(880,268,912,240))
    s.append(txt(690,288,"Station de relevage de 30 m :",11,"end","bold","#b35f00"))
    s.append(txt(690,303,"du bassin haut vers le lac",11,"end"))
    s.append(txt(690,318,"le lac redescend par gravité",11,"end"))
    if taille==3:
        s.append('<rect x="600" y="350" width="230" height="45" fill="#1b4f7a" stroke="#0a3d62" stroke-width="2"/>')
        s.append('<rect x="713" y="350" width="4" height="45" fill="#d8cdb4"/>')
        s.append(vagues(605,372,105,3));s.append(vagues(720,372,105,3,"#c8e0b0"))
        s.append(txt(845,378,"Bassin haut 2 × 400 000 m³",12,"start","bold"))
    else:
        s.append('<rect x="600" y="350" width="230" height="45" fill="#1b4f7a" stroke="#0a3d62" stroke-width="2"/>')
        s.append(vagues(605,372,220,6))
        s.append(txt(845,378,"Bassin haut 800 000 m³",12,"start","bold"))
    for i in range(8):
        x=1200+i*45
        s.append(eolienne(x,132-i*3,50,18))
    s.append(txt(1300,348,{2:"40 éoliennes de 6 MW et 16 MWc : quand le réseau déborde, elles pompent d'abord",3:"10 éoliennes, 100 MWc et 1 000 panneaux hybrides pompent d'abord, le réseau complète"}[taille],13,"middle","bold"))
    s.append(txt(1300,366,{2:"70 des 82 GWh remontés chaque année viennent de notre vent et de notre soleil",3:"85 GWh de notre production, 334 GWh de surplus du réseau à prix presque nul","A":"80 GWh de notre production, 339 GWh de surplus du réseau à prix presque nul"}[sky if sky=="A" else taille],12,"middle"))
    s.append('<path d="M 640 395 L 520 700 L 440 700" stroke="#7fb3d9" stroke-width="9" fill="none"/>')
    s.append('<path d="M 640 395 L 520 700 L 440 700" stroke="#2c3e50" stroke-width="3" fill="none"/>')
    s.append(fleche(540,640,610,465))
    l1,l2={2:("Surplus : les pompes de 64 MW, presque","à l'arrêt sur la Gold, remontent le lac du bas"),3:("Surplus : les pompes de 300 MW aspirent","dans le lac d'eau douce, jamais dans la mer")}[taille]
    s.append(txt(640,580,l1,13,"start","bold","#b35f00"))
    s.append(txt(640,597,l2,13,"start","bold","#b35f00"))
    l1,l2={2:("Deux DN2000 et une troisième conduite, 300 m :","Pelton 48 MW + Pelton Sky 25 MW"),3:("Puits blindé de 1 500 m :","Pelton 300 MW en duplex + Pelton Sky 100 MW")}[taille]
    s.append(txt(640,657,l1,14,"start","bold"))
    s.append(txt(640,675,l2,14,"start","bold"))
    s.append(leader(636,657,572,600))
    s.append('<rect x="270" y="645" width="200" height="90" fill="#5a4024"/>')
    s.append('<rect x="315" y="630" width="110" height="20" fill="#8a6a3e"/>')
    s.append(txt(370,668,"Usine",13,"middle","bold","#fff"))
    s.append(txt(370,688,"surplus : pompes, Pelton arrêtées",10,"middle","normal","#fff"))
    s.append(txt(370,704,"forte demande : tout tourne 24 h/24",10,"middle","normal","#fff"))
    s.append(txt(370,720,"le reste du temps : la Gold",10,"middle","normal","#fff"))
    if taille==3:
        s.append('<ellipse cx="135" cy="530" rx="115" ry="24" fill="#1b4f7a" stroke="#0a3d62" stroke-width="2"/>')
        s.append(vagues(45,530,180,5,"#c8e0b0"))
        s.append(txt(135,570,"Lac du bas d'eau douce, 12,8 Mm³",12,"middle","bold"))
        s.append(txt(135,585,"l'eau de pluie turbinée, gardée pour remonter",10,"middle"))
        s.append('<rect x="20" y="605" width="235" height="38" fill="#1b4f7a" stroke="#0a3d62" stroke-width="2"/>')
        s.append(vagues(25,628,225,6,"#c8e0b0"))
        for i in range(8):
            s.append(f'<rect x="{24+i*29}" y="600" width="25" height="8" fill="#2d3e66" stroke="#9fb3d1" stroke-width="1"/>')
        s.append(txt(137,660,"Bassin bas d'eau douce couvert, gardé :",12,"middle","bold"))
        s.append(txt(137,675,"l'eau de la ville, à l'abri des fortes chaleurs",10,"middle"))
        s.append('<line x1="248" y1="537" x2="248" y2="600" stroke="#5b8fb5" stroke-width="5"/>')
        s.append('<line x1="270" y1="690" x2="255" y2="630" stroke="#5b8fb5" stroke-width="5"/>')
        s.append('<rect x="40" y="440" width="40" height="55" fill="#c3ccd6" stroke="#9aa6b2"/>')
        s.append('<rect x="100" y="450" width="40" height="45" fill="#c3ccd6" stroke="#9aa6b2"/>')
        s.append(txt(155,412,"La ville côtière garde son eau douce :",11,"middle","bold"))
        s.append(txt(155,427,"la Sky prête l'eau, elle la rend",11,"middle","bold"))
    else:
        s.append('<rect x="250" y="745" width="220" height="55" fill="#1b4f7a" stroke="#0a3d62" stroke-width="2"/>')
        s.append(vagues(255,772,210,5))
        s.append(txt(360,818,"Bassin bas 800 000 m³",12,"middle","bold"))
        s.append('<line x1="300" y1="735" x2="300" y2="745" stroke="#5b8fb5" stroke-width="6"/>')
        s.append('<ellipse cx="125" cy="772" rx="112" ry="32" fill="#1b4f7a" stroke="#0a3d62" stroke-width="2"/>')
        s.append(vagues(35,772,180,5))
        s.append('<path d="M 237 772 L 250 772" stroke="#5b8fb5" stroke-width="6"/>')
        s.append(txt(125,820,"Lac du bas 6,4 millions de m³",12,"middle","bold"))
        s.append('<path d="M 13 772 L 0 772" stroke="#5b8fb5" stroke-width="6"/>')
        s.append(txt(135,650,"Lac du bas à côté du bassin bas :",12,"middle","bold"))
        s.append(txt(135,668,"il garde l'eau turbinée pour la remonter,",12,"middle"))
        s.append(txt(135,686,"le surplus continue vers la vallée",12,"middle"))
        s.append(leader(120,695,110,745))
    s.append(regle(1010,770 if taille==2 else 700,372,300 if taille==2 else 1500,50 if taille==2 else 500))
    if sky=="A":
        s.append('<polygon points="1250,175 1600,150 1600,175 1250,200" fill="#6b6b6b"/>')
        s.append('<polygon points="1250,200 1600,175 1600,215 1250,240" fill="#4f86b8"/>')
        s.append('<polygon points="1250,240 1600,215 1600,240 1250,265" fill="#6b6b6b"/>')
        s.append(txt(1430,232,"nappe captive sous pression",11,"middle","bold","#fff"))
        s.append(txt(1590,168,"couche imperméable",10,"end","normal","#fff"))
        s.append('<line x1="900" y1="196" x2="1600" y2="150" stroke="#c0392b" stroke-width="2" stroke-dasharray="6,4"/>')
        s.append('<path d="M 1260 222 L 1240 225 L 1240 210" stroke="#2c7fb8" stroke-width="7" fill="none"/>')
        s.append('<path d="M 1240 210 q -6 -14 -12 0 M 1240 210 q 6 -14 12 0" stroke="#2c7fb8" stroke-width="3" fill="none"/>')
        s.append(txt(1425,276,"Source artésienne de 2 m³/s : la pression de la nappe",11,"middle","bold","#17415f"))
        s.append(txt(1425,289,"(ligne rouge) la porte jusqu'au lac, sans pompe",11,"middle","bold","#17415f"))
    s.append('<g transform="translate(1080,465)">')
    s.append('<rect x="0" y="0" width="505" height="170" fill="#ffffff" stroke="#17415f" stroke-width="2"/>')
    s.append(txt(252,24,{2:"V18 Sky 2 : la Gold 2 qui garde le vent pour les jours sans vent",3:"V18 Sky 3 : la Gold 3 devenue réserve d'hiver à 1 500 m","A":"V18 Sky Artesia : la Sky 3 et une source artésienne"}[sky],15,"middle","bold","#17415f"))
    lignes={2:["Surplus : Pelton arrêtées, nos éoliennes pompent le lac du bas vers le haut","Réserve de 6,4 millions de m³ gardée pour l'hiver, en plus des 8 de pluie","Forte demande : 48 + 25 MW + Pelton de pluie, 24 h sur 24, tant qu'il y a de l'eau","87 M€ ajoutés, +11,3 M€ par an, 5,9 M€ nets","Le MWh vendu vaut 95,6 € au lieu de 87,6 : on vend mieux, pas plus cher","Aller-retour 71 % : 300 m rendus pour 330 m pompés"],3:["Surplus : les pompes de 300 MW remontent le lac d'eau douce du bas","Réserve de 12,8 millions de m³ à 1 500 m : 47 GWh, 5 jours à pleine puissance","Forte demande : 400 MW de Pelton, 1 802 GWh vendus au lieu de 1 565","225 M€ ajoutés, +42,4 M€ par an, 28,2 M€ nets","Le MWh vendu vaut 108,1 € au lieu de 98,0","Jamais d'eau de mer dans le lac : aspiration commutée et chasse d'eau douce"],"A":["Source artésienne de 2 m³/s : 26 MW d'eau gratuite à 1 500 m, jour et nuit","La pression de la nappe porte l'eau jusqu'au lac, sans pompe","Réserve de 12,8 millions de m³ : 47 GWh, remplie en partie par la source","1 721 GWh vendus au lieu de 1 429 pour la Pacha 3, lac vide 7 jours au lieu de 136","229 M€ ajoutés à la Pacha 3, +45,4 M€ par an, 31,0 M€ nets","On ne prend à la nappe que ce qu'elle rend : jamais d'eau fossile"]}[sky]
    for i,l in enumerate(lignes):
        s.append(txt(15,52+i*20,l,12,"start","bold" if i==3 else "normal"))
    s.append('</g>')
    titres={"A":"Hydroloop Muhleisen 2026 V18 Sky Artesia : la Sky 3 plus une source artésienne, l'eau qui monte seule jusqu'au lac",2:"Hydroloop Muhleisen 2026 V18 Sky 2 : la Gold 2 plus un lac du bas, on remonte l'eau quand le réseau déborde",3:"Hydroloop Muhleisen 2026 V18 Sky 3 : la Gold 3 plus un lac du bas d'eau douce, 47 GWh gardés pour l'hiver"}
    s.append(txt(800,860,titres[sky],21,"middle","bold","#fff"))
    s.append(txt(800,885,"Coupe schématique : hauteurs à l'échelle entre les deux bassins (règle), le reste non. www.mulo.world, octobre 2026, CC BY 4.0",13,"middle","normal","#dfe7ee"))
    s.append('</svg>')
    return "\n".join(s)
for k in (2,3,"A"):
    cairosvg.svg2png(bytestring=dessin(k).encode("utf-8"),write_to=f"schema_hydroloop_muhleisen_2026_v18_sky_{'artesia' if k=='A' else k}.png",output_width=W,output_height=H)
print("ok")
