package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"c65gm/internal/commands"
	"c65gm/internal/compiler"
	"c65gm/internal/preproc"
)

// c65gm - A 6502 Cross-Compiler for the ACME Cross-Assembler
// Copyright (C) 1999, 2025 Mattias Hansson
// Distributed under GPL.

func main() {
	fmt.Println("c65gm - A 6502 Cross-Compiler for the ACME Cross-Assembler.")
	fmt.Println("Copyright (C) 1999, 2025 Mattias Hansson. v1.0.0")
	fmt.Println("Distributed under GPL.")
	fmt.Println()

	inFile := flag.String("in", "", "input source file (required)")
	outFile := flag.String("out", "", "output assembly file (required)")
	flag.Parse()

	if *inFile == "" || *outFile == "" {
		_, _ = fmt.Fprintln(os.Stderr, "Error: -in and -out are required")
		flag.Usage()
		os.Exit(1)
	}

	if err := run(*inFile, *outFile); err != nil {
		if _, ok := err.(preproc.HaltError); ok {
			fmt.Println("Halted by #HALT directive")
			os.Exit(2)
		}
		_, _ = fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}

	fmt.Println("Compilation successful.")
}

func run(inFile, outFile string) error {
	// Preprocess
	lines, pragma, err := preproc.PreProcess(inFile)
	if err != nil {
		return fmt.Errorf("preprocessing failed: %w", err)
	}

	fmt.Printf("Preprocessed %d lines\n", len(lines))

	// Create compiler and register commands
	comp := compiler.NewCompiler(pragma)
	registerCommands(comp)

	// Compile
	asmLines, err := comp.Compile(lines)
	if err != nil {
		return fmt.Errorf("compilation failed: %w", err)
	}

	fmt.Printf("Generated %d lines of assembly\n", len(asmLines))

	// Write output
	if err := writeOutput(outFile, asmLines); err != nil {
		return fmt.Errorf("failed to write output: %w", err)
	}

	return nil
}

func registerCommands(comp *compiler.Compiler) {
	// Register all command handlers here
	// This is the single place where all commands are wired up

	comp.Registry().Register(&commands.ByteCommand{})
	comp.Registry().Register(&commands.WordCommand{})
	comp.Registry().Register(&commands.AddCommand{})
	comp.Registry().Register(&commands.AndCommand{})
	comp.Registry().Register(&commands.OrCommand{})
	comp.Registry().Register(&commands.XorCommand{})
	comp.Registry().Register(&commands.SubtractCommand{})
	comp.Registry().Register(&commands.LetCommand{})
	comp.Registry().Register(&commands.IfCommand{})
	comp.Registry().Register(&commands.ElseCommand{})
	comp.Registry().Register(&commands.EndIfCommand{})
	comp.Registry().Register(&commands.WhileCommand{})
	comp.Registry().Register(&commands.BreakCommand{})
	comp.Registry().Register(&commands.WendCommand{})
	comp.Registry().Register(&commands.FuncCommand{})
	comp.Registry().Register(commands.NewCallCommand(comp.Context().FunctionHandler))
	comp.Registry().Register(&commands.FendCommand{})
	comp.Registry().Register(&commands.IncrCommand{})
	comp.Registry().Register(&commands.DecrCommand{})
	comp.Registry().Register(&commands.GotoCommand{})
	comp.Registry().Register(&commands.LabelCommand{})
	comp.Registry().Register(&commands.OriginCommand{})
	comp.Registry().Register(&commands.PointerCommand{})
	comp.Registry().Register(&commands.PeekCommand{})
	comp.Registry().Register(&commands.PeekWCommand{})
	comp.Registry().Register(&commands.PokeCommand{})
	comp.Registry().Register(&commands.PokeWCommand{})
	comp.Registry().Register(&commands.SubEndCommand{})
	comp.Registry().Register(&commands.GosubCommand{})
	comp.Registry().Register(&commands.ForCommand{})
	comp.Registry().Register(&commands.NextCommand{})
}

func writeOutput(filename string, lines []string) error {
	return os.WriteFile(filename, []byte(strings.Join(lines, "\n")+"\n"), 0644)
}
