// 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 main

import (
	strings
	os
)

/*
	.vh generation logic.
	.vh files contain only function signatures, consts, and types.
	They are used together with pre-compiled modules.
*/

// "fn foo(a int) string"
fn (f &Fn) v_definition() string {
	//t :=time.ticks()
	mut sb := strings.new_builder(100)
	if f.is_public {
		sb.write('pub ')
	}	
	sb.write('fn ')
	if f.is_c {
		sb.write('C.')
	}	
	if f.is_method {
		recv := f.args[0]
		typ := v_type_str(recv.typ).replace('*', '')
		mut mu := if recv.is_mut { 'mut' } else { '' }
		if recv.ref {
			mu = '&'
		}	
		sb.write('($recv.name $mu $typ) ')
	}	
	if f.name.contains('__') {
		sb.write(f.name.all_after('__') + '(')
	} else {
		sb.write('$f.name(')
	}
	for i, arg in f.args {
		if i == 0 && f.is_method { // skip the receiver
			continue
		}	
		typ := v_type_str(arg.typ).replace('*', '&')
		if arg.name == '' {
			sb.write(typ)
		}	 else {
			sb.write('$arg.name $typ')
		}
		if i != f.args.len - 1 {
			sb.write(', ')
		}
	}
	sb.write(')')
	if f.typ != 'void' {
		typ := v_type_str(f.typ).replace('*', '&')
		sb.write(' ')
		sb.write(typ)
		sb.writeln(' ')
	}
	//println('ms: ${time.ticks() - t}')
	return sb.str()
}

fn v_type_str(typ_ string) string {
	mut typ := if typ_.ends_with('*') {
		'*' + typ_.left(typ_.len - 1)
	} else {
		typ_
	}	
	typ = typ.replace('Option_', '?')
	if typ.contains('_V_MulRet') {
		words := typ.replace('_V_MulRet_', '').split('_V_')
		typ = '('
		for i in 0 .. words.len {
			typ += words[i]
			if i != words.len - 1 {
				typ += ','
			}	
		}	
		typ += ')'
		return typ
	}	
	//println('"$typ"')
	if typ == '*void' {
		return 'voidptr'
	}	
	if typ == '*byte' {
		return 'byteptr'
	}	
	if typ.starts_with('array_') {
		return '[]' + typ.right(6)
	}	
	if typ.contains('__') {
		opt := typ.starts_with('?')
		typ = typ.all_after('__')
		if opt {
			typ = '?' + typ
		}	
	}	
	return typ
}	

fn (v &V) generate_vh() {
	println('\n\n\n\nGenerating a V header file for module `$v.mod`')
	dir := '$v_modules_path${os.PathSeparator}$v.mod'
	path := dir + '.vh'
	if !os.dir_exists(dir) {
		os.mkdir(dir)
	}
	println(path)
	
	file := os.create(path) or { panic(err) }
	// Consts
	file.writeln('// $v.mod module header \n')
	file.writeln('module $v.mod')
	file.writeln('// Consts')
	if v.table.consts.len > 0 {
		file.writeln('const (')
		for i, c in v.table.consts {
			if c.mod != v.mod {
				continue
			}	
			println('$i $c.name')
			//if !c.name.contains('__') {
				//continue
			//}
			name := c.name.all_after('__')
			typ := v_type_str(c.typ)
			file.writeln('\t$name $typ')
		}	
		file.writeln(')\n')
		// Globals
		for var in v.table.consts {
			if var.mod != v.mod {
				continue
			}	
			if !var.is_global {
				continue
			}	
			name := var.name.all_after('__')
			typ := v_type_str(var.typ)
			file.writeln('__global $name $typ')
		}
		file.writeln('\n')
	}
	// Types
	file.writeln('// Types')
	for _, typ in v.table.typesmap {
		//println(typ.name)
		if typ.mod != v.mod && typ.mod != ''{ // int, string etc mod == ''
			//println('skipping type "$typ.name"')
			continue
		}	
		if typ.name.contains('_V_MulRet') {
			continue
		}	
		mut name := typ.name
		if typ.name.contains('__') {
			name = typ.name.all_after('__')
		}	
		if typ.cat in [TypeCategory.struct_, .c_struct] {
			c := if typ.is_c { 'C.' } else { '' }
			file.writeln('struct ${c}$name {')
			// Private fields
			for field in typ.fields {
				if field.access_mod == .public {
					continue
				}	
				field_type := v_type_str(field.typ).replace('*', '&')
				file.writeln('\t$field.name $field_type')
			}	
			//file.writeln('pub:')
			mut public_str := ''
			for field in typ.fields {
				if field.access_mod == .private {
					continue
				}	
				field_type := v_type_str(field.typ).replace('*', '&')
				public_str += '\t$field.name $field_type\n'
				//file.writeln('\t$field.name $field_type')
			}	
			if public_str != '' {
				file.writeln('pub:' + public_str)
			}	
			file.writeln('}\n')
		}
	}	
	// Functions & methods
	file.writeln('// Functions')
	// Public first
	mut fns := []Fn
	// TODO fns := v.table.fns.filter(.mod == v.mod)
	for _, f in v.table.fns {
		if f.mod == v.mod || f.mod == ''{
			fns << f
		}	 else {
			//println('skipping fn $f.name mod=$f.mod')
		}	
	}
	for _, f in fns {
		if !f.is_public {
			continue
		}	
		file.writeln(f.v_definition())
	}	
	// Private
	for _, f in fns {
		if f.is_public {
			continue
		}	
		file.writeln(f.v_definition())
	}	
	// Methods
	file.writeln('\n// Methods //////////////////')
	for _, typ in v.table.typesmap {
		if typ.mod != v.mod && !(v.mod == 'builtin' && typ.mod == '') {
			println('skipping method typ $typ.name mod=$typ.mod')
			continue
		}	
		for method in typ.methods {
			file.writeln(method.v_definition())
		}	
	}	
	file.close()
	
	/*
	for i, p in v.parsers {
		if v.parsers[i].vh_lines.len > 0 {
			os.write_file(p.file_name +'.vh', v.parsers[i].vh_lines.join('\n'))
		}
	}	
	*/
}	

