//!wrt $BSPEC:{"icn":"apps/linux","cpr":"Copyright (C) Windows 96 Team 2023.","dsc":"Linux subsystem for SYSTEM36","frn":"Linux","ver":1,"ssy":"cli"}

const term=this.boxedEnv.term;if(!term)return 1;const{loader:Loader}=w96.sys,{Theme:Theme}=w96.ui,{ProgressBar:ProgressBar,Spinner:Spinner}=w96.util.cli,{argParser:ArgParser,requestTerminal:requestTerminal}=w96.util,ON_MAINSITE="1"==new URLSearchParams(location.search).get("9lxetc")||location.hostname.endsWith("windows96.net"),V86_USER_URL=ON_MAINSITE?"https://etc.windows96.net/9LX/":"//localhost:8000/",NET_RELAY_URL="wss://relay.widgetry.org/",LINUX_DIRECTORY="C:/system/linux",ROOTFS_DIRECTORY_R="C:/system/linux/rootfs",ROOTFS_VERSION=1,ROOTFS_DIRECTORY=ROOTFS_DIRECTORY_R+"/1",ROOTFS_URL=V86_USER_URL+"rootfs.tar.gz",FT_PATH=ROOTFS_DIRECTORY+".json";w96.wmem.linemulators||(w96.wmem.linemulators=[]);let emulators=w96.wmem.linemulators;function parseFile(t){let e={mode:t.mode,mtime:t.mtime,type:t.type,uid:0,gid:0,link:null};return"2"===e.type&&(e.link=t.linkname),"L"!==e.type&&e}async function saveFT(t){return await FS.writestr(FT_PATH,JSON.stringify(t))}async function readFT(){return JSON.parse(await FS.readstr(FT_PATH))}class MappedUNFS{constructor(t,e,r){this.ft=e,this.basePath=t,this.dbg=r}async _syncFT(){await saveFT(this.ft)}_getPath(t,e=!0){return this.basePath+(e?"/":"")+t}_getDirectory(t){let e=t.split("/").filter((t=>t));return e.pop(),e=e.join("/"),e="/"+e+"/",e.startsWith("//")&&(e=e.substring(1)),e}_getMode(t,e){let r=e+"";if("2"===t)r=r.replaceAt(1,"12");else if("5"===t)r=r.replaceAt(2,"4");else{if("0"!==t)return!1;r=r.replaceAt(1,"1")}return parseInt(r,8)}async getSize(t){return this.dbg("GS "+t),(await FS.stat(this._getPath(t,!1))).length}async read(t,e,r){return this.dbg("READ "+t),(await FS.readbin(this._getPath(t,!1))).subarray(e,e+r)}async write(t,e){return this.dbg("WRITE "+t),await FS.mkdir(this._getPath(this._getDirectory(t),!1)),await FS.writebin(this._getPath(t,!1),e)}uncache(t){this.dbg("UNCACHE "+t)}}class JSLinuxApplication extends WApplication{constructor(){super()}async main(t){super.main(t),t.shift();let e=t;if(!await FS.exists(ROOTFS_DIRECTORY)||!await FS.exists(FT_PATH)){for(term.write("Your JSLinux root filesystem is outdated, corrupted or missing. \nTo continue, you must download a filesystem package (~83MB). \nPlease note that the extraction process may temporarily freeze your browser.");;){let t=await term.prompt("Do you want to download it now? (yes/No): ",!0);if(!t)return;if(t=t.toLowerCase(),"n"==t||"no"==t)return;if("y"==t||"yes"==t)break}await FS.mkdir("C:/system/linux/dists/");const t="C:/system/linux/dists/_rootfsv1.tar.gz";if(await FS.exists(t))term.writeln("Using cached rootfs...");else{let e=new ProgressBar(40,"Downloading",term),r=await fetch(ROOTFS_URL),i=await r.body.getReader(),a=+r.headers.get("Content-Length"),s=[],n=0,o=(a/1024/1024).toFixed(2);for(;;){const{done:t,value:r}=await i.read();if(t)break;s.push(r),n+=r.length,e.setProgress(n/a*100,`${(n/1024/1024).toFixed(2)} MiB / ${o} MiB`)}let l=new Uint8Array(n),u=0;for(let t of s)l.set(t,u),u+=t.length;await FS.writebin(t,l),e.stop(`${o} MiB / ${o} MiB   `)}let e=new Spinner("Extracting... ",term);e.start(),await Loader.loadlibAsync("/system/libraries/extern/pako/pako_inflate.min.js"),await Loader.loadlibAsync("/system/libraries/extern/js-untar/untar.js");let r=await new Promise((e=>e(FS.readbin(t)))).then(pako.inflate).then((t=>t.buffer)).then(untar);e.stop(!0);let i={version:1,ft:{}},a=new ProgressBar(40,"Extracting ",term);await FS.mkdir(ROOTFS_DIRECTORY);let s=[];for(let t in r){a.setProgress(t/r.length*100,`${t} / ${r.length}`);let e=r[t],n=parseFile(e);if(!n)continue;let o=e.name;if(o.startsWith("./")&&(o=o.substring(1)),"0"===n.type){let t=o.split("/");t.pop(),t=t.join("/"),await FS.mkdir(ROOTFS_DIRECTORY+t),s.push([ROOTFS_DIRECTORY+o,new Uint8Array(e.buffer)])}"5"===n.type&&await FS.mkdir(ROOTFS_DIRECTORY+o),i.ft[o]=[n.type,n.mode,n.mtime,n.uid,n.gid,n.link||void 0],t%50==0&&await new Promise((t=>setTimeout(t,1)))}a.setProgress(100,`${r.length} / ${r.length}`),term.write("\n"),r=null,a.action="Writing    ";for(let t in s){a.setProgress(t/s.length*100,`${t} / ${s.length}`);let e=s[t];await FS.exists(e[0])&&(await FS.stat(e[0])).length==e[1].length||(await FS.writebin(e[0],e[1]),e[1]=null)}a.stop(`${s.length} / ${s.length}`),s=null;let n=0;for(let t of Object.keys(i.ft))i.ft[t].push(n),n+=1;i.ic=n,await saveFT(i)}await Loader.loadlibAsync(V86_USER_URL+"build/libv86.js");let r=emulators[emulators.findIndex((t=>t.count<3))];if(r){let t=r.emulator,e=Number(r.count);(0,r.dbgln)("Attached ttyS"+e),t.add_listener(`serial${e}-output-char`,(t=>{term.write(t)})),term.onData((r=>{t[`serial${e}_send`](r)})),t[`serial${e}_send`]("\r"),term.wnd.setTitle("ttyS"+e),r.count+=1}else{if(emulators[0]&&!e.includes("--force"))return term.writeln("over 4 instances are not supported. If you want this anyway, pass --force.");let t=()=>{},r=()=>{};if(e.includes("--debug")){let e=(await requestTerminal("jslinux-debug",await Theme.getIconUrl("apps/linux","16x16"))).terminal;t=(...t)=>e.writeln(...t),r=(...t)=>e.write(...t)}t("Starting emulation..."),await FS.mkdir(ROOTFS_DIRECTORY);let i=new V86Starter({wasm_path:V86_USER_URL+"build/v86.wasm",memory_size:536870912,filesystem:{class:new MappedUNFS(ROOTFS_DIRECTORY,await readFT(),t)},cmdline:["rw loglevel=50 apm=off","root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose","mitigations=off audit=0 edd=off","page_poison=on console=ttyS0","tsc=reliable nosmp maxcpus=0","random.trust_cpu=on","nowatchdog","net.ifnames=0 biosdevname=0 kernel.nmi_watchdog=0"].join(" "),bzimage_initrd_from_filesystem:!0,autostart:!0,bios:{url:V86_USER_URL+"bios/seabios.bin"},network_relay_url:NET_RELAY_URL,uart1:!0,uart2:!0});i.add_listener("serial0-output-char",(t=>{term.write(t)})),term.onData((t=>{i.serial0_send(t)})),term.wnd.setTitle("ttyS0"),emulators.push({emulator:i,count:1,dbgl:r,dbgln:t})}await new Promise((t=>this.completer=t))}ontermination(){}}

return await WApplication.execAsync(new JSLinuxApplication(), this.boxedEnv.args, this);