package org.bds.lang.statement;

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

import org.antlr.v4.runtime.tree.ParseTree;
import org.bds.Config;
import org.bds.compile.CompilerMessage.MessageType;
import org.bds.compile.CompilerMessages;
import org.bds.lang.BdsNode;
import org.bds.symbol.SymbolTable;

/**
 * Include statement: Get source from another file
 */
public class StatementInclude extends BlockWithFile {

	private static final long serialVersionUID = 7172299775285428224L;


	protected String parentFileName;

	/**
	 * Find the appropriate file
	 * @param includedFilename
	 * @param parent : Parent file (where the 'include' statement is)
	 */
	public static File includeFile(String includedFilename, File parent) {
		File includedFile = new File(includedFilename);
		if (includedFile.exists() && includedFile.canRead()) return includedFile;

		// Not an absolute file name? Try to find relative to parent file
		if (!includedFile.isAbsolute()) {
			if (parent != null && parent.exists()) includedFile = new File(parent.getParent(), includedFilename);
			if (includedFile.exists() && includedFile.canRead()) return includedFile;

			// Try parent's canonical path (e.g. when file is a symLink)
			File parentCanon = null;
			try {
				parentCanon = parent.getCanonicalFile();
				if (parentCanon != null && parentCanon.exists()) includedFile = new File(parentCanon.getParent(), includedFilename);
				if (includedFile.exists() && includedFile.canRead()) return includedFile;
			} catch (IOException e) {
			}

			// Try all include paths
			for (String incPath : Config.get().getIncludePath()) {
				File incDir = new File(incPath);
				includedFile = new File(incDir, includedFilename);
				if (includedFile.exists() && includedFile.canRead()) return includedFile; // Found the file?
			}
		}

		return null;
	}

	/**
	 * Find the appropriate include file name
	 */
	public static String includeFileName(String includedFilename) {
		if (includedFilename.startsWith("\'") || includedFilename.startsWith("\"")) includedFilename = includedFilename.substring(1); // Remove leading quotes
		if (includedFilename.endsWith("\'") || includedFilename.endsWith("\"")) includedFilename = includedFilename.substring(0, includedFilename.length() - 1).trim(); // Remove trailing quotes
		if (!includedFilename.endsWith(".bds")) includedFilename += ".bds"; // Append '.bds' if needed (it's optional)
		return includedFilename;
	}

	private static boolean isValidFileName(String fileName) {
		if (fileName == null || fileName.length() == 0 || !fileName.trim().equals(fileName)) return false;
		return true;
	}

	public StatementInclude(BdsNode parent, ParseTree tree) {
		super(parent, tree);
	}

	public String getParentFileName() {
		return parentFileName;
	}

	@Override
	protected void parse(ParseTree tree) {
		setNeedsScope(false);

		// File name & parent file name: this is merely informational
		File parentFile = getParent().getFile();
		parentFileName = (parentFile != null ? parentFile.toString() : null);
		fileName = includeFileName(tree.getChild(0).getChild(1).getText());

		// Resolve file and read program text
		File includedFile = StatementInclude.includeFile(fileName, parentFile);
		setFile(includedFile);

		super.parse(tree.getChild(0)); // Block parses statement
	}

	@Override
	public void typeCheckNotNull(SymbolTable symtab, CompilerMessages compilerMessages) {
		if (!isValidFileName(fileName)) compilerMessages.add(this, "include: Invalid file name '" + fileName + "'", MessageType.ERROR);
	}

}