// Copyright (c) 2021 Lars Pontoppidan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module ast

import toml.token
import strconv

// Key is a sumtype representing all types of keys that
// can be found in a TOML document.
pub type Key = Bare | Bool | Null | Number | Quoted

// str returns the string representation of the key. This is implemented
// by all the variants of Key.
pub fn (k Key) str() string {
	return k.text
}

// Value is a sumtype representing all possible value types
// found in a TOML document.
pub type Value = Bool
	| Date
	| DateTime
	| Null
	| Number
	| Quoted
	| Time
	| []Value
	| map[string]Value

// str outputs the value in JSON-like format for eased
// debugging
pub fn (v Value) str() string {
	match v {
		Quoted, Date, DateTime, Time {
			return '"${v.text}"'
		}
		Bool, Null, Number {
			return v.text
		}
		map[string]Value {
			mut str := '{'
			for key, val in v {
				str += ' "${key}": ${val},'
			}
			str = str.trim_right(',')
			str += ' }'
			return str
		}
		[]Value {
			mut str := '['
			for val in v {
				str += ' ${val},'
			}
			str = str.trim_right(',')
			str += ' ]'
			return str
		}
	}
}

// DateTimeType is a sumtype representing all possible date types
// found in a TOML document.
pub type DateTimeType = Date | DateTime | Time

// str returns the `string` representation of the `DateTimeType` type.
pub fn (dtt DateTimeType) str() string {
	return dtt.text
}

// Comment is the data representation of a TOML comment (`# This is a comment`).
pub struct Comment {
pub:
	text string
	pos  token.Pos
}

// str returns the `string` representation of the `Comment` type.
pub fn (c Comment) str() string {
	mut s := typeof(c).name + '{\n'
	s += '  text:  \'${c.text}\'\n'
	s += '  pos:  ${c.pos}\n'
	s += '}'
	return s
}

// Null is used in sumtype checks as a "default" value when nothing else is possible.
pub struct Null {
pub:
	text string
	pos  token.Pos
}

// str returns the `string` representation of the `Null` type
pub fn (n Null) str() string {
	return n.text
}

// Quoted is the data representation of a TOML quoted type (`"quoted-key" = "I'm a quoted value"`).
// Quoted types can appear both as keys and values in TOML documents.
pub struct Quoted {
pub mut:
	text string
pub:
	pos          token.Pos
	is_multiline bool
	quote        u8
}

// str returns the `string` representation of the `Quoted` type.
pub fn (q Quoted) str() string {
	mut str := typeof(q).name + '{\n'
	str += '  text:  \'${q.text}\'\n'
	str += '  pos:  ${q.pos}\n'
	str += '  is_multiline:  ${q.is_multiline}\n'
	str += '  quote: \'${q.quote}\'\n'
	str += '}'
	return str
}

// Bare is the data representation of a TOML bare type (`bare_key = ...`).
// Bare types can appear only as keys in TOML documents. Otherwise they take the
// form of Bool or Numbers.
pub struct Bare {
pub:
	text string
	pos  token.Pos
}

// str returns the `string` representation of the `Bare` type.
pub fn (b Bare) str() string {
	mut str := typeof(b).name + '{\n'
	str += '  text:  \'${b.text}\'\n'
	str += '  pos:  ${b.pos}\n'
	str += '}'
	return str
}

// Bool is the data representation of a TOML boolean type (`... = true`).
// Bool types can appear only as values in TOML documents. Keys named `true` or `false`
// are considered as Bare types.
pub struct Bool {
pub:
	text string
	pos  token.Pos
}

// str returns the `string` representation of the `Bool` type.
pub fn (b Bool) str() string {
	mut str := typeof(b).name + '{\n'
	str += '  text:  \'${b.text}\'\n'
	str += '  pos:  ${b.pos}\n'
	str += '}'
	return str
}

// Number is the data representation of a TOML number type (`25 = 5e2`).
// Number types can appear both as keys and values in TOML documents.
// Number can be integers, floats, infinite, NaN - they can have exponents (`5e2`) and be sign prefixed (`+2`).
pub struct Number {
pub:
	pos token.Pos
pub mut:
	text string
}

// str returns the `string` representation of the `Number` type.
pub fn (n Number) str() string {
	mut str := typeof(n).name + '{\n'
	str += '  text:  \'${n.text}\'\n'
	str += '  pos:  ${n.pos}\n'
	str += '}'
	return str
}

// i64 returns the `n Number` as an `i64` value.
pub fn (n Number) i64() i64 {
	if n.text.starts_with('0x') {
		hex := n.text.all_after('0x').to_upper().replace('_', '')
		return strconv.parse_int(hex, 16, 64) or { i64(0) }
	} else if n.text.starts_with('0o') {
		oct := n.text.all_after('0o').replace('_', '')
		return strconv.parse_int(oct, 8, 64) or { i64(0) }
	} else if n.text.starts_with('0b') {
		bin := n.text.all_after('0b').replace('_', '')
		return strconv.parse_int(bin, 2, 64) or { i64(0) }
	}
	return strconv.parse_int(n.text, 0, 64) or { i64(0) }
}

// f64 returns the `n Number` as an `f64` value.
pub fn (n Number) f64() f64 {
	return n.text.replace('_', '').f64()
}

// Date is the data representation of a TOML date type (`YYYY-MM-DD`).
// Date types can appear both as keys and values in TOML documents.
// Keys named like dates e.g. `1980-12-29` are considered Bare key types.
pub struct Date {
pub:
	text string
	pos  token.Pos
}

// str returns the `string` representation of the `Date` type.
pub fn (d Date) str() string {
	mut str := typeof(d).name + '{\n'
	str += '  text:  \'${d.text}\'\n'
	str += '  pos:  ${d.pos}\n'
	str += '}'
	return str
}

// Time is the data representation of a TOML time type (`HH:MM:SS.milli`).
// Time types can appear only as values in TOML documents.
pub struct Time {
pub:
	text   string
	offset int
	pos    token.Pos
}

// str returns the `string` representation of the `Time` type.
pub fn (t Time) str() string {
	mut str := typeof(t).name + '{\n'
	str += '  text:  \'${t.text}\'\n'
	str += '  offset:  \'${t.offset}\'\n'
	str += '  pos:  ${t.pos}\n'
	str += '}'
	return str
}

// DateTime is the data representation of a TOML date-time type (`YYYY-MM-DDTHH:MM:SS.milli`).
// DateTime types can appear only as values in TOML documents.
pub struct DateTime {
pub mut:
	text string
pub:
	pos  token.Pos
	date Date
	time Time
}

// str returns the `string` representation of the `DateTime` type.
pub fn (dt DateTime) str() string {
	mut str := typeof(dt).name + '{\n'
	str += '  text:  \'${dt.text}\'\n'
	str += '  date:  \'${dt.date}\'\n'
	str += '  time:  \'${dt.time}\'\n'
	str += '  pos:  ${dt.pos}\n'
	str += '}'
	return str
}

// EOF is the data representation of the end of the TOML document.
pub struct EOF {
pub:
	pos token.Pos
}

// str returns the `string` representation of the `EOF` type.
pub fn (e EOF) str() string {
	mut str := typeof(e).name + '{\n'
	str += '  pos:  ${e.pos}\n'
	str += '}'
	return str
}
