package javascript

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/xeth"
	"github.com/obscuren/otto"
)

var jsrelogger = logger.NewLogger("JSRE")

type JSRE struct {
	Vm   *otto.Otto
	xeth *xeth.XEth

	objectCb map[string][]otto.Value
}

func (jsre *JSRE) LoadExtFile(path string) {
	result, err := ioutil.ReadFile(path)
	if err == nil {
		jsre.Vm.Run(result)
	} else {
		jsrelogger.Infoln("Could not load file:", path)
	}
}

func (jsre *JSRE) LoadIntFile(file string) {
	assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
	jsre.LoadExtFile(path.Join(assetPath, file))
}

func NewJSRE(xeth *xeth.XEth) *JSRE {
	re := &JSRE{
		otto.New(),
		xeth,
		make(map[string][]otto.Value),
	}

	// Init the JS lib
	re.Vm.Run(jsLib)

	// Load extra javascript files
	re.LoadIntFile("bignumber.min.js")

	re.Bind("eth", &JSEthereum{re.xeth, re.Vm})

	re.initStdFuncs()

	jsrelogger.Infoln("started")

	return re
}

func (self *JSRE) Bind(name string, v interface{}) {
	self.Vm.Set(name, v)
}

func (self *JSRE) Run(code string) (otto.Value, error) {
	return self.Vm.Run(code)
}

func (self *JSRE) initStdFuncs() {
	t, _ := self.Vm.Get("eth")
	eth := t.Object()
	eth.Set("require", self.require)
}

func (self *JSRE) Require(file string) error {
	if len(filepath.Ext(file)) == 0 {
		file += ".js"
	}

	fh, err := os.Open(file)
	if err != nil {
		return err
	}

	content, _ := ioutil.ReadAll(fh)
	self.Run("exports = {};(function() {" + string(content) + "})();")

	return nil
}

func (self *JSRE) require(call otto.FunctionCall) otto.Value {
	file, err := call.Argument(0).ToString()
	if err != nil {
		return otto.UndefinedValue()
	}
	if err := self.Require(file); err != nil {
		fmt.Println("err:", err)
		return otto.UndefinedValue()
	}

	t, _ := self.Vm.Get("exports")

	return t
}