// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler

fn (p mut Parser) for_st() {
	p.check(.key_for)
	p.for_expr_cnt++
	next_tok := p.peek()
	if p.tok != .lcbr {
		p.fspace()
	}
	// debug := p.scanner.file_path.contains('r_draw')
	p.open_scope()
	mut label := 0
	mut to := 0
	if p.tok == .lcbr {
		// Infinite loop
		p.gen('while (1) {')
	}
	else if p.tok == .key_mut {
		p.error('`mut` is not required in for loops')
	}
	// for i := 0; i < 10; i++ {
	else if next_tok == .decl_assign || next_tok == .assign || p.tok == .semicolon {
		p.genln('for (')
		if next_tok == .decl_assign {
			p.check_not_reserved()
			p.var_decl()
		}
		else if p.tok != .semicolon {
			// allow `for ;; i++ {`
			// Allow `for i = 0; i < ...`
			p.statement(false)
		}
		p.check(.semicolon)
		p.gen(' ; ')
		p.fspace()
		if p.tok != .semicolon {
			p.bool_expression()
		}
		p.check(.semicolon)
		p.gen(' ; ')
		p.fspace()
		if p.tok != .lcbr {
			p.statement(false)
		}
		p.genln(') { ')
	}
	// for i, val in array
	else if p.peek() == .comma {
		/*
		`for i, val in array {`
		==>
		```
		 array_int tmp = array;
		 for (int i = 0; i < tmp.len; i++) {
		 int val = tmp[i];
		```
		*/
		i := p.check_name()
		p.check(.comma)
		p.fspace()
		val := p.check_name()
		if i == '_' && val == '_' {
			p.error('no new variables on the left side of `in`')
		}
		p.fspace()
		p.check(.key_in)
		p.fspace()
		tmp := p.get_tmp()
		mut typ,expr := p.tmp_expr()
		is_arr := typ.starts_with('array_')
		is_map := typ.starts_with('map_')
		is_str := typ == 'string'
		is_variadic_arg := typ.starts_with('varg_')
		if !is_arr && !is_str && !is_map && !is_variadic_arg {
			p.error('cannot range over type `$typ`')
		}
		if !is_variadic_arg {
			if p.is_js {
				p.genln('var $tmp = $expr;')
			}
			else {
				p.genln('$typ $tmp = $expr;')
			}
		}
		// typ = strings.Replace(typ, "_ptr", "*", -1)
		mut i_var_type := 'int'
		if is_variadic_arg {
			typ = typ[5..]
			p.gen_for_varg_header(i, expr, typ, val)
		}
		else if is_arr {
			typ = parse_pointer(typ[6..])
			p.gen_for_header(i, tmp, typ, val)
		}
		else if is_map {
			i_var_type = 'string'
			typ = parse_pointer(typ[4..])
			p.gen_for_map_header(i, tmp, typ, val, typ)
		}
		else if is_str {
			typ = 'byte'
			p.gen_for_str_header(i, tmp, typ, val)
		}
		// Register temp vars
		if i != '_' {
			if p.known_var(i) {
				p.error('redefinition of `$i`')
			}
			p.register_var(Var{
				name: i
				typ: i_var_type
				is_mut: true
				is_changed: true
			})
		}
		if val != '_' {
			if p.known_var(val) {
				p.error('redefinition of `$val`')
			}
			p.register_var(Var{
				name: val
				typ: typ
				ptr: typ.contains('*')
			})
		}
	}
	// `for val in vals`
	else if p.peek() == .key_in {
		val := p.check_name()
		p.fspace()
		p.check(.key_in)
		p.fspace()
		tmp := p.get_tmp()
		mut typ,expr := p.tmp_expr()
		is_range := p.tok == .dotdot
		is_variadic_arg := typ.starts_with('varg_')
		mut range_end := ''
		if is_range {
			p.check_types(typ, 'int')
			p.check_space(.dotdot)
			if p.pref.x64 {
				to = p.lit.int()
			}
			range_typ,range_expr := p.tmp_expr()
			p.check_types(range_typ, 'int')
			range_end = range_expr
			if p.pref.x64 {
				label = p.x64.gen_loop_start(expr.int())
				// to  = range_expr.int() // TODO why empty?
			}
		}
		is_arr := typ.contains('array')
		is_fixed := typ.starts_with('[')
		is_str := typ == 'string'
		if !is_arr && !is_str && !is_range && !is_fixed && !is_variadic_arg {
			p.error('cannot range over type `$typ`')
		}
		if !is_variadic_arg {
			if p.is_js {
				p.genln('var $tmp = $expr;')
			}
			else if !is_fixed {
				// Don't copy if it's a fixed array
				p.genln('$typ $tmp = $expr;')
			}
		}
		// TODO var_type := if...
		i := p.get_tmp()
		if is_variadic_arg {
			typ = typ[5..]
			p.gen_for_varg_header(i, expr, typ, val)
		}
		else if is_range {
			typ = 'int'
			p.gen_for_range_header(i, range_end, tmp, typ, val)
		}
		else if is_arr {
			typ = parse_pointer(typ[6..]) // all after `array_`
			p.gen_for_header(i, tmp, typ, val)
		}
		else if is_str {
			typ = 'byte'
			p.gen_for_str_header(i, tmp, typ, val)
		}
		else if is_fixed {
			typ = typ.all_after(']')
			p.gen_for_fixed_header(i, expr, typ, val)
		}
		// println('for typ=$typ vartyp=$var_typ')
		// Register temp var
		if val != '_' {
			if p.known_var(val) {
				p.error('redefinition of `$val`')
			}
			p.register_var(Var{
				name: val
				typ: typ
				ptr: typ.contains('*')
				is_changed: true
				is_mut: false
				is_for_var: true
			})
		}
	}
	else {
		// `for a < b {`
		p.gen('while (')
		p.check_types(p.bool_expression(), 'bool')
		p.genln(') {')
	}
	p.fspace()
	p.check(.lcbr)
	p.genln('') // TODO why is this needed?
	p.statements()
	p.close_scope()
	p.for_expr_cnt--
	p.returns = false // TODO handle loops that are guaranteed to return
	if label > 0 {
		p.x64.gen_loop_end(to, label)
	}
}

