module compiler

import strings

const (
	dot_ptr = '.'
)

fn (p mut Parser) gen_var_decl(name string, is_static bool) string {
	p.gen('var $name /* typ */ = ')
	mut typ := p.bool_expression()
	if typ.starts_with('...') { typ = typ.right(3) }
	or_else := p.tok == .key_orelse
	//tmp := p.get_tmp()
	if or_else {
		//panic('optionals todo')
	}
	return typ
}

fn (p mut Parser) gen_fn_decl(f Fn, typ, _str_args string) {
	mut str_args := ''
	for i, arg in f.args   {
		str_args += ' /** @type { $arg.typ } **/ ' + arg.name
		if i < f.args.len - 1 {
			str_args += ', '
		}
	}
	name := p.table.fn_gen_name(f)
	if f.is_method {
		p.genln('\n${f.receiver_typ}.prototype.${name} = function($str_args) {')
	}	 else {
		p.genln('/** @return { $typ } **/\nfunction $name($str_args) {')
	}
}

fn (p mut Parser) gen_blank_identifier_assign() {
	assign_error_tok_idx := p.token_idx
	p.check_name()
	p.check_space(.assign)
	expr := p.lit
	is_indexer := p.peek() == .lsbr
	is_fn_call := p.peek() == .lpar || (p.peek() == .dot && p.tokens[p.token_idx+2].tok == .lpar)
	if !is_indexer && !is_fn_call {
		p.error_with_token_index('assigning `$expr` to `_` is redundant', assign_error_tok_idx)
	}
	p.bool_expression()
	or_else := p.tok == .key_orelse
	//tmp := p.get_tmp()
	if or_else {
		//panic('optionals todo')
	}
}

fn types_to_c(types []Type, table &Table) string {
	mut sb := strings.new_builder(10)
	for t in types {
		if t.cat != .union_ && t.cat != .struct_ {
			continue
		}
		sb.write('\n/**\n')
		sb.write('* @typedef { object } $t.name' + 'Type\n')
		for field in t.fields {
			sb.writeln('* @property { $field.typ' + '= } $field.name')
		}
		sb.writeln('**/\n')
		sb.writeln('/** @type { function & $t.name' + 'Type } **/')
		sb.writeln('var $t.name = function() {}')
	}
	return sb.str()
}

fn (p mut Parser) index_get(typ string, fn_ph int, cfg IndexCfg) {
	p.cgen.cur_line = p.cgen.cur_line.replace(',', '[') + ']'
}

fn (table &Table) fn_gen_name(f &Fn) string {
	mut name := f.name
	if f.is_method {
		name = name.replace(' ', '')
		name = name.replace('*', '')
		name = name.replace('+', 'plus')
		name = name.replace('-', 'minus')
		return name
	}
	// Avoid name conflicts (with things like abs(), print() etc).
	// Generate b_abs(), b_print()
	// TODO duplicate functionality
	if f.mod == 'builtin' && f.name in CReserved {
		return 'v_$name'
	}
	return name
}

fn (p mut Parser) gen_method_call(receiver_type, ftyp string, cgen_name string, receiver Var,method_ph int) {
	//mut cgen_name := p.table.fn_gen_name(f)
	//mut method_call := cgen_name + '('
	p.gen('.' + cgen_name.all_after('_') + '(')
	//p.cgen.set_placeholder(method_ph, '$cast kKE $method_call')
	//return method_call
}


fn (p mut Parser) gen_array_at(typ string, is_arr0 bool, fn_ph int) {
	p.gen('[')
}	

fn (p mut Parser) gen_for_header(i, tmp, var_typ, val string) {
	p.genln('for (var $i = 0; $i < ${tmp}.length; $i++) {')
	if val == '_' { return }
	p.genln('var $val = $tmp [$i];')
}

fn (p mut Parser) gen_for_range_header(i, range_end, tmp, var_type, val string) {
	p.genln(';\nfor (var $i = $tmp; $i < $range_end; $i++) {')
	if val == '_' { return }
	p.genln('var /*$var_type*/ $val = $i;')
}

fn (p mut Parser) gen_for_str_header(i, tmp, var_typ, val string) {
	p.genln('for (var $i = 0; $i < $tmp .length; $i ++) {')
	if val == '_' { return }
	p.genln('var $val = $tmp[$i];')
}

fn (p mut Parser) gen_for_map_header(i, tmp, var_typ, val, typ string) {
	p.genln('for (var $i in $tmp) {')
	if val == '_' { return }
	p.genln('var $val = $tmp[$i];')
}

fn (p mut Parser) gen_for_varg_header(i, varg, var_typ, val string) {
	p.genln('for (var $i = 0; $i < ${varg}.len; $i++) {')
	if val == '_' { return }
	p.genln('var $val = ${varg}.args[$i];')
}

fn (p mut Parser) gen_array_init(typ string, no_alloc bool, new_arr_ph int, nr_elems int) {
	p.cgen.set_placeholder(new_arr_ph,	'[')
	p.gen(']')
}

fn (p mut Parser) gen_array_set(typ string, is_ptr, is_map bool,fn_ph, assign_pos int, is_cao bool) {
	mut val := p.cgen.cur_line.right(assign_pos)
	p.cgen.resetln(p.cgen.cur_line.left(assign_pos))
	p.gen('] =')
	cao_tmp := p.cgen.cur_line
	if is_cao  {
		val = cao_tmp + val.all_before('=') +	val.all_after('=')
	}
	p.gen(val)
}

// returns true in case of an early return
fn (p mut Parser) gen_struct_init(typ string, t Type) bool {
	p.next()
	p.check(.lcbr)
	ptr := typ.contains('*')
	if !ptr {
			p.gen('{')
	}
	else {
		// TODO tmp hack for 0 pointers init
		// &User{!} ==> 0
		if p.tok == .not {
			p.next()
			p.gen('}')
			p.check(.rcbr)
			return true
		}
	}
	return false
}

fn (p mut Parser) gen_struct_field_init(field string) {
	p.gen('$field : ')
}

fn (p mut Parser) gen_empty_map(typ string) {
	p.gen('{}')
}

fn (p mut Parser) cast(typ string) string {
	p.next()
	pos := p.cgen.add_placeholder()
	if p.tok == .rpar {
		p.next()
	}
	p.check(.lpar)
	p.bool_expression()
	if typ == 'string' {
		if p.tok == .comma {
			p.check(.comma)
			p.cgen.set_placeholder(pos, 'tos(')
			//p.gen('tos(')
			p.gen(', ')
			p.expression()
			p.gen(')')
		}
	}
	p.check(.rpar)
	return typ
}

fn type_default(typ string) string {
	if typ.starts_with('array_') {
		return '[]'
	}
	// Always set pointers to 0
	if typ.ends_with('*') {
		return '0'
	}
	// User struct defined in another module.
	if typ.contains('__') {
		return '{}'
	}
	// Default values for other types are not needed because of mandatory initialization
	switch typ {
	case 'bool': return '0'
	case 'string': return '""'
	case 'i8': return '0'
	case 'i16': return '0'
	case 'i64': return '0'
	case 'u16': return '0'
	case 'u32': return '0'
	case 'u64': return '0'
	case 'byte': return '0'
	case 'int': return '0'
	case 'rune': return '0'
	case 'f32': return '0.0'
	case 'f64': return '0.0'
	case 'byteptr': return '0'
	case 'voidptr': return '0'
	}
	return '{}'
}

fn (p mut Parser) gen_array_push(ph int, typ, expr_type, tmp, tmp_typ string) {
	push_array := typ == expr_type
	if push_array {
		p.cgen.set_placeholder(ph, 'push(&' )
		p.gen('), $tmp, $typ)')
	}  else {
		p.check_types(expr_type, tmp_typ)
		p.gen(')')
		p.cgen.cur_line = p.cgen.cur_line.replace(',', '.push')
	}
}