package og

import (
	"fmt"
	"github.com/champii/og/lib/ast/walker"
	"github.com/champii/og/lib/common"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type OgCompiler struct {
	Config  *common.OgConfig
	Parser  *OgParser
	Preproc *OgPreproc
	Files   []*common.File
}

func (this *OgCompiler) Compile() error {
	for _, p := range this.Config.Paths {
		if err := filepath.Walk(p, this.walker); err != nil {
			fmt.Println("Error", err)
			return err
		}
	}
	if len(this.Files) == 0 {
		if this.Config.Run {
			return nil
		}
	}
	poolSize := this.Config.Workers
	if len(this.Files) < poolSize {
		poolSize = len(this.Files)
	}
	pool := NewPool(poolSize, len(this.Files), this.ParseFile)
	for _, file := range this.Files {
		pool.Queue(file)
	}
	if err := pool.Run(); err != nil {
		return err
	}
	if this.Config.Blocks {
		return nil
	}
	walker.NewDesugar().Run(this.Files)
	return this.outputFiles()
}
func (this *OgCompiler) outputFiles() error {
	for _, file := range this.Files {
		file.Output = file.Ast.Eval()
		if !this.Config.Dirty {
			if err := file.Format(); err != nil {
				return err
			}
		}
		if this.Config.Print || this.Config.Dirty || this.Config.Blocks {
			fmt.Println(file.Output)
		} else {
			file.Write()
		}
	}
	return nil
}
func (this *OgCompiler) walker(filePath string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if info.IsDir() {
		return nil
	}
	if path.Ext(filePath) != ".og" {
		return nil
	}
	if !this.mustCompile(filePath, info) {
		return nil
	}
	this.Files = append(this.Files, common.NewFile(filePath, this.getNewPath(filePath)))
	return nil
}
func (this OgCompiler) mustCompile(filePath string, info os.FileInfo) bool {
	newPath := this.getNewPath(filePath)
	stat, err := os.Stat(newPath)
	if err != nil {
		return true
	}
	if this.Config.Ast || this.Config.Print || this.Config.Dirty || this.Config.Blocks || this.Config.Force {
		return true
	}
	return info.ModTime().After(stat.ModTime())
}
func (this *OgCompiler) ParseFile(file *common.File) error {
	this.Preproc.Run(file)
	if this.Config.Blocks {
		fmt.Println(file.Output)
		return nil
	}
	var (
		err error
	)
	if !this.Config.Interpreter {
		err = this.Parser.Parse(file)
	} else {
		err = this.Parser.ParseInterpret(file)
	}
	if err != nil {
		return err
	}
	return nil
}
func (this OgCompiler) getNewPath(filePath string) string {
	if this.Config.OutPath != "./" {
		splited := strings.SplitN(filePath, "/", 2)
		filePath = splited[1]
	}
	return strings.Replace(path.Join(this.Config.OutPath, filePath), ".og", ".go", 1)
}
func NewOgCompiler(config *common.OgConfig) *OgCompiler {
	return &OgCompiler{
		Config:  config,
		Parser:  NewOgParser(config),
		Preproc: NewOgPreproc(),
		Files:   []*common.File{},
	}
}
