package dyvilx.tools.compiler.ast.header;

import dyvil.lang.Name;
import dyvil.reflect.Modifiers;
import dyvil.source.FileSource;
import dyvil.source.position.SourcePosition;
import dyvilx.tools.compiler.DyvilCompiler;
import dyvilx.tools.compiler.ast.attribute.AttributeList;
import dyvilx.tools.compiler.ast.context.IContext;
import dyvilx.tools.compiler.ast.context.IDefaultContext;
import dyvilx.tools.compiler.ast.structure.Package;
import dyvilx.tools.compiler.backend.ObjectFormat;
import dyvilx.tools.compiler.lang.I18n;
import dyvilx.tools.compiler.parser.DyvilSymbols;
import dyvilx.tools.compiler.parser.SemicolonInference;
import dyvilx.tools.compiler.parser.header.SourceFileParser;
import dyvilx.tools.compiler.sources.DyvilFileType;
import dyvilx.tools.compiler.util.Markers;
import dyvilx.tools.parsing.ParserManager;
import dyvilx.tools.parsing.TokenList;
import dyvilx.tools.parsing.lexer.DyvilLexer;
import dyvilx.tools.parsing.marker.MarkerList;

import java.io.File;
import java.io.IOException;

public class SourceHeader extends AbstractHeader implements ISourceHeader, IDefaultContext
{
	protected TokenList tokens;
	protected MarkerList markers = new MarkerList(Markers.INSTANCE);

	public final FileSource fileSource;
	public final File       outputDirectory;
	public final File       outputFile;

	protected final DyvilCompiler compiler;

	public SourceHeader(DyvilCompiler compiler, Package pack, File input, File output)
	{
		this.compiler = compiler;

		this.pack = pack;
		this.fileSource = new FileSource(input);

		this.setNameFromFile(input);

		final String name = output.getPath();
		final int start = name.lastIndexOf(File.separatorChar);
		final int end = name.lastIndexOf('.');

		this.outputDirectory = new File(name.substring(0, start));
		this.outputFile = new File(name.substring(0, end) + DyvilFileType.OBJECT_EXTENSION);
	}

	protected void setNameFromFile(File input)
	{
		final String name = input.getAbsolutePath();
		final int start = name.lastIndexOf(File.separatorChar);
		final int end = name.lastIndexOf('.');
		this.name = Name.fromQualified(name.substring(start + 1, end));
	}

	@Override
	public MarkerList getMarkers()
	{
		return this.markers;
	}

	@Override
	public DyvilCompiler getCompilationContext()
	{
		return this.compiler;
	}

	@Override
	public FileSource getFileSource()
	{
		return this.fileSource;
	}

	@Override
	public File getOutputFile()
	{
		return this.outputFile;
	}

	protected boolean load()
	{
		try
		{
			this.fileSource.load();
			return true;
		}
		catch (IOException ex)
		{
			this.compiler.error(I18n.get("source.error", this.fileSource.file()), ex);
			return false;
		}
	}

	@Override
	public void tokenize()
	{
		if (this.load())
		{
			this.tokens = new DyvilLexer(this.markers, DyvilSymbols.INSTANCE).tokenize(this.fileSource.text());
			SemicolonInference.inferSemicolons(this.tokens.first());
		}
	}

	@Override
	public void parse()
	{
		new ParserManager(DyvilSymbols.INSTANCE, this.tokens.iterator(), this.markers)
			.parse(new SourceFileParser(this).withFlags(SourceFileParser.NO_CLASSES));
	}

	@Override
	public void resolveHeaders()
	{
		for (int i = 0; i < this.importCount; i++)
		{
			this.importDeclarations[i].resolveTypes(this.markers, this);
		}
	}

	@Override
	public void resolveTypes()
	{
		final IContext context = this.getContext();

		for (int i = 0; i < this.typeAliasCount; i++)
		{
			this.typeAliases[i].resolveTypes(this.markers, context);
		}
	}

	@Override
	public void resolve()
	{
		if (this.headerDeclaration == null)
		{
			this.headerDeclaration = new HeaderDeclaration(this, SourcePosition.ORIGIN, this.name,
			                                               AttributeList.of(Modifiers.PUBLIC));
		}

		this.resolveImports();
	}

	protected void resolveImports()
	{
		for (int i = 0; i < this.importCount; i++)
		{
			this.importDeclarations[i].resolve(this.markers, this);
		}
	}

	@Override
	public void checkTypes()
	{
	}

	@Override
	public void check()
	{
		this.pack.check(this.packageDeclaration, this.markers);

		if (this.headerDeclaration != null)
		{
			this.headerDeclaration.check(this.markers);
		}
	}

	@Override
	public void foldConstants()
	{
	}

	@Override
	public void cleanup()
	{
	}

	protected boolean printMarkers()
	{
		return ICompilationUnit
			       .printMarkers(this.compiler, this.markers, DyvilFileType.DYVIL_HEADER, this.name, this.fileSource);
	}

	@Override
	public void compile()
	{
		if (this.printMarkers())
		{
			return;
		}

		ObjectFormat.write(this.compiler, this.outputFile, this);
	}
}

@@ -1104,16 +1104,60 @@
 ntext%0A%7B%0A
+%09// =============== Fields ===============%0A%0A
 %09protect
@@ -1177,16 +1177,16 @@
 tokens;%0A
-
 %09protect
@@ -1403,16 +1403,66 @@
 piler;%0A%0A
+%09// =============== Constructors ===============%0A%0A
 %09public 
@@ -2212,32 +2212,134 @@
 + 1, end));%0A%09%7D%0A%0A
+%09// =============== Methods ===============%0A%0A%09// --------------- Getters and Setters ---------------%0A%0A
 %09@Override%0A%09publ
@@ -2631,20 +2631,64 @@
 utFile;%0A
-
 %09%7D%0A%0A
+%09// --------------- Phases ---------------%0A%0A
 %09protect

Clean up SourceHeader