module Language.Z (runProgram, Instruction(..)) where

import Control.Monad.ST (ST)
import Data.Array.ST (runSTArray, newArray, readArray, writeArray, STArray)

data Instruction = Z Int | I Int | J Int Int Int deriving (Show, Read)

maxMemoryAddress = 10
programCounterAddress = maxMemoryAddress

buildMemory :: ST s (STArray s Int Int)
buildMemory = newArray (0, maxMemoryAddress) 0 

readPC memory = readArray memory programCounterAddress 

writePC memory = writeArray memory programCounterAddress 

incPC memory = do
    pc <- readPC memory
    writePC memory (pc + 1)

processInstruction memory (Z i) = do
    writeArray memory i 0
    incPC memory
processInstruction memory (I i) = do
    a <- readArray memory i
    writeArray memory i (a + 1)
    incPC memory
processInstruction memory (J i j npc) = do
    a <- readArray memory i
    b <- readArray memory j 
    if a /= b 
        then writePC memory npc 
        else incPC memory

processInstructions memory [] = do
    return ()

processInstructions memory instructions = do
    pc <- readPC memory
    if pc >= (length instructions)
        then return ()
        else do
            processInstruction memory (instructions !! pc)
            processInstructions memory instructions

runProgram program = runSTArray $ do
    memory <- buildMemory
    processInstructions memory program
    return memory
