/*=============================================================================
Copyright (c) 2019-2021 Dario Deledda. All rights reserved.
Use of this source code is governed by an MIT license
that can be found in the LICENSE file.

This file contains string interpolation V functions
=============================================================================*/
module strconv

import strings

enum Char_parse_state {
	start
	norm_char
	field_char
	pad_ch
	len_set_start
	len_set_in
	check_type
	check_float
	check_float_in
	reset_params
}

pub fn v_printf(str string, pt ...voidptr) {
	print(v_sprintf(str, pt))
}

pub fn v_sprintf(str string, pt ...voidptr) string {
	mut res := strings.new_builder(pt.len * 16)

	mut i := 0 // main string index
	mut p_index := 0 // parameter index
	mut sign := false // sign flag
	mut allign := Align_text.right
	mut len0 := -1 // forced length, if -1 free length
	mut len1 := -1 // decimal part for floats
	def_len1 := 6 // default value for len1
	mut pad_ch := byte(` `) // pad char

	// prefix chars for Length field
	mut ch1 := `0` // +1 char if present else `0`
	mut ch2 := `0` // +2 char if present else `0`

	mut status := Char_parse_state.norm_char
	for i < str.len {
		if status == .reset_params {
			sign = false
			allign = .right
			len0 = -1
			len1 = -1
			pad_ch = ` `
			status = .norm_char
			ch1 = `0`
			ch2 = `0`
			continue
		}

		ch := str[i]
		if ch != `%` && status == .norm_char {
			res.write_b(ch)
			i++
			continue
		}
		if ch == `%` && status == .norm_char {
			status = .field_char
			i++
			continue
		}

		// single char, manage it here
		if ch == `c` && status == .field_char {
			v_sprintf_panic(p_index, pt.len)
			d1 := unsafe { *(&byte(pt[p_index])) }
			res.write_b(d1)
			status = .reset_params
			p_index++
			i++
			continue
		}

		// pointer, manage it here
		if ch == `p` && status == .field_char {
			v_sprintf_panic(p_index, pt.len)
			res.write_string('0x')
			res.write_string(ptr_str(unsafe { pt[p_index] }))
			status = .reset_params
			p_index++
			i++
			continue
		}

		if status == .field_char {
			mut fc_ch1 := `0`
			mut fc_ch2 := `0`
			if (i + 1) < str.len {
				fc_ch1 = str[i + 1]
				if (i + 2) < str.len {
					fc_ch2 = str[i + 2]
				}
			}
			if ch == `+` {
				sign = true
				i++
				continue
			} else if ch == `-` {
				allign = .left
				i++
				continue
			} else if ch in [`0`, ` `] {
				if allign == .right {
					pad_ch = ch
				}
				i++
				continue
			} else if ch == `\'` {
				i++
				continue
			} else if ch == `.` && fc_ch1 >= `1` && fc_ch1 <= `9` {
				status = .check_float
				i++
				continue
			}
			// manage "%.*s" precision field
			else if ch == `.` && fc_ch1 == `*` && fc_ch2 == `s` {
				v_sprintf_panic(p_index, pt.len)
				len := unsafe { *(&int(pt[p_index])) }
				p_index++
				v_sprintf_panic(p_index, pt.len)
				mut s := unsafe { *(&string(pt[p_index])) }
				s = s[..len]
				p_index++
				res.write_string(s)
				status = .reset_params
				i += 3
				continue
			}
			status = .len_set_start
			continue
		}

		if status == .len_set_start {
			if ch >= `1` && ch <= `9` {
				len0 = int(ch - `0`)
				status = .len_set_in
				i++
				continue
			}
			if ch == `.` {
				status = .check_float
				i++
				continue
			}
			status = .check_type
			continue
		}

		if status == .len_set_in {
			if ch >= `0` && ch <= `9` {
				len0 *= 10
				len0 += int(ch - `0`)
				i++
				continue
			}
			if ch == `.` {
				status = .check_float
				i++
				continue
			}
			status = .check_type
			continue
		}

		if status == .check_float {
			if ch >= `0` && ch <= `9` {
				len1 = int(ch - `0`)
				status = .check_float_in
				i++
				continue
			}
			status = .check_type
			continue
		}

		if status == .check_float_in {
			if ch >= `0` && ch <= `9` {
				len1 *= 10
				len1 += int(ch - `0`)
				i++
				continue
			}
			status = .check_type
			continue
		}

		if status == .check_type {
			if ch == `l` {
				if ch1 == `0` {
					ch1 = `l`
					i++
					continue
				} else {
					ch2 = `l`
					i++
					continue
				}
			} else if ch == `h` {
				if ch1 == `0` {
					ch1 = `h`
					i++
					continue
				} else {
					ch2 = `h`
					i++
					continue
				}
			}
			// signed integer
			else if ch in [`d`, `i`] {
				mut d1 := u64(0)
				mut positive := true

				// println("$ch1 $ch2")
				match ch1 {
					// h for 16 bit int
					// hh fot 8 bit int
					`h` {
						if ch2 == `h` {
							v_sprintf_panic(p_index, pt.len)
							x := unsafe { *(&i8(pt[p_index])) }
							positive = if x >= 0 { true } else { false }
							d1 = if positive { u64(x) } else { u64(-x) }
						} else {
							x := unsafe { *(&i16(pt[p_index])) }
							positive = if x >= 0 { true } else { false }
							d1 = if positive { u64(x) } else { u64(-x) }
						}
					}
					// l  i64
					// ll i64 for now
					`l` {
						// placeholder for future 128bit integer code
						/*
						if ch2 == `l` {
							v_sprintf_panic(p_index, pt.len)
							x := *(&i128(pt[p_index]))
							positive = if x >= 0 { true } else { false }
							d1 = if positive { u128(x) } else { u128(-x) }
						} else {
							v_sprintf_panic(p_index, pt.len)
							x := *(&i64(pt[p_index]))
							positive = if x >= 0 { true } else { false }
							d1 = if positive { u64(x) } else { u64(-x) }
						}
						*/
						v_sprintf_panic(p_index, pt.len)
						x := unsafe { *(&i64(pt[p_index])) }
						positive = if x >= 0 { true } else { false }
						d1 = if positive { u64(x) } else { u64(-x) }
					}
					// default int
					else {
						v_sprintf_panic(p_index, pt.len)
						x := unsafe { *(&int(pt[p_index])) }
						positive = if x >= 0 { true } else { false }
						d1 = if positive { u64(x) } else { u64(-x) }
					}
				}
				res.write_string(format_dec_old(d1,
					pad_ch: pad_ch
					len0: len0
					len1: 0
					positive: positive
					sign_flag: sign
					allign: allign
				))
				status = .reset_params
				p_index++
				i++
				ch1 = `0`
				ch2 = `0`
				continue
			}
			// unsigned integer
			else if ch == `u` {
				mut d1 := u64(0)
				positive := true
				v_sprintf_panic(p_index, pt.len)
				match ch1 {
					// h for 16 bit unsigned int
					// hh fot 8 bit unsigned int
					`h` {
						if ch2 == `h` {
							d1 = u64(unsafe { *(&byte(pt[p_index])) })
						} else {
							d1 = u64(unsafe { *(&u16(pt[p_index])) })
						}
					}
					// l  u64
					// ll u64 for now
					`l` {
						// placeholder for future 128bit integer code
						/*
						if ch2 == `l` {
							d1 = u128(*(&u128(pt[p_index])))
						} else {
							d1 = u64(*(&u64(pt[p_index])))
						}
						*/
						d1 = u64(unsafe { *(&u64(pt[p_index])) })
					}
					// default int
					else {
						d1 = u64(unsafe { *(&u32(pt[p_index])) })
					}
				}

				res.write_string(format_dec_old(d1,
					pad_ch: pad_ch
					len0: len0
					len1: 0
					positive: positive
					sign_flag: sign
					allign: allign
				))
				status = .reset_params
				p_index++
				i++
				continue
			}
			// hex
			else if ch in [`x`, `X`] {
				v_sprintf_panic(p_index, pt.len)
				mut s := ''
				match ch1 {
					// h for 16 bit int
					// hh fot 8 bit int
					`h` {
						if ch2 == `h` {
							x := unsafe { *(&i8(pt[p_index])) }
							s = x.hex()
						} else {
							x := unsafe { *(&i16(pt[p_index])) }
							s = x.hex()
						}
					}
					// l  i64
					// ll i64 for now
					`l` {
						// placeholder for future 128bit integer code
						/*
						if ch2 == `l` {
							x := *(&i128(pt[p_index]))
							s = x.hex()
						} else {
							x := *(&i64(pt[p_index]))
							s = x.hex()
						}
						*/
						x := unsafe { *(&i64(pt[p_index])) }
						s = x.hex()
					}
					else {
						x := unsafe { *(&int(pt[p_index])) }
						s = x.hex()
					}
				}

				if ch == `X` {
					s = s.to_upper()
				}

				res.write_string(format_str(s,
					pad_ch: pad_ch
					len0: len0
					len1: 0
					positive: true
					sign_flag: false
					allign: allign
				))
				status = .reset_params
				p_index++
				i++
				continue
			}

			// float and double
			if ch in [`f`, `F`] {
				v_sprintf_panic(p_index, pt.len)
				x := unsafe { *(&f64(pt[p_index])) }
				positive := x >= f64(0.0)
				len1 = if len1 >= 0 { len1 } else { def_len1 }
				s := format_fl_old(f64(x),
					pad_ch: pad_ch
					len0: len0
					len1: len1
					positive: positive
					sign_flag: sign
					allign: allign
				)
				res.write_string(if ch == `F` { s.to_upper() } else { s })
				status = .reset_params
				p_index++
				i++
				continue
			} else if ch in [`e`, `E`] {
				v_sprintf_panic(p_index, pt.len)
				x := unsafe { *(&f64(pt[p_index])) }
				positive := x >= f64(0.0)
				len1 = if len1 >= 0 { len1 } else { def_len1 }
				s := format_es_old(f64(x),
					pad_ch: pad_ch
					len0: len0
					len1: len1
					positive: positive
					sign_flag: sign
					allign: allign
				)
				res.write_string(if ch == `E` { s.to_upper() } else { s })
				status = .reset_params
				p_index++
				i++
				continue
			} else if ch in [`g`, `G`] {
				v_sprintf_panic(p_index, pt.len)
				x := unsafe { *(&f64(pt[p_index])) }
				positive := x >= f64(0.0)
				mut s := ''
				tx := fabs(x)
				if tx < 999_999.0 && tx >= 0.00001 {
					// println("Here g format_fl [$tx]")
					len1 = if len1 >= 0 { len1 + 1 } else { def_len1 }
					s = format_fl_old(x,
						pad_ch: pad_ch
						len0: len0
						len1: len1
						positive: positive
						sign_flag: sign
						allign: allign
						rm_tail_zero: true
					)
				} else {
					len1 = if len1 >= 0 { len1 + 1 } else { def_len1 }
					s = format_es_old(x,
						pad_ch: pad_ch
						len0: len0
						len1: len1
						positive: positive
						sign_flag: sign
						allign: allign
						rm_tail_zero: true
					)
				}
				res.write_string(if ch == `G` { s.to_upper() } else { s })
				status = .reset_params
				p_index++
				i++
				continue
			}
			// string
			else if ch == `s` {
				v_sprintf_panic(p_index, pt.len)
				s1 := unsafe { *(&string(pt[p_index])) }
				pad_ch = ` `
				res.write_string(format_str(s1,
					pad_ch: pad_ch
					len0: len0
					len1: 0
					positive: true
					sign_flag: false
					allign: allign
				))
				status = .reset_params
				p_index++
				i++
				continue
			}
		}

		status = .reset_params
		p_index++
		i++
	}

	if p_index != pt.len {
		panic('$p_index % conversion specifiers, but given $pt.len args')
	}

	return res.str()
}

[inline]
fn v_sprintf_panic(idx int, len int) {
	if idx >= len {
		panic('${idx + 1} % conversion specifiers, but given only $len args')
	}
}

fn fabs(x f64) f64 {
	if x < 0.0 {
		return -x
	}
	return x
}

// strings.Builder version of format_fl
[manualfree]
pub fn format_fl_old(f f64, p BF_param) string {
	unsafe {
		mut s := ''
		// mut fs := "1.2343"
		mut fs := f64_to_str_lnd1(if f >= 0.0 { f } else { -f }, p.len1)
		// println("Dario")
		// println(fs)

		// error!!
		if fs[0] == `[` {
			s.free()
			return fs
		}

		if p.rm_tail_zero {
			tmp := fs
			fs = remove_tail_zeros_old(fs)
			tmp.free()
		}
		mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len })

		mut sign_len_diff := 0
		if p.pad_ch == `0` {
			if p.positive {
				if p.sign_flag {
					res.write_b(`+`)
					sign_len_diff = -1
				}
			} else {
				res.write_b(`-`)
				sign_len_diff = -1
			}
			tmp := s
			s = fs.clone()
			tmp.free()
		} else {
			if p.positive {
				if p.sign_flag {
					tmp := s
					s = '+' + fs
					tmp.free()
				} else {
					tmp := s
					s = fs.clone()
					tmp.free()
				}
			} else {
				tmp := s
				s = '-' + fs
				tmp.free()
			}
		}

		dif := p.len0 - s.len + sign_len_diff

		if p.allign == .right {
			for i1 := 0; i1 < dif; i1++ {
				res.write_b(p.pad_ch)
			}
		}
		res.write_string(s)
		if p.allign == .left {
			for i1 := 0; i1 < dif; i1++ {
				res.write_b(p.pad_ch)
			}
		}

		s.free()
		fs.free()
		tmp_res := res.str()
		res.free()
		return tmp_res
	}
}

[manualfree]
pub fn format_es_old(f f64, p BF_param) string {
	unsafe {
		mut s := ''
		mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1)
		if p.rm_tail_zero {
			fs = remove_tail_zeros_old(fs)
		}
		mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len })

		mut sign_len_diff := 0
		if p.pad_ch == `0` {
			if p.positive {
				if p.sign_flag {
					res.write_b(`+`)
					sign_len_diff = -1
				}
			} else {
				res.write_b(`-`)
				sign_len_diff = -1
			}
			tmp := s
			s = fs.clone()
			tmp.free()
		} else {
			if p.positive {
				if p.sign_flag {
					tmp := s
					s = '+' + fs
					tmp.free()
				} else {
					tmp := s
					s = fs.clone()
					tmp.free()
				}
			} else {
				tmp := s
				s = '-' + fs
				tmp.free()
			}
		}

		dif := p.len0 - s.len + sign_len_diff
		if p.allign == .right {
			for i1 := 0; i1 < dif; i1++ {
				res.write_b(p.pad_ch)
			}
		}
		res.write_string(s)
		if p.allign == .left {
			for i1 := 0; i1 < dif; i1++ {
				res.write_b(p.pad_ch)
			}
		}
		s.free()
		fs.free()
		tmp_res := res.str()
		res.free()
		return tmp_res
	}
}

pub fn remove_tail_zeros_old(s string) string {
	mut i := 0
	mut last_zero_start := -1
	mut dot_pos := -1
	mut in_decimal := false
	mut prev_ch := byte(0)
	for i < s.len {
		ch := unsafe { s.str[i] }
		if ch == `.` {
			in_decimal = true
			dot_pos = i
		} else if in_decimal {
			if ch == `0` && prev_ch != `0` {
				last_zero_start = i
			} else if ch >= `1` && ch <= `9` {
				last_zero_start = -1
			} else if ch == `e` {
				break
			}
		}
		prev_ch = ch
		i++
	}

	mut tmp := ''
	if last_zero_start > 0 {
		if last_zero_start == dot_pos + 1 {
			tmp = s[..dot_pos] + s[i..]
		} else {
			tmp = s[..last_zero_start] + s[i..]
		}
	} else {
		tmp = s
	}
	if unsafe { tmp.str[tmp.len - 1] } == `.` {
		return tmp[..tmp.len - 1]
	}
	return tmp
}

// max int64 9223372036854775807
pub fn format_dec_old(d u64, p BF_param) string {
	mut s := ''
	mut res := strings.new_builder(20)
	mut sign_len_diff := 0
	if p.pad_ch == `0` {
		if p.positive {
			if p.sign_flag {
				res.write_b(`+`)
				sign_len_diff = -1
			}
		} else {
			res.write_b(`-`)
			sign_len_diff = -1
		}
		s = d.str()
	} else {
		if p.positive {
			if p.sign_flag {
				s = '+' + d.str()
			} else {
				s = d.str()
			}
		} else {
			s = '-' + d.str()
		}
	}
	dif := p.len0 - s.len + sign_len_diff

	if p.allign == .right {
		for i1 := 0; i1 < dif; i1++ {
			res.write_b(p.pad_ch)
		}
	}
	res.write_string(s)
	if p.allign == .left {
		for i1 := 0; i1 < dif; i1++ {
			res.write_b(p.pad_ch)
		}
	}
	return res.str()
}