package ast

import (
	"bytes"
	"strings"

	"gitlab.com/olanguage/olang/token"
)

type Node interface {
	TokenLiteral() string
	String() string
	Debug() string
}

type Statement interface {
	Node
	statementNode()
}

type Expression interface {
	Node
	expressionNode()
}

type Program struct {
	Statements []Statement
}

type LetStatement struct {
	Token token.Token // the token.LET token
	Name  *Identifier
	Value Expression
}

type SignalExpression struct {
	Token token.Token // the token.SIGNAL token
	Name  *Identifier
	Value string
}

type Identifier struct {
	Token token.Token // the token.IDENT token
	Value string
}

type ReturnStatement struct {
	Token       token.Token // the `return` token
	ReturnValue Expression
}

type ExpressionStatement struct {
	Token      token.Token // the first token of the expression
	Expression Expression
}

type IntegerLiteral struct {
	Token token.Token
	Value int64
}

type FloatLiteral struct {
	Token token.Token
	Value float64
}

type PrefixExpression struct {
	Token    token.Token // The prefix token, e.g. !
	Operator string
	Right    Expression
}

type InfixExpression struct {
	Token    token.Token // The operator tokne, e.g. +
	Left     Expression
	Operator string
	Right    Expression
}

type Boolean struct {
	Token token.Token
	Value bool
}

type IfExpression struct {
	Token       token.Token // The 'if' token
	Condition   Expression
	Consequence *BlockStatement
	Alternative *BlockStatement
}

type LoopExpression struct {
	Token       token.Token // The 'if' token
	Condition   Expression
	Consequence *BlockStatement
}

type WhileExpression struct {
	Token       token.Token // The 'if' token
	Condition   Expression
	Consequence *BlockStatement
}

type BlockStatement struct {
	Token      token.Token // the { token
	Statements []Statement
}

type FunctionLiteral struct {
	Token      token.Token // The 'fn' token
	Parameters []*Identifier
	Body       *BlockStatement
}

type RouteLiteral struct {
	Token  token.Token
	Config Expression
}

type CallExpression struct {
	Token     token.Token // the '(' token
	Function  Expression  // Identifier or FunctionLiteral
	Arguments []Expression
}

type StringLiteral struct {
	Token token.Token
	Value string
}

type ArrayLiteral struct {
	Token    token.Token // the `[` token
	Elements []Expression
}

type IndexExpression struct {
	Token token.Token // The [ token
	Left  Expression
	Index Expression
}

type HashLiteral struct {
	Token token.Token // the `{` token
	Pairs map[Expression]Expression
}

type ForExpression struct {
	Token       token.Token // the `for` token
	LoopToken   token.Token // in or ;(semicolon)
	Init        Expression
	Key         Expression
	Value       Expression
	Condition   Expression
	Increment   Expression
	Consequence Expression
}

type ProcessExpression struct {
	Token      token.Token // The 'procces' token
	Definition Expression
	Process    Expression
	Path       Expression
	Params     Expression
}

type SocketExpression struct {
	Token      token.Token // The 'sock' token
	Definition string
	Host       string
	Port       string
	ConType    string
	Debugger   bool
}

type EnvExpression struct {
	Token token.Token // The 'procces' token
	Name  string
	Value string
}

type CustomLiteral struct {
	Token      token.Token
	Name       string
	Parameters []*Identifier
	Body       *BlockStatement
}

type EvalLiteral struct {
	Token   token.Token
	Content Expression
}

type AsyncLiteral struct {
	Token token.Token
	Name  string
	Block *BlockStatement
}

type LoadStatement struct {
	Token      token.Token
	Name       string
	Statements []Statement
}

func (ll *LoadStatement) expressionNode() {}
func (ll *LoadStatement) TokenLiteral() string {
	return ll.Token.Literal
}
func (ll *LoadStatement) String() string { return ll.Name }
func (ll *LoadStatement) Debug() string  { return ll.Name }

func (al *AsyncLiteral) expressionNode() {}
func (al *AsyncLiteral) TokenLiteral() string {
	return al.Token.Literal
}
func (al *AsyncLiteral) String() string { return "" }
func (al *AsyncLiteral) Debug() string  { return "" }

type WaitExpression struct {
	Token token.Token
	Async string
	Block *BlockStatement
}

func (we *WaitExpression) expressionNode() {}
func (we *WaitExpression) TokenLiteral() string {
	return we.Token.Literal
}
func (we *WaitExpression) String() string { return "" }
func (we *WaitExpression) Debug() string  { return "" }

func (el *EvalLiteral) expressionNode() {}
func (el *EvalLiteral) TokenLiteral() string {
	return el.Token.Literal
}
func (el *EvalLiteral) String() string { return el.Content.String() }
func (el *EvalLiteral) Debug() string  { return "" }

func (cl *CustomLiteral) expressionNode() {}
func (cl *CustomLiteral) TokenLiteral() string {
	return cl.Token.Literal
}
func (cl *CustomLiteral) String() string { return "" }
func (cl *CustomLiteral) Debug() string  { return "" }

func (ss *SignalExpression) expressionNode() {}
func (ss *SignalExpression) TokenLiteral() string {
	return ss.Token.Literal
}
func (ss *SignalExpression) String() string { return "" }
func (ss *SignalExpression) Debug() string  { return "" }

func (rl *RouteLiteral) expressionNode() {}
func (rl *RouteLiteral) TokenLiteral() string {
	return rl.Token.Literal
}
func (rl *RouteLiteral) String() string { return "" }
func (rl *RouteLiteral) Debug() string  { return "" }

type BeginLiteral struct {
	Token token.Token
	Name  string
	Block *BlockStatement
}

func (bl *BeginLiteral) expressionNode() {}
func (bl *BeginLiteral) TokenLiteral() string {
	return bl.Token.Literal
}
func (bl *BeginLiteral) String() string { return "" }
func (bl *BeginLiteral) Debug() string  { return "" }

type ExceptionExpression struct {
	Token      token.Token
	ErrorBlock *BlockStatement
	Begin      string
	Condition  Expression
}

func (ee *ExceptionExpression) expressionNode() {}
func (ee *ExceptionExpression) TokenLiteral() string {
	return ee.Token.Literal
}
func (ee *ExceptionExpression) String() string { return "" }
func (ee *ExceptionExpression) Debug() string  { return "" }

type RecoverExpression struct {
	Token      token.Token
	Begin      string
	ErrorIdent string
	Block      *BlockStatement
}

func (re *RecoverExpression) expressionNode() {}
func (re *RecoverExpression) TokenLiteral() string {
	return re.Token.Literal
}
func (re *RecoverExpression) String() string { return "" }
func (re *RecoverExpression) Debug() string  { return "" }

type FinalExpression struct {
	Token token.Token
	Begin string
	Block *BlockStatement
}

func (fe *FinalExpression) expressionNode() {}
func (fe *FinalExpression) TokenLiteral() string {
	return fe.Token.Literal
}
func (fe *FinalExpression) String() string { return "" }
func (fe *FinalExpression) Debug() string  { return "" }

func (fe *ForExpression) expressionNode() {}
func (fe *ForExpression) TokenLiteral() string {
	return fe.Token.Literal
}
func (fe *ForExpression) String() string { return "" }
func (fe *ForExpression) Debug() string  { return "" }

func (e *EnvExpression) expressionNode() {}
func (e *EnvExpression) TokenLiteral() string {
	return e.Token.Literal
}
func (e *EnvExpression) String() string { return "" }
func (e *EnvExpression) Debug() string  { return "" }

func (pl *ProcessExpression) expressionNode() {}
func (pl *ProcessExpression) TokenLiteral() string {
	return pl.Token.Literal
}
func (pl *ProcessExpression) String() string { return "" }
func (pl *ProcessExpression) Debug() string  { return "" }

func (s *SocketExpression) expressionNode() {}
func (s *SocketExpression) TokenLiteral() string {
	return s.Token.Literal
}
func (s *SocketExpression) String() string { return "" }
func (s *SocketExpression) Debug() string  { return "" }

type ScopeLiteral struct {
	Token token.Token // The 'scope' token
	Name  string
	Body  *BlockStatement
}

type ScopeExpression struct {
	Token     token.Token // The 'scope' token
	Left      string
	Right     string
	Arguments []Expression
}

func (ss *ScopeExpression) expressionNode() {}
func (ss *ScopeExpression) TokenLiteral() string {
	return ss.Token.Literal
}

func (ss *ScopeExpression) String() string { return "" }
func (ss *ScopeExpression) Debug() string {
	var out bytes.Buffer
	out.WriteString(ss.Left)
	out.WriteString(".")
	out.WriteString(ss.Right)
	out.WriteString("(")

	out.WriteString(")")
	return out.String()
}

type ScopeChangeExpression struct {
	Token token.Token // The 'scope' token
	Left  string
	Right string
	Value Statement
}

func (sce *ScopeChangeExpression) expressionNode() {}

func (sce *ScopeChangeExpression) TokenLiteral() string {
	return sce.Token.Literal
}

func (sce *ScopeChangeExpression) String() string {
	var out bytes.Buffer
	return out.String()
}

func (sce *ScopeChangeExpression) Debug() string {
	var out bytes.Buffer
	return out.String()
}

type ThisExpression struct {
	Token token.Token // The 'scope' token
	Name  *Identifier
}

func (te *ThisExpression) expressionNode() {}

func (te *ThisExpression) TokenLiteral() string {
	return te.Token.Literal
}

func (te *ThisExpression) String() string {
	var out bytes.Buffer

	out.WriteString(te.TokenLiteral() + ".")
	out.WriteString(te.Name.String())

	out.WriteString(";")

	return out.String()
}

func (te *ThisExpression) Debug() string {
	var out bytes.Buffer
	return out.String()
}

func (p *Program) statementNode() {}

func (p *Program) TokenLiteral() string {
	if len(p.Statements) > 0 {
		return p.Statements[0].TokenLiteral()
	}
	return ""
}

func (p *Program) String() string {
	var out bytes.Buffer

	for _, s := range p.Statements {
		out.WriteString(s.String())
	}

	return out.String()
}

func (p *Program) Debug() string {
	var out bytes.Buffer

	for _, s := range p.Statements {
		out.WriteString(s.Debug())
	}

	return out.String()
}

func (ls *LetStatement) statementNode() {}
func (ls *LetStatement) TokenLiteral() string {
	return ls.Token.Literal
}

func (ls *LetStatement) String() string {
	var out bytes.Buffer

	out.WriteString(ls.TokenLiteral() + " ")
	out.WriteString(ls.Name.String())
	out.WriteString(" = ")

	if ls.Value != nil {
		out.WriteString(ls.Value.String())
	}

	out.WriteString(";")

	return out.String()
}

func (ls *LetStatement) Debug() string {
	var out bytes.Buffer

	out.WriteString("window.")
	out.WriteString(ls.Name.Debug())
	out.WriteString(" = ")

	if ls.Value != nil {
		out.WriteString(ls.Value.Debug())
	}

	out.WriteString(";")

	return out.String()
}

func (i *Identifier) expressionNode() {}
func (i *Identifier) TokenLiteral() string {
	return i.Token.Literal
}

func (i *Identifier) String() string {
	return i.Value
}

func (i *Identifier) Debug() string {
	return i.Value
}

func (rs *ReturnStatement) statementNode() {}
func (rs *ReturnStatement) TokenLiteral() string {
	return rs.Token.Literal
}

func (rs *ReturnStatement) String() string {
	var out bytes.Buffer

	out.WriteString(rs.TokenLiteral() + " ")

	if rs.ReturnValue != nil {
		out.WriteString(rs.ReturnValue.String())
	}

	out.WriteString(";")

	return out.String()
}

func (rs *ReturnStatement) Debug() string {
	var out bytes.Buffer

	out.WriteString("return ")

	if rs.ReturnValue != nil {
		out.WriteString(rs.ReturnValue.Debug())
	}

	out.WriteString(";")

	return out.String()
}

func (es *ExpressionStatement) statementNode() {}

func (es *ExpressionStatement) TokenLiteral() string {
	return es.Token.Literal
}

func (es *ExpressionStatement) Self() Expression {
	return es.Expression
}

func (es *ExpressionStatement) String() string {
	if es.Expression != nil {
		return es.Expression.String()
	}

	return ""
}

func (es *ExpressionStatement) Debug() string {
	if es.Expression != nil {
		return es.Expression.Debug()
	}

	return ""
}

func (il *IntegerLiteral) expressionNode() {}
func (il *IntegerLiteral) TokenLiteral() string {
	return il.Token.Literal
}
func (il *IntegerLiteral) String() string {
	return il.Token.Literal
}

func (il *IntegerLiteral) Debug() string {
	return il.Token.Literal
}

func (il *FloatLiteral) expressionNode() {}
func (il *FloatLiteral) TokenLiteral() string {
	return il.Token.Literal
}
func (il *FloatLiteral) String() string {
	return il.Token.Literal
}

func (il *FloatLiteral) Debug() string {
	return il.Token.Literal
}

func (pe *PrefixExpression) expressionNode() {}
func (pe *PrefixExpression) TokenLiteral() string {
	return pe.Token.Literal
}
func (pe *PrefixExpression) String() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(pe.Operator)
	out.WriteString(pe.Right.String())
	out.WriteString(")")

	return out.String()
}

func (pe *PrefixExpression) Debug() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(pe.Operator)
	out.WriteString(pe.Right.Debug())
	out.WriteString(")")

	return out.String()
}

func (oe *InfixExpression) expressionNode() {}
func (oe *InfixExpression) TokenLiteral() string {
	return oe.Token.Literal
}
func (oe *InfixExpression) String() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(oe.Left.String())
	out.WriteString(" " + oe.Operator + " ")
	out.WriteString(oe.Right.String())
	out.WriteString(")")

	return out.String()
}

func (oe *InfixExpression) Debug() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(oe.Left.Debug())
	out.WriteString(" " + oe.Operator + " ")
	out.WriteString(oe.Right.Debug())
	out.WriteString(")")

	return out.String()
}

func (b *Boolean) expressionNode() {}
func (b *Boolean) TokenLiteral() string {
	return b.Token.Literal
}
func (b *Boolean) String() string {
	return b.Token.Literal
}

func (b *Boolean) Debug() string {
	return b.Token.Literal
}

func (b *Boolean) Code() string {
	return b.Token.Literal
}

func (ie *IfExpression) expressionNode() {}
func (ie *IfExpression) TokenLiteral() string {
	return ie.Token.Literal
}
func (ie *IfExpression) String() string {
	var out bytes.Buffer

	out.WriteString("if")
	out.WriteString(ie.Condition.String())
	out.WriteString(" ")
	out.WriteString(ie.Consequence.String())

	if ie.Alternative != nil {
		out.WriteString("else ")
		out.WriteString(ie.Alternative.String())
	}

	return out.String()
}
func (ie *IfExpression) Debug() string {
	var out bytes.Buffer

	out.WriteString("if (")
	out.WriteString(ie.Condition.Debug())
	out.WriteString(") {")
	out.WriteString(ie.Consequence.Debug())
	out.WriteString("} ")

	if ie.Alternative != nil {
		out.WriteString(" else {")
		out.WriteString(ie.Alternative.Debug())
		out.WriteString("}")
	}

	return out.String()
}

func (le *LoopExpression) expressionNode() {}
func (le *LoopExpression) TokenLiteral() string {
	return le.Token.Literal
}
func (le *LoopExpression) String() string {
	var out bytes.Buffer

	out.WriteString("loop ")
	out.WriteString(le.Condition.String())
	out.WriteString(" ")
	out.WriteString(le.Consequence.String())

	return out.String()
}

func (le *LoopExpression) Debug() string {
	var out bytes.Buffer

	out.WriteString("loop (")
	out.WriteString(le.Condition.Debug())
	out.WriteString(") {\n")
	out.WriteString(le.Consequence.Debug())
	out.WriteString("}\n")

	return out.String()
}

func (le *WhileExpression) expressionNode() {}
func (le *WhileExpression) TokenLiteral() string {
	return le.Token.Literal
}
func (le *WhileExpression) String() string {
	var out bytes.Buffer

	out.WriteString("while ")
	out.WriteString(le.Condition.String())
	out.WriteString(" ")
	out.WriteString(le.Consequence.String())

	return out.String()
}

func (le *WhileExpression) Debug() string {
	var out bytes.Buffer

	out.WriteString("while (")
	out.WriteString(le.Condition.Debug())
	out.WriteString(") {\n")
	out.WriteString(le.Consequence.Debug())
	out.WriteString("}\n")

	return out.String()
}

func (sl *ScopeLiteral) expressionNode() {}
func (sl *ScopeLiteral) TokenLiteral() string {
	return sl.Token.Literal
}
func (sl *ScopeLiteral) String() string { return "" }
func (sl *ScopeLiteral) Debug() string {
	var out bytes.Buffer

	out.WriteString("function ")
	out.WriteString(sl.Name)
	out.WriteString("{")
	out.WriteString(sl.Body.Debug())
	out.WriteString("}")

	return out.String()
}

func (bs *BlockStatement) expressionNode() {}
func (bs *BlockStatement) TokenLiteral() string {
	return bs.Token.Literal
}
func (bs *BlockStatement) String() string {
	var out bytes.Buffer

	for _, s := range bs.Statements {
		out.WriteString(s.String())
	}

	return out.String()
}
func (bs *BlockStatement) Debug() string {
	var out bytes.Buffer

	for _, s := range bs.Statements {
		out.WriteString(s.Debug())
	}

	return out.String()
}

func (fl *FunctionLiteral) expressionNode() {}
func (fl *FunctionLiteral) TokenLiteral() string {
	return fl.Token.Literal
}
func (fl *FunctionLiteral) String() string {
	var out bytes.Buffer

	params := []string{}

	for _, p := range fl.Parameters {
		params = append(params, p.String())
	}

	out.WriteString(fl.TokenLiteral())
	out.WriteString("(")
	out.WriteString(strings.Join(params, ", "))
	out.WriteString(") \n")
	out.WriteString(fl.Body.String())

	return out.String()
}

func (fl *FunctionLiteral) Debug() string {
	var out bytes.Buffer

	params := []string{}

	for _, p := range fl.Parameters {
		params = append(params, p.String())
	}

	out.WriteString(fl.TokenLiteral())
	out.WriteString("(")
	out.WriteString(strings.Join(params, ", "))
	out.WriteString(") \n")
	out.WriteString(fl.Body.String())

	return out.String()
}

func (ce *CallExpression) expressionNode() {}
func (ce *CallExpression) TokenLiteral() string {
	return ce.Token.Literal
}
func (ce *CallExpression) String() string {
	var out bytes.Buffer

	args := []string{}
	for _, a := range ce.Arguments {
		args = append(args, a.String())
	}

	out.WriteString(ce.Function.String())
	out.WriteString("(")
	out.WriteString(strings.Join(args, ", "))
	out.WriteString(")")

	return out.String()
}

func (ce *CallExpression) Debug() string {
	var out bytes.Buffer

	args := []string{}
	for _, a := range ce.Arguments {
		args = append(args, a.String())
	}

	out.WriteString(ce.Function.Debug())
	out.WriteString("(")
	out.WriteString(strings.Join(args, ", "))
	out.WriteString(")")

	return out.String()
}

func (sl *StringLiteral) expressionNode()      {}
func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal }
func (sl *StringLiteral) String() string       { return sl.Token.Literal }
func (sl *StringLiteral) Debug() string        { return "\"" + sl.Token.Literal + "\"" }

func (al *ArrayLiteral) expressionNode()      {}
func (al *ArrayLiteral) TokenLiteral() string { return al.Token.Literal }
func (al *ArrayLiteral) String() string {
	var out bytes.Buffer

	elements := []string{}
	for _, el := range al.Elements {
		elements = append(elements, el.String())
	}

	out.WriteString("[")
	out.WriteString(strings.Join(elements, ", "))
	out.WriteString("]")

	return out.String()
}
func (al *ArrayLiteral) Debug() string {
	var out bytes.Buffer

	elements := []string{}
	for _, el := range al.Elements {
		elements = append(elements, el.Debug())
	}

	out.WriteString("[")
	out.WriteString(strings.Join(elements, ", "))
	out.WriteString("]")

	return out.String()
}

func (ie *IndexExpression) expressionNode()      {}
func (ie *IndexExpression) TokenLiteral() string { return ie.Token.Literal }
func (ie *IndexExpression) String() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(ie.Left.String())
	out.WriteString("[")
	out.WriteString(ie.Index.String())
	out.WriteString("])")

	return out.String()
}

func (ie *IndexExpression) Debug() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(ie.Left.Debug())
	out.WriteString("[")
	out.WriteString(ie.Index.Debug())
	out.WriteString("])")

	return out.String()
}
func (hl *HashLiteral) expressionNode()      {}
func (hl *HashLiteral) TokenLiteral() string { return hl.Token.Literal }
func (hl *HashLiteral) String() string {
	var out bytes.Buffer

	pairs := []string{}
	for k, v := range hl.Pairs {
		pairs = append(pairs, k.String()+":"+v.String())
	}

	out.WriteString("{")
	out.WriteString(strings.Join(pairs, ", "))
	out.WriteString("}")

	return out.String()
}

func (hl *HashLiteral) Debug() string {
	var out bytes.Buffer

	pairs := []string{}
	for k, v := range hl.Pairs {
		pairs = append(pairs, k.Debug()+":"+v.Debug())
	}

	out.WriteString("{")
	out.WriteString(strings.Join(pairs, ", "))
	out.WriteString("}")

	return out.String()
}
