// Scyla Language Converter — Python/PyTorch → Scyla
// Written IN Scyla. This is both the tool and the proof.
//
// Three-pass architecture:
//   Pass 1: Line-by-line syntax translation (def→fn, indent→braces, imports→use)
//   Pass 2: API mapping (numpy/torch/math → Scyla builtins)
//   Pass 3: Polish (collapse empty lines, fix patterns, clean up)
//
// Usage: scylac run convert.sy
//   Then enter the path to a .py file when prompted.
//
// Aislinn McCubbins, April 2026

use converter::mappings

// ── Helpers ─────────────────────────────────────────────

fn count_leading_spaces(line: String) -> Int {
    let i = 0
    let len = str_len(line)
    while i < len {
        let ch = str_char_at(line, i)
        if str_eq(ch, " ") {
            i = i + 1
        } else {
            return i
        }
    }
    return i
}

fn is_blank(line: String) -> Bool {
    return str_eq(str_trim(line), "")
}

fn is_comment(line: String) -> Bool {
    return starts_with(str_trim(line), "#")
}

fn is_decorator(line: String) -> Bool {
    return starts_with(str_trim(line), "@")
}

fn is_docstring_marker(line: String) -> Bool {
    let t = str_trim(line)
    if starts_with(t, "\"\"\"") { return true }
    if starts_with(t, "'''") { return true }
    return false
}

fn remove_type_hints(line: String) -> String {
    let result = line
    result = str_replace(result, ": int", "")
    result = str_replace(result, ": str", "")
    result = str_replace(result, ": float", "")
    result = str_replace(result, ": bool", "")
    result = str_replace(result, ": list", "")
    result = str_replace(result, ": dict", "")
    result = str_replace(result, ": List", "")
    result = str_replace(result, ": Dict", "")
    result = str_replace(result, ": Optional", "")
    result = str_replace(result, ": Any", "")
    result = str_replace(result, ": Tuple", "")
    result = str_replace(result, ": torch.Tensor", "")
    result = str_replace(result, ": Tensor", "")
    result = str_replace(result, ": nn.Module", "")
    result = str_replace(result, "-> None", "")
    result = str_replace(result, "-> int", "-> Int")
    result = str_replace(result, "-> str", "-> String")
    result = str_replace(result, "-> float", "-> Float")
    result = str_replace(result, "-> bool", "-> Bool")
    result = str_replace(result, "-> list", "-> Array")
    result = str_replace(result, "-> torch.Tensor", "-> Tensor")
    result = str_replace(result, "-> Tensor", "-> Tensor")
    return result
}

// ── Pass 1: Syntax Translation ──────────────────────────

fn pass1_translate_line(line: String, prev_indent: Int, next_indent: Int) -> String {
    let trimmed = str_trim(line)
    let indent = count_leading_spaces(line)

    // Skip empty lines
    if is_blank(line) { return "" }

    // Comments: # → //
    if is_comment(line) {
        let comment_text = str_trim(trimmed)
        let comment_body = str_slice(comment_text, 1, str_len(comment_text))
        return str_repeat(" ", indent) + "//" + comment_body
    }

    // Skip decorators
    if is_decorator(line) {
        return "// " + trimmed
    }

    // Skip docstrings
    if is_docstring_marker(line) {
        return "// " + str_replace(str_replace(trimmed, "\"\"\"", ""), "'''", "")
    }

    // Import statements → commented out
    if starts_with(trimmed, "import ") {
        return "// " + trimmed
    }
    if starts_with(trimmed, "from ") {
        return "// " + trimmed
    }

    let result = trimmed

    // if __name__ == "__main__": → fn main() {
    if str_contains(trimmed, "__name__") {
        if str_contains(trimmed, "__main__") {
            return "fn main() \{"
        }
    }

    // def __init__ → fn new (constructor convention)
    if starts_with(trimmed, "def __init__") {
        result = str_replace(result, "def __init__", "fn new")
        result = str_replace(result, "self, ", "")
        result = str_replace(result, "self,", "")
        result = str_replace(result, "self)", ")")
        result = str_replace(result, "(self)", "()")
        result = remove_type_hints(result)
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        result = str_trim(result) + " \{"
        return str_repeat(" ", indent) + result
    }

    // def __repr__ / __str__ → fn to_string
    if starts_with(trimmed, "def __repr__") {
        result = str_replace(result, "def __repr__", "fn to_string")
        result = str_replace(result, "self)", ")")
        result = str_replace(result, "(self)", "()")
        result = remove_type_hints(result)
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        result = str_trim(result) + " \{"
        return str_repeat(" ", indent) + result
    }
    if starts_with(trimmed, "def __str__") {
        result = str_replace(result, "def __str__", "fn to_string")
        result = str_replace(result, "self)", ")")
        result = str_replace(result, "(self)", "()")
        result = remove_type_hints(result)
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        result = str_trim(result) + " \{"
        return str_repeat(" ", indent) + result
    }

    // def → fn + braces
    if starts_with(trimmed, "def ") {
        result = str_replace(result, "def ", "fn ")
        result = str_replace(result, "self, ", "")
        result = str_replace(result, "self,", "")
        result = str_replace(result, "self)", ")")
        result = str_replace(result, "(self)", "()")
        result = remove_type_hints(result)
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        result = str_trim(result) + " \{"
        return str_repeat(" ", indent) + result
    }

    // class Foo(nn.Module): → struct Foo {
    if starts_with(trimmed, "class ") {
        result = str_replace(result, "class ", "struct ")
        let paren_pos = str_find(result, "(")
        if paren_pos > 0 {
            result = str_slice(result, 0, paren_pos)
        }
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        result = str_trim(result) + " \{"
        return str_repeat(" ", indent) + result
    }

    // if/elif/else/while/for with colon → add brace
    if starts_with(trimmed, "if ") {
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        return str_repeat(" ", indent) + result + " \{"
    }
    if starts_with(trimmed, "elif ") {
        result = str_replace(result, "elif ", "\} else if ")
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        return str_repeat(" ", indent) + result + " \{"
    }
    if str_eq(trimmed, "else:") {
        return str_repeat(" ", indent) + "\} else \{"
    }
    if starts_with(trimmed, "while ") {
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        return str_repeat(" ", indent) + result + " \{"
    }
    if starts_with(trimmed, "for ") {
        result = str_replace(result, "range(", "0..")
        result = str_replace(result, ")", "")
        if str_ends_with(result, ":") {
            result = str_slice(result, 0, str_len(result) - 1)
        }
        return str_repeat(" ", indent) + result + " \{"
    }

    // return
    if starts_with(trimmed, "return ") {
        return str_repeat(" ", indent) + result
    }
    if str_eq(trimmed, "return") {
        return str_repeat(" ", indent) + "return"
    }

    // with torch.no_grad(): → // inference mode
    if starts_with(trimmed, "with ") {
        if str_contains(trimmed, "no_grad") {
            return str_repeat(" ", indent) + "// inference mode (no grad tracking)"
        }
        return str_repeat(" ", indent) + "// " + trimmed
    }

    // pass → // (empty block)
    if str_eq(trimmed, "pass") {
        return str_repeat(" ", indent) + "// pass"
    }

    // self.x → x
    if str_contains(result, "self.") {
        result = str_replace(result, "self.", "")
    }

    // super().__init__() → // (parent constructor)
    if str_contains(result, "super()") {
        return str_repeat(" ", indent) + "// " + trimmed
    }

    // print() → println()
    if starts_with(trimmed, "print(") {
        result = str_replace(result, "print(", "println(")
        result = str_replace(result, "f\"", "\"")
        result = str_replace(result, "f'", "'")
    }

    // True/False/None → true/false/void
    result = str_replace(result, "True", "true")
    result = str_replace(result, "False", "false")
    result = str_replace(result, "None", "void")

    // [] empty list → array_new()
    result = str_replace(result, "= []", "= array_new()")

    // Assignment: add let for simple variable assignments
    if str_contains(result, " = ") {
        if str_contains(result, "==") == false {
            if starts_with(result, "let ") == false {
                let eq_pos = str_find(result, " = ")
                if eq_pos > 0 {
                    let lhs = str_slice(result, 0, eq_pos)
                    if str_contains(lhs, ".") == false {
                        if str_contains(lhs, "[") == false {
                            result = "let " + result
                        }
                    }
                }
            }
        }
    }

    return str_repeat(" ", indent) + result
}

// Sort mappings by pattern length (longest first) to avoid partial matches
fn sort_mappings_by_length(maps: Array) -> Array {
    // Simple insertion sort — longest pattern first
    let sorted = maps
    let n = array_len(sorted)
    let i = 1
    while i < n {
        let j = i
        while j > 0 {
            let cur = array_get(sorted, j)
            let prev = array_get(sorted, j - 1)
            let cur_parts = str_split(cur, "|")
            let prev_parts = str_split(prev, "|")
            let cur_len = str_len(array_get(cur_parts, 0))
            let prev_len = str_len(array_get(prev_parts, 0))
            if cur_len > prev_len {
                // Swap: longer pattern should come first
                sorted = array_set(sorted, j, prev)
                sorted = array_set(sorted, j - 1, cur)
                j = j - 1
            } else {
                j = 0
            }
        }
        i = i + 1
    }
    return sorted
}

// ── Pass 2: API Mapping ──────────────────────────────────

fn pass2_apply_mappings(line: String, all_maps: Array) -> String {
    // Don't apply mappings to comment lines
    if starts_with(str_trim(line), "//") { return line }

    let result = line
    let i = 0
    let num_maps = array_len(all_maps)

    while i < num_maps {
        let entry = array_get(all_maps, i)
        let parts = str_split(entry, "|")
        let py_pattern = array_get(parts, 0)
        let sy_replacement = array_get(parts, 1)

        if str_contains(result, py_pattern) {
            result = str_replace(result, py_pattern, sy_replacement)
        }

        i = i + 1
    }

    // Additional pattern replacements not in mapping table
    result = str_replace(result, ".item()", "")
    result = str_replace(result, ".detach()", "")
    result = str_replace(result, ".cpu()", "")
    result = str_replace(result, ".numpy()", "")
    result = str_replace(result, ".cuda()", "")
    result = str_replace(result, ".to(device)", "")
    result = str_replace(result, ".float()", "")
    result = str_replace(result, ".long()", "")
    result = str_replace(result, ".append(", " = array_push(")
    result = str_replace(result, "len(", "array_len(")
    result = str_replace(result, "**2", "* 2")
    result = str_replace(result, "** 2", "* 2")
    result = str_replace(result, ".view(", ".reshape(")
    result = str_replace(result, ".size()", ".shape")
    result = str_replace(result, ".dim()", ".rank")
    result = str_replace(result, ".numel()", ".numel")
    result = str_replace(result, ", dim=-1", "")
    result = str_replace(result, ", dim=0", "")
    result = str_replace(result, ", dim=1", "")
    result = str_replace(result, ".mean()", ".mean")
    result = str_replace(result, ".sum()", ".sum")
    result = str_replace(result, ".astype(np.float32)", "")
    result = str_replace(result, ".state_dict()", "")

    return result
}

// ── Pass 3: Polish ───────────────────────────────────────

fn pass3_polish(lines: Array) -> Array {
    // Remove consecutive blank lines and trailing whitespace
    let result = array_new()
    let prev_blank = false
    let i = 0
    let num = array_len(lines)

    while i < num {
        let line = array_get(lines, i)
        let cur_blank = is_blank(line)

        if cur_blank {
            if prev_blank == false {
                result = array_push(result, "")
            }
            // Skip consecutive blanks
        } else {
            result = array_push(result, line)
        }

        prev_blank = cur_blank
        i = i + 1
    }

    return result
}

// ── Main Converter ───────────────────────────────────────

fn convert_python(source: String) -> String {
    let all_maps = sort_mappings_by_length(get_all_mappings())
    let lines = str_lines(source)
    let num_lines = array_len(lines)
    let output = array_new()

    // Add Scyla header
    output = array_push(output, "// Converted from Python by Scyla")
    output = array_push(output, "")

    // Track indent for brace insertion
    let prev_indent = 0
    let brace_stack = array_new()
    let in_docstring = false

    let i = 0
    while i < num_lines {
        let line = array_get(lines, i)
        let trimmed = str_trim(line)
        let cur_indent = count_leading_spaces(line)

        // Handle multi-line docstrings
        if in_docstring {
            if str_contains(trimmed, "\"\"\"") {
                in_docstring = false
            }
            if str_contains(trimmed, "'''") {
                in_docstring = false
            }
            // Skip docstring content
            i = i + 1
            prev_indent = cur_indent
            // continue equivalent
        } else {
            // Check if this starts a docstring
            if is_docstring_marker(line) {
                // Check if it's a single-line docstring (opens and closes on same line)
                let triple_dq = str_find(trimmed, "\"\"\"")
                if triple_dq >= 0 {
                    let rest = str_slice(trimmed, triple_dq + 3, str_len(trimmed))
                    if str_contains(rest, "\"\"\"") == false {
                        in_docstring = true
                    }
                }
                let triple_sq = str_find(trimmed, "'''")
                if triple_sq >= 0 {
                    let rest = str_slice(trimmed, triple_sq + 3, str_len(trimmed))
                    if str_contains(rest, "'''") == false {
                        in_docstring = true
                    }
                }
                i = i + 1
                prev_indent = cur_indent
            } else {
                // Determine next non-blank line's indent
                let next_indent = 0
                let j = i + 1
                while j < num_lines {
                    let next_line = array_get(lines, j)
                    if is_blank(next_line) == false {
                        next_indent = count_leading_spaces(next_line)
                        j = num_lines
                    } else {
                        j = j + 1
                    }
                }

                // Close braces when dedenting
                if is_blank(line) == false {
                    let stack_len = array_len(brace_stack)
                    while stack_len > 0 {
                        let top_indent_str = array_get(brace_stack, stack_len - 1)
                        let top_indent = str_len(top_indent_str)
                        if cur_indent < top_indent {
                            brace_stack = array_slice(brace_stack, 0, stack_len - 1)
                            let close_indent = top_indent - 4
                            if close_indent < 0 {
                                close_indent = 0
                            }
                            output = array_push(output, str_repeat(" ", close_indent) + "\}")
                            stack_len = stack_len - 1
                        } else {
                            stack_len = 0
                        }
                    }
                }

                // Pass 1: syntax translation
                let converted = pass1_translate_line(line, prev_indent, next_indent)

                // Pass 2: API mapping
                converted = pass2_apply_mappings(converted, all_maps)

                // Track braces we opened
                if str_ends_with(str_trim(converted), "\{") {
                    brace_stack = array_push(brace_stack, str_repeat(" ", cur_indent + 4))
                }

                if is_blank(converted) == false {
                    output = array_push(output, converted)
                } else {
                    if is_blank(line) {
                        output = array_push(output, "")
                    }
                }

                if is_blank(line) == false {
                    prev_indent = cur_indent
                }
                i = i + 1
            }
        }
    }

    // Close any remaining open braces
    let remaining = array_len(brace_stack)
    while remaining > 0 {
        let top_str = array_get(brace_stack, remaining - 1)
        let top_indent = str_len(top_str)
        let close_indent = top_indent - 4
        if close_indent < 0 {
            close_indent = 0
        }
        output = array_push(output, str_repeat(" ", close_indent) + "\}")
        remaining = remaining - 1
    }

    // Pass 3: Polish — remove consecutive blank lines
    output = pass3_polish(output)

    return str_join(output, "\n")
}

// ── Entry Point ──────────────────────────────────────────

fn main() {
    println("=== Scyla Language Converter ===")
    println("Python/PyTorch -> Scyla")
    println("")

    let input_path = input("Enter Python file path: ")

    let source = read_file(input_path)
    if starts_with(source, "error:") {
        println("Could not read file: " + source)
        return
    }

    println("Converting " + input_path + "...")
    println("")

    let result = convert_python(source)

    // Output to .sy file
    let output_path = str_replace(input_path, ".py", ".sy")
    write_file(output_path, result)

    println(result)
    println("")
    println("Saved to: " + output_path)

    // Show stats
    let py_lines = array_len(str_lines(source))
    let sy_lines = array_len(str_lines(result))
    println("Python: " + int_to_str(py_lines) + " lines -> Scyla: " + int_to_str(sy_lines) + " lines")
}
