Skip to content
use base64::Engine;
use std::{
    env,
    fs::{self, File},
    io::{Read, Write},
    net::TcpListener,
    path::PathBuf,
};

fn main() {
    let args: Vec<String> = env::args().collect();
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string());
    let html_path = PathBuf::from(&manifest_dir).join("target/index.html");
    let js_path = PathBuf::from(&manifest_dir).join("target/hecatomb.js");

    if args.len() > 1 && args[1] == "serve" {
        let mut port = 10000;
        if args.len() > 2 {
            if let Ok(p) = args[2].parse::<u16>() {
                port = p;
            } else if args[2] == "--port" || args[2] == "-p" {
                if args.len() > 3 {
                    if let Ok(p) = args[3].parse::<u16>() {
                        port = p;
                    }
                }
            }
        }

        println!(
            "Serving {} on http://127.0.0.1:{}",
            html_path.display(),
            port
        );

        let listener = match TcpListener::bind(format!("127.0.0.1:{}", port)) {
            Ok(l) => l,
            Err(e) => {
                eprintln!("Error binding to port {}: {}", port, e);
                std::process::exit(1);
            }
        };

        for stream in listener.incoming() {
            let mut stream = match stream {
                Ok(s) => s,
                Err(_) => continue,
            };

            let mut buffer = [0; 4096];
            let n = stream.read(&mut buffer).unwrap_or(0);
            let request = String::from_utf8_lossy(&buffer[..n]);
            let path = request
                .lines()
                .next()
                .and_then(|line| line.split_whitespace().nth(1))
                .unwrap_or("/");

            let (file_path, content_type) = if path == "/hecatomb.js" {
                (&js_path, "text/javascript; charset=utf-8")
            } else {
                (&html_path, "text/html; charset=utf-8")
            };

            match fs::read(file_path) {
                Ok(content) => {
                    let response = format!(
                        "HTTP/1.1 200 OK\r\n\
                         Content-Type: {}\r\n\
                         Content-Length: {}\r\n\
                         Connection: close\r\n\r\n",
                        content_type,
                        content.len()
                    );
                    let _ = stream.write_all(response.as_bytes());
                    let _ = stream.write_all(&content);
                }
                Err(e) => {
                    let error_msg = format!(
                        "Error: file not found at {}. Run build first. Details: {}",
                        file_path.display(),
                        e
                    );
                    let response = format!(
                        "HTTP/1.1 404 NOT FOUND\r\n\
                         Content-Type: text/plain; charset=utf-8\r\n\
                         Content-Length: {}\r\n\
                         Connection: close\r\n\r\n{}",
                        error_msg.len(),
                        error_msg
                    );
                    let _ = stream.write_all(response.as_bytes());
                }
            }
            let _ = stream.flush();
        }
        return;
    }

    println!("Building");
    let wasm_path =
        PathBuf::from(&manifest_dir).join("target/wasm32-unknown-unknown/release/hecatomb.wasm");
    let Ok(mut wasm_file) = File::options().read(true).open(&wasm_path) else {
        panic!("could not locate wasm file");
    };

    // turn file into base64
    let mut buffer = Vec::new();
    wasm_file.read_to_end(&mut buffer).unwrap();
    let base64 = base64::engine::general_purpose::STANDARD.encode(&buffer);

    // print out base64
    let first_ten_chars: String = base64.chars().take(10).collect();
    println!("base64 created {}", first_ten_chars);

    //inline wasm into the js bundle (served separately, not inlined into html)
    let js_template = include_str!("template.js");
    let js = js_template.replace("{{}}", base64.as_str());

    std::fs::write(&js_path, js).unwrap();
    println!("Wrote {}", js_path.display());

    // html loads the js bundle from the server via <script src="/hecatomb.js">
    let html = include_str!("template.html");
    std::fs::write(&html_path, html).unwrap();
    println!("Wrote {}", html_path.display());
}