// Copyright 2010 The postscript-go Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff

package postscript

import (
	"os"
	"log"
	"strconv"
	"io"
	"draw2d.googlecode.com/hg/draw2d"
)


type Interpreter struct {
	valueStack      ValueStack
	dictionaryStack DictionaryStack
	gc              draw2d.GraphicContext
}

type Value interface{}

type ValueStack []Value

type Dictionary map[string]Value

type DictionaryStack []Dictionary

type Operator interface {
	Execute(interpreter *Interpreter)
}

func NewInterpreter(gc draw2d.GraphicContext) *Interpreter {
	interpreter := new(Interpreter)
	interpreter.valueStack = make([]Value, 0, 100)
	interpreter.dictionaryStack = make([]Dictionary, 2, 10)
	interpreter.dictionaryStack[0] = NewDictionary(100) // System dictionary
	interpreter.dictionaryStack[1] = NewDictionary(100) // user dictionary
	initSystemOperators(interpreter)
	interpreter.gc = gc
	return interpreter
}

func NewDictionary(prealloc int) Dictionary {
	return make(Dictionary, prealloc)
}

func (interpreter *Interpreter) GetGraphicContext() draw2d.GraphicContext {
	return interpreter.gc
}
func (interpreter *Interpreter) Execute(reader io.Reader) {
	var scanner Scanner
	scanner.Init(reader)
	token := scanner.Scan()
	for token != EOF {
		interpreter.scan(&scanner, token)
		token = scanner.Scan()
	}
}

func (interpreter *Interpreter) ExecuteFile(filePath string) os.Error {
	src, err := os.Open(filePath)
	if src == nil {
		log.Printf("can't open file; err=%s\n", err.String())
		return err
	}
	defer src.Close()
	interpreter.Execute(src)
	return nil
}
func (interpreter *Interpreter) computeReference(ref string) {
	value, _ := interpreter.FindValueInDictionaries(ref)
	if value == nil {
		log.Printf("Unknown def: %s\n", ref)
	} else {
		operator, isOperator := value.(Operator)
		if isOperator {
			operator.Execute(interpreter)
		} else {
			interpreter.Push(value)
		}
	}
}
func (interpreter *Interpreter) scan(scanner *Scanner, token int) {
	if token == Ident {
		switch scanner.TokenText() {
		case "true":
			interpreter.Push(true)
		case "false":
			interpreter.Push(false)
		case "null":
			interpreter.Push(nil)
		default:
			interpreter.computeReference(scanner.TokenText())
		}
	} else if token == '/' {
		scanner.Scan()
		interpreter.Push("/" + scanner.TokenText())
	} else if token == '[' {
		interpreter.Push(interpreter.scanArray(scanner))
	} else if token == '{' {
		// procedure
		interpreter.Push(interpreter.scanProcedure(scanner))
	} else if token == Float || token == Int {
		f, err := strconv.Atof64(scanner.TokenText())
		if err != nil {
			log.Printf("Float expected: %s\n", scanner.TokenText())
			interpreter.Push(scanner.TokenText())
		} else {
			interpreter.Push(f)
		}
	} else {
		interpreter.Push(scanner.TokenText())
	}
}

func (interpreter *Interpreter) scanArray(scanner *Scanner) []Value {
	array := make([]Value, 0, 10)
	token := scanner.Scan()
	for token != EOF && token != ']' {
		if token == Ident {
			var v Value = scanner.TokenText()
			switch scanner.TokenText() {
			case "true":
				v = true
			case "false":
				v = false
			case "null":
				v = nil
			}
			array = append(array, v)
		} else {
			interpreter.scan(scanner, token)
			array = append(array, interpreter.Pop())
		}
		token = scanner.Scan()
	}
	return array
}

func (interpreter *Interpreter) scanProcedure(scanner *Scanner) *ProcedureDefinition {
	proceduredef := NewProcedureDefinition()
	token := scanner.Scan()
	for token != EOF && token != '}' {
		if token == Ident {
			var v Value = scanner.TokenText()
			switch scanner.TokenText() {
			case "true":
				v = true
			case "false":
				v = false
			case "null":
				v = nil
			}
			proceduredef.Add(v)
		} else {
			interpreter.scan(scanner, token)
			proceduredef.Add(interpreter.Pop())
		}
		token = scanner.Scan()
	}
	return proceduredef
}

//Dictionary Operation

func (interpreter *Interpreter) PushDictionary(dictionary Dictionary) {
	interpreter.dictionaryStack = append(interpreter.dictionaryStack, dictionary)
}

func (interpreter *Interpreter) PopDictionary() Dictionary {
	stackPointer := len(interpreter.dictionaryStack) - 1
	dictionary := interpreter.dictionaryStack[stackPointer]
	interpreter.dictionaryStack = interpreter.dictionaryStack[0:stackPointer]
	return dictionary
}

func (interpreter *Interpreter) PeekDictionary() Dictionary {
	stackPointer := len(interpreter.dictionaryStack) - 1
	return interpreter.dictionaryStack[stackPointer]
}
func (interpreter *Interpreter) ClearDictionaries() {
	interpreter.dictionaryStack = interpreter.dictionaryStack[:2]
}

func (interpreter *Interpreter) DictionaryStackSize() int {
	return len(interpreter.dictionaryStack)
}

func (interpreter *Interpreter) FindValue(name string) Value {
	return interpreter.PeekDictionary()[name]
}

func (interpreter *Interpreter) FindValueInDictionaries(name string) (Value, Dictionary) {
	for i := len(interpreter.dictionaryStack) - 1; i >= 0; i-- {
		value := interpreter.dictionaryStack[i][name]
		if value != nil {
			return value, interpreter.dictionaryStack[i]
		}
	}
	return nil, nil
}

func (interpreter *Interpreter) UserDictionary() Dictionary {
	return interpreter.dictionaryStack[0]
}

func (interpreter *Interpreter) SystemDictionary() Dictionary {
	return interpreter.dictionaryStack[0]
}

func (interpreter *Interpreter) Define(name string, value Value) {
	interpreter.PeekDictionary()[name] = value
}

func (interpreter *Interpreter) SystemDefine(name string, value Value) {
	interpreter.dictionaryStack[0][name] = value
}


//Operand Operation

func (interpreter *Interpreter) Push(operand Value) {
	//log.Printf("Push operand: %v\n", operand)
	interpreter.valueStack = append(interpreter.valueStack, operand)
}

func (interpreter *Interpreter) Pop() Value {
	valueStackPointer := len(interpreter.valueStack) - 1
	operand := interpreter.valueStack[valueStackPointer]
	interpreter.valueStack = interpreter.valueStack[0:valueStackPointer]
	//log.Printf("Pop operand: %v\n", operand)
	return operand
}

func (interpreter *Interpreter) PopValues(n int) []Value {
	valueStackPointer := len(interpreter.valueStack) - 1
	operands := make([]Value, n)
	copy(operands, interpreter.valueStack[valueStackPointer-n+1:valueStackPointer+1])
	interpreter.valueStack = interpreter.valueStack[0 : valueStackPointer-n+1]
	return operands
}

func (interpreter *Interpreter) GetValues(n int) []Value {
	valueStackPointer := len(interpreter.valueStack) - 1
	operands := make([]Value, n)
	copy(operands, interpreter.valueStack[valueStackPointer-n+1:valueStackPointer+1])
	return operands
}

func (interpreter *Interpreter) Get(index int) Value {
	valueStackPointer := len(interpreter.valueStack) - 1
	return interpreter.valueStack[valueStackPointer-index]
}

func (interpreter *Interpreter) Peek() Value {
	valueStackPointer := len(interpreter.valueStack) - 1
	return interpreter.valueStack[valueStackPointer]
}

func (interpreter *Interpreter) OperandSize() int {
	return len(interpreter.valueStack)
}

func (interpreter *Interpreter) ClearOperands() {
	interpreter.valueStack = interpreter.valueStack[0:0]
}

// misc pop

func (interpreter *Interpreter) PopFloat() float64 {
	operand := interpreter.Pop()
	return operand.(float64)
}

func (interpreter *Interpreter) PopInt() int {
	f := interpreter.PopFloat()
	return int(f)
}

func (interpreter *Interpreter) PopOperator() Operator {
	operator := interpreter.Pop()
	return operator.(Operator)
}

func (interpreter *Interpreter) PopProcedureDefinition() *ProcedureDefinition {
	def := interpreter.Pop()
	return def.(*ProcedureDefinition)
}

func (interpreter *Interpreter) PopName() string {
	name := interpreter.Pop().(string)
	return name[1:]
}

func (interpreter *Interpreter) PopString() string {
	s := interpreter.Pop().(string)
	return s[1 : len(s)-1]
}

func (interpreter *Interpreter) PopBoolean() bool {
	s := interpreter.Pop()
	return s.(bool)
}

func (interpreter *Interpreter) PopArray() []Value {
	s := interpreter.Pop()
	return s.([]Value)
}
