package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

// Memory is a map representing the memory cells
type Memory map[int]interface{}

// SVM is the Sapbot VM interpreter
type SVM struct {
	pc   int
	mem  Memory
	code map[int]string
}

// NewSVM creates a new SVM instance
func NewSVM() *SVM {
	return &SVM{
		pc:   0,
		mem:  make(Memory),
		code: make(map[int]string),
	}
}

// LoadProgram loads a program from a JSON file
func (svm *SVM) LoadProgram(filename string) error {
	file, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	// Try to unmarshal as a map (object)
	var rawMap map[string]interface{}
	if err := json.Unmarshal(file, &rawMap); err == nil {
		// Parse the JSON object into the code map
		for key, value := range rawMap {
			lineNum, err := strconv.Atoi(key)
			if err != nil {
				return fmt.Errorf("invalid line number: %s", key)
			}
			svm.code[lineNum] = value.(string)
		}
	} else {
		// Try to unmarshal as a slice (array)
		var rawSlice []string
		if err := json.Unmarshal(file, &rawSlice); err != nil {
			return err
		}
		// For arrays, assume each line is a command and assign sequential line numbers
		for i, cmd := range rawSlice {
			svm.code[i+1] = cmd
		}
	}

	return nil
}

// ParseExpr parses a value expression (e.g., V3, M3, Ttext, F0.5)
func (svm *SVM) ParseExpr(val string) interface{} {
	if len(val) == 0 {
		return 0
	}

	switch val[0] {
	case 'V':
		// Integer value
		intVal, _ := strconv.Atoi(val[1:])
		return intVal
	case 'M':
		// Memory cell reference
		cell, _ := strconv.Atoi(val[1:])
		return svm.mem[cell]
	case 'T':
		// Text value
		return val[1:]
	case 'F':
		// Float value
		floatVal, _ := strconv.ParseFloat(val[1:], 64)
		return floatVal
	default:
		return 0
	}
}

// Run executes the loaded program
func (svm *SVM) Run() {
	rand.Seed(time.Now().UnixNano())

	for svm.pc < 65000 {
		line, exists := svm.code[svm.pc]
		if !exists {
			svm.pc++
			continue
		}

		parts := strings.Split(line, ";")
		if len(parts) == 0 {
			svm.pc++
			continue
		}

		instruction := parts[0]
		args := parts[1:]

		switch instruction {
		case "LOAD":
			if len(args) != 2 {
				fmt.Println("Invalid number of operands for LOAD")
			} else {
				cell := svm.ParseExpr(args[0]).(int)
				value := svm.ParseExpr(args[1])
				svm.mem[cell] = value
			}
		case "NOP":
			// Do nothing
		case "GOTO":
			if len(args) != 1 {
				fmt.Println("WHERE TO JUMP???")
			} else {
				svm.pc = svm.ParseExpr(args[0]).(int)
				continue
			}
		case "DEBUG":
			if len(args) != 1 {
				fmt.Println("What to print, bro?")
			} else {
				fmt.Println(svm.ParseExpr(args[0]))
			}
		case "NOT":
			if len(args) != 2 {
				fmt.Println("Duh.")
			} else {
				cell := svm.ParseExpr(args[1]).(int)
				value := svm.ParseExpr(args[0])
				if boolVal, ok := value.(int); ok {
					svm.mem[cell] = 0
					if boolVal != 0 {
						svm.mem[cell] = 1
					}
				} else {
					svm.mem[cell] = 0
				}
			}
		case "ADD":
			if len(args) != 3 {
				fmt.Println("Format: ADD;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var result interface{}
				switch a := a.(type) {
				case int:
					bVal := b.(int)
					result = a + bVal
				case float64:
					bVal := b.(float64)
					result = a + bVal
				default:
					result = 0
				}
				svm.mem[to] = result
			}
		case "SUB":
			if len(args) != 3 {
				fmt.Println("Format: SUB;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var result interface{}
				switch a := a.(type) {
				case int:
					bVal := b.(int)
					result = a - bVal
				case float64:
					bVal := b.(float64)
					result = a - bVal
				default:
					result = 0
				}
				svm.mem[to] = result
			}
		case "DIV":
			if len(args) != 3 {
				fmt.Println("Format: DIV;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var result interface{}
				switch a := a.(type) {
				case int:
					bVal := b.(int)
					if bVal == 0 {
						result = 0
					} else {
						result = a / bVal
					}
				case float64:
					bVal := b.(float64)
					if bVal == 0 {
						result = 0.0
					} else {
						result = a / bVal
					}
				default:
					result = 0
				}
				svm.mem[to] = result
			}
		case "MUL":
			if len(args) != 3 {
				fmt.Println("Format: MUL;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var result interface{}
				switch a := a.(type) {
				case int:
					bVal := b.(int)
					result = a * bVal
				case float64:
					bVal := b.(float64)
					result = a * bVal
				default:
					result = 0
				}
				svm.mem[to] = result
			}
		case "MOD":
			if len(args) != 3 {
				fmt.Println("Format: MOD;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0]).(int)
				b := svm.ParseExpr(args[1]).(int)
				to := svm.ParseExpr(args[2]).(int)

				if b == 0 {
					svm.mem[to] = 0
				} else {
					svm.mem[to] = a % b
				}
			}
		case "ALB":
			if len(args) != 3 {
				fmt.Println("Format: ALB;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var aVal, bVal int
				switch a := a.(type) {
				case int:
					aVal = a
				case float64:
					aVal = int(a)
				default:
					aVal = 0
				}

				switch b := b.(type) {
				case int:
					bVal = b
				case float64:
					bVal = int(b)
				default:
					bVal = 0
				}

				if aVal < bVal {
					svm.pc = to
					continue
				}
			}
		case "AQB":
			if len(args) != 3 {
				fmt.Println("Format: AQB;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var aVal, bVal int
				switch a := a.(type) {
				case int:
					aVal = a
				case float64:
					aVal = int(a)
				default:
					aVal = 0
				}

				switch b := b.(type) {
				case int:
					bVal = b
				case float64:
					bVal = int(b)
				default:
					bVal = 0
				}

				if aVal == bVal {
					svm.pc = to
					continue
				}
			}
		case "ABB":
			if len(args) != 3 {
				fmt.Println("Format: ABB;A;B;TO")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var aVal, bVal int
				switch a := a.(type) {
				case int:
					aVal = a
				case float64:
					aVal = int(a)
				default:
					aVal = 0
				}

				switch b := b.(type) {
				case int:
					bVal = b
				case float64:
					bVal = int(b)
				default:
					bVal = 0
				}

				if aVal > bVal {
					svm.pc = to
					continue
				}
			}
		case "AND":
			if len(args) != 3 {
				fmt.Println("Duh.")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var aVal, bVal int
				switch a := a.(type) {
				case int:
					aVal = a
				case float64:
					aVal = int(a)
				default:
					aVal = 0
				}

				switch b := b.(type) {
				case int:
					bVal = b
				case float64:
					bVal = int(b)
				default:
					bVal = 0
				}

				if aVal != 0 && bVal != 0 {
					svm.mem[to] = 1
				} else {
					svm.mem[to] = 0
				}
			}
		case "OR":
			if len(args) != 3 {
				fmt.Println("Duh.")
			} else {
				a := svm.ParseExpr(args[0])
				b := svm.ParseExpr(args[1])
				to := svm.ParseExpr(args[2]).(int)

				var aVal, bVal int
				switch a := a.(type) {
				case int:
					aVal = a
				case float64:
					aVal = int(a)
				default:
					aVal = 0
				}

				switch b := b.(type) {
				case int:
					bVal = b
				case float64:
					bVal = int(b)
				default:
					bVal = 0
				}

				if aVal != 0 || bVal != 0 {
					svm.mem[to] = 1
				} else {
					svm.mem[to] = 0
				}
			}
		case "DEBINP":
			if len(args) != 1 {
				fmt.Println("What do you wanted?")
			} else {
				cell := svm.ParseExpr(args[0]).(int)
				var input string
				fmt.Print("Input: ")
				fmt.Scanln(&input)
				svm.mem[cell] = input
			}
		case "PARSEINT":
			if len(args) != 2 {
				fmt.Println("Format: PARSEINT;IN;OUT")
			} else {
				in := svm.ParseExpr(args[0]).(string)
				out := svm.ParseExpr(args[1]).(int)

				intVal, err := strconv.Atoi(in)
				if err != nil {
					svm.mem[out] = 0
				} else {
					svm.mem[out] = intVal
				}
			}
		case "RND":
			if len(args) != 2 {
				fmt.Println("Format: RND;MAX;TO")
			} else {
				max := svm.ParseExpr(args[0]).(int)
				to := svm.ParseExpr(args[1]).(int)
				svm.mem[to] = rand.Intn(max)
			}
		default:
			fmt.Printf("Unknown operand %s\n", instruction)
		}

		svm.pc++
	}
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run svm.go <program.svm>")
		return
	}

	svm := NewSVM()
	if err := svm.LoadProgram(os.Args[1]); err != nil {
		fmt.Printf("Error loading program: %v\n", err)
		return
	}

	svm.Run()
}
