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 ile(x,y,w,n,col="#2d3e66"):
    out=[]
    pas=w/n
    for i in range(n):
        cx=x+pas*i+pas/2
        out.append(f'<rect x="{cx-pas*0.42:.0f}" y="{y-3}" width="{pas*0.84:.0f}" height="6" fill="{col}" stroke="#9fb3d1" stroke-width="1"/>')
        out.append(f'<circle cx="{cx:.0f}" cy="{y-9}" r="6" fill="none" stroke="#222" stroke-width="1.5"/>')
        out.append(f'<line x1="{cx-5:.0f}" y1="{y-12}" x2="{cx+5:.0f}" y2="{y-6}" stroke="#222" stroke-width="1.5"/>')
    return "".join(out)
def flot(x,y,w,n,col="#2d3e66"):
    pas=w/n
    return "".join(f'<rect x="{x+pas*i+2:.0f}" y="{y-4}" width="{pas-4:.0f}" height="8" fill="{col}" stroke="#9fb3d1" stroke-width="1"/>' for i in range(n))
def dessin(sky):
    taille=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 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,"Le lac du haut : pluie au-dessus, réserve Sky en dessous, sans flottant",14,"middle","bold"))
    s.append(txt(1240,318,{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³",11,"start","bold"))
        s.append(txt(845,394,"sans flottant : boucle salée,",11,"start"))
        s.append(txt(845,408,"niveau qui change chaque jour",11,"start"))
    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(ile(606,352,218,7))
        s.append(txt(845,378,"Bassin haut 800 000 m³",12,"start","bold"))
        s.append(txt(845,394,"île hybride : 168 modules",11,"start","bold","#17415f"))
        s.append(txt(845,408,"+ 9,6 MWc de flottant",11,"start","bold","#17415f"))
    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"}[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(ile(40,528,190,6))
        s.append(txt(130,570,"Lac du bas d'eau douce, 12,8 Mm³, 100 ha",11,"middle","bold"))
        s.append(txt(135,585,"île hybride, 1 144 modules + 58,5 MWc",10,"middle","bold","#17415f"))
        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(130,660,"Bassin bas de la ville : flottant 6 MWc",11,"middle","bold"))
        s.append(txt(130,675,"il produit, et l'eau reste à l'abri",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(flot(252,747,216,8))
        s.append(txt(360,814,"Bassin bas 800 000 m³",11,"middle","bold"))
        s.append(txt(360,827,"flottant classique, 6 MWc",10,"middle","bold","#17415f"))
        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(ile(30,768,190,6))
        s.append('<path d="M 237 772 L 250 772" stroke="#5b8fb5" stroke-width="6"/>')
        s.append(txt(125,814,"Lac du bas 6,4 Mm³, 64 ha",11,"middle","bold"))
        s.append(txt(125,827,"île hybride, 714 modules + 37,8 MWc",10,"middle","bold","#17415f"))
        s.append('<path d="M 13 772 L 0 772" stroke="#5b8fb5" stroke-width="6"/>')
        s.append(txt(135,650,"Le lac du bas garde l'eau pour la remonter ;",12,"middle","bold"))
        s.append(txt(135,668,"l'O Sky le couvre d'une île hybride :",12,"middle"))
        s.append(txt(135,686,"moins d'évaporation, du vent vendu le soir",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))
    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:"V20 O Sky 2 : la Sky 2 dont les eaux produisent aussi",3:"V20 O Sky 3 : la Sky 3 dont le lac d'eau douce produit aussi"}[sky],15,"middle","bold","#17415f"))
    lignes={2:["Îles hybrides sur le lac du bas (64 ha) et le bassin haut (16 ha),","flottant classique sur le bassin bas (8 ha) : 882 rotors + 53 MWc","+106 GWh par an, dont 18 de vent vendus la nuit et l'hiver","62,6 M€ ajoutés à la Sky 2, +6,7 M€ par an, 1,9 M€ nets, retour 9,4 ans","1 357 GWh vendus au lieu de 1 261 : elle produit plus, ne stocke pas plus","88 ha couverts : 0,4 million de m³ d'évaporation évités par an"],3:["Île hybride sur le lac du bas d'eau douce (100 ha), flottant classique","sur le bassin bas de la ville : 1 144 rotors + 64 MWc","+78 GWh vendus par an, dont 26 de vent","77,6 M€ ajoutés à la Sky 3, +7,2 M€ par an, 1,3 M€ nets, retour 10,7 ans","1 880 GWh vendus au lieu de 1 802 : plus de production, même stockage","Jamais de flottant sur la boucle salée ni sur le lac de pluie"]}[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={2:"Hydroloop Muhleisen 2026 V20 O Sky 2 : la Sky 2 plus des îles de rotors hybrides et du flottant sur ses eaux",3:"Hydroloop Muhleisen 2026 V20 O Sky 3 : la Sky 3 plus une île hybride sur son lac d'eau douce"}
    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):
    cairosvg.svg2png(bytestring=dessin(k).encode("utf-8"),write_to=f"schema_hydroloop_muhleisen_2026_v20_o_sky_{k}.png",output_width=W,output_height=H)
print("ok")
