/** @file
 
 Copyright (c) 2006, Intel Corporation
 All rights reserved. This program and the accompanying materials
 are licensed and made available under the terms and conditions of the BSD License
 which accompanies this distribution.  The full text of the license may be found at
 http://opensource.org/licenses/bsd-license.php
 
 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 
 **/
package org.tianocore.migration;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.StringReader;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.tianocore.FilenameDocument;
import org.tianocore.ModuleSurfaceAreaDocument;
import org.tianocore.MsaHeaderDocument;
import org.tianocore.SourceFilesDocument;

public final class ModuleReader implements Common.ForDoAll {
	private static final ModuleReader modulereader = new ModuleReader();

	private ModuleInfo mi;

	private final CommentLaplace commentlaplace = new CommentLaplace();

	private static final Pattern ptninfequation = Pattern
			.compile("([^\\s]*)\\s*=\\s*([^\\s]*)");

	private static final Pattern ptnsection = Pattern.compile(
			"\\[([^\\[\\]]*)\\]([^\\[\\]]*)\\n", Pattern.MULTILINE);

	private static final Pattern ptnfilename = Pattern.compile("[^\\s]+");

	public final void ModuleScan() throws Exception {
		Common.toDoAll(mi.modulepath, ModuleInfo.class.getMethod("enroll",
				String.class), mi, null, Common.FILE);

		// inf&msa
		String filename = null;
		if (mi.msaorinf.isEmpty()) {
			MigrationTool.ui.println("No INF nor MSA file found!");
			System.exit(0);
		} else {
			if (mi.msaorinf.size() == 1) {
				filename = (String) mi.msaorinf.toArray()[0];
			} else {
				filename = MigrationTool.ui.choose(
						"Found .inf or .msa file for module\n" + mi.modulepath
								+ "\nChoose one Please", mi.msaorinf.toArray());
			}
		}

		if (filename.contains(".inf")) {
			readInf(filename);
		} else if (filename.contains(".msa")) {
			readMsa(filename);
		}
		// inf&msa

		preProcessModule();
	}

	private final void readMsa(String name) throws Exception {
		ModuleSurfaceAreaDocument msadoc = ModuleSurfaceAreaDocument.Factory
				.parse(new File(mi.modulepath + File.separator + name));
		ModuleSurfaceAreaDocument.ModuleSurfaceArea msa = msadoc
				.getModuleSurfaceArea();
		MsaHeaderDocument.MsaHeader msaheader = msa.getMsaHeader();

		mi.modulename = msaheader.getModuleName();
		mi.guidvalue = msaheader.getGuidValue();
		mi.moduletype = msaheader.getModuleType().toString(); // ???

		SourceFilesDocument.SourceFiles sourcefiles = msa.getSourceFiles();

		String temp;
		Iterator<FilenameDocument.Filename> li = sourcefiles.getFilenameList()
				.iterator();
		while (li.hasNext()) {
			if (!mi.localmodulesources.contains(temp = li.next().toString())) {
				System.out.println("Source File Missing! : " + temp);
			}
		}
	}

	private final String extractLicense(String wholeline) throws Exception {
		String tempLine;
		String license = null;

		BufferedReader rd = new BufferedReader(new StringReader(wholeline));
		while ((tempLine = rd.readLine()) != null) {
			if (tempLine.contains("#")) {
				if (tempLine.contains("Copyright")) {
					//
					// Find license info.
					// 
					license = "";
					while ((tempLine = rd.readLine()) != null) {
						if (!tempLine.contains("#")
								|| tempLine.contains("Module Name:")
								|| tempLine.contains("Abstract:")) {
							//
							// We assume license ends here.
							// 
							break;
						}
						license += "      "
								+ tempLine
										.replaceAll("\\s*[#]\\s*(.*)", "$1\n");
					}
					break;
				}
			}
		}
		return license;
	}

	private final void readInf(String name) throws Exception {
		System.out.println("\nParsing INF file: " + name);
		String wholeline;
		Matcher mtrinfequation;
		Matcher mtrsection;
		Matcher mtrfilename;

		wholeline = Common.file2string(mi.modulepath + File.separator + name);
		mi.license = extractLicense(wholeline);
		mtrsection = ptnsection.matcher(wholeline);
		while (mtrsection.find()) {
			if (mtrsection.group(1).matches("defines")) {
				mtrinfequation = ptninfequation.matcher(mtrsection.group(2));
				while (mtrinfequation.find()) {
					if (mtrinfequation.group(1).matches("BASE_NAME")) {
						mi.modulename = mtrinfequation.group(2);
					}
					if (mtrinfequation.group(1).matches("FILE_GUID")) {
						mi.guidvalue = mtrinfequation.group(2);
					}
					if (mtrinfequation.group(1).matches("COMPONENT_TYPE")) {
						mi.moduletype = mtrinfequation.group(2);
						if (mi.moduletype.matches("LIBRARY")) {
							mi.isLibrary = true;
						}
					}
				}
			}
			if (mtrsection.group(1).contains("nmake.")) {
				mtrinfequation = ptninfequation.matcher(mtrsection.group(2));
				while (mtrinfequation.find()) {
					if (mtrinfequation.group(1).matches("IMAGE_ENTRY_POINT")) {
						mi.entrypoint = mtrinfequation.group(2);
					}
					if (mtrinfequation.group(1).matches("DPX_SOURCE")) {
						if (!mi.localmodulesources.contains(mtrinfequation
								.group(2))) {
							MigrationTool.ui.println("DPX File Missing! : "
									+ mtrinfequation.group(2));
						}
					}
				}
			}
			if (mtrsection.group(1).contains("sources.")) {
				mtrfilename = ptnfilename.matcher(mtrsection.group(2));
				while (mtrfilename.find()) {
					mi.infsources.add(mtrfilename.group());
					if (!mi.localmodulesources.contains(mtrfilename.group())) {
						MigrationTool.ui
								.println("Warn: Source File Missing! : "
										+ mtrfilename.group());
					}
				}
			}
			if (mtrsection.group(1).matches("includes.")) {
				mtrfilename = ptnfilename.matcher(mtrsection.group(2));
				while (mtrfilename.find()) {
					mi.infincludes.add(mtrfilename.group());
				}
			}
		}
	}

	private final void preProcessModule() throws Exception {
		// according to .inf file, add extraordinary includes and sourcefiles
		Common.dirCopy(mi.modulepath, mi.temppath); // collect all
													// Laplace.namechange to
													// here???

		if (!mi.infincludes.isEmpty()) {
			Iterator<String> it = mi.infincludes.iterator();
			String tempincludename = null;
			while (it.hasNext()) {
				tempincludename = it.next();
				if (tempincludename.contains("..")) {
					Matcher mtr = Common.PTNSEPARATER.matcher(tempincludename);
					if (mtr.find() && !mtr.group(2).matches(".")) {
						Common.oneLevelDirCopy(mi.modulepath.replaceAll(
								Common.STRSEPARATER, "$1")
								+ File.separator + mtr.group(2), mi.temppath,
								".h");
					} else {
						Common.oneLevelDirCopy(mi.modulepath.replaceAll(
								Common.STRSEPARATER, "$1"), mi.temppath, ".h");
					}
				}
			}
		}
		if (!mi.infsources.isEmpty()) {
			Iterator<String> it = mi.infsources.iterator();
			String tempsourcename = null;
			while (it.hasNext()) {
				tempsourcename = it.next();
				if (tempsourcename.contains("..")) {
					Common.ensureDir(mi.temppath + File.separator
							+ "MT_Parent_Sources");
					Matcher mtr = Common.PTNSEPARATER.matcher(tempsourcename);
					if (mtr.find()) {
						Common.fileCopy(mi.modulepath.replaceAll(
								Common.STRSEPARATER, "$1")
								+ File.separator + mtr.group(2), mi.temppath
								+ File.separator + "MT_Parent_Sources"
								+ File.separator + mtr.group(2));
					}
				}
			}
		}

		Common.toDoAll(mi.temppath, this, Common.FILE);

		parsePreProcessedSourceCode();

	}

	private final void parsePreProcessedSourceCode() throws Exception {
		BufferedReader rd = null;
		String ifile = null;
		String line = null;
		String temp = null;

		Iterator<String> ii = mi.localmodulesources.iterator();
		while (ii.hasNext()) {
			temp = ii.next();
			if (temp.contains(".c") || temp.contains(".dxs")) {
				mi.preprocessedccodes.add(temp);
			}
		}

		ii = mi.preprocessedccodes.iterator();

		Pattern patefifuncc = Pattern.compile(
				"g?(BS|RT)\\s*->\\s*([a-zA-Z_]\\w*)", Pattern.MULTILINE);
		Matcher matguid;
		Matcher matfuncc;
		Matcher matfuncd;
		Matcher matenclosereplace;
		Matcher matefifuncc;
		Matcher matmacro;

		while (ii.hasNext()) {
			StringBuffer wholefile = new StringBuffer();
			ifile = ii.next();
			rd = new BufferedReader(new FileReader(mi.temppath + File.separator
					+ ifile));
			while ((line = rd.readLine()) != null) {
				wholefile.append(line + '\n');
			}
			line = wholefile.toString();

			// find guid
			matguid = Guid.ptnguid.matcher(line); // several ways to implement
													// this , which one is
													// faster ? :
			while (matguid.find()) { // 1.currently , find once , then call
										// to identify which is it
				if ((temp = Guid.register(matguid, mi, MigrationTool.db)) != null) { // 2.use
																						// 3
																						// different
																						// matchers
																						// ,
																						// search
																						// 3
																						// times
																						// to
																						// find
																						// each
					// matguid.appendReplacement(result,
					// MigrationTool.db.getR9Guidname(temp)); // search the
					// database for all 3 kinds of guids , high cost
				}
			}
			// matguid.appendTail(result);
			// line = result.toString();

			// find EFI call in form of '->' , many
			// 'gUnicodeCollationInterface->' like things are not changed
			// This item is not simply replaced , special operation is required.
			matefifuncc = patefifuncc.matcher(line);
			while (matefifuncc.find()) {
				mi.hashEFIcall.add(matefifuncc.group(2));
			}

			// find function call
			matfuncc = Func.ptnfuncc.matcher(line);
			while (matfuncc.find()) {
				if ((temp = Func.register(matfuncc, mi, MigrationTool.db)) != null) {
					// MigrationTool.ui.println(ifile + " dofunc " + temp);
					// matfuncc.appendReplacement(result,
					// MigrationTool.db.getR9Func(temp));
				}
			}
			// matfuncc.appendTail(result);
			// line = result.toString();

			// find macro
			matmacro = Macro.ptntmacro.matcher(line);
			while (matmacro.find()) {
				if ((temp = Macro.register(matmacro, mi, MigrationTool.db)) != null) {
				}
			}

			// find function definition
			// replace all {} to @
			while ((matenclosereplace = Func.ptnbrace.matcher(line)).find()) {
				line = matenclosereplace.replaceAll("@");
			}

			matfuncd = Func.ptnfuncd.matcher(line);
			while (matfuncd.find()) {
				if ((temp = Func.register(matfuncd, mi, MigrationTool.db)) != null) {
				}
			}
		}

		// op on hash
		Iterator<String> funcci = mi.hashfuncc.iterator();
		while (funcci.hasNext()) {
			if (!mi.hashfuncd.contains(temp = funcci.next())
					&& !mi.hashEFIcall.contains(temp)) {
				mi.hashnonlocalfunc.add(temp); // this set contains both
												// changed and not changed items
			}
		}
	}

	public class CommentLaplace extends Common.Laplace {
		public String operation(String wholeline) {
			StringBuffer wholebuffer = new StringBuffer();
			String templine = null;
			Pattern ptnincludefile = Pattern.compile("[\"<](.*[.]h)[\">]");
			Pattern ptninclude = Pattern.compile("#include\\s*(.*)");
			Matcher mtrinclude = ptninclude.matcher(wholeline);
			Matcher mtrincludefile = null;
			while (mtrinclude.find()) {
				mtrincludefile = ptnincludefile.matcher(mtrinclude.group(1));
				if (mtrincludefile.find()
						&& mi.localmodulesources.contains(mtrincludefile
								.group(1))) {
					templine = mtrinclude.group();
				} else {
                    String line = mtrinclude.group().toLowerCase();
                    if (line.contains("pal.h")) {
                        templine = "#include <IndustryStandard/Pal.h>\n";
                    } else if (line.contains("sal.h")) {
                        templine = "#include <IndustryStandard/Sal.h>\n";
                    } else if (line.contains("pci22.h")) {
                        templine = "#include <IndustryStandard/Pci22.h>\n";
                    } else if (line.contains("pci23.h")) {
                        templine = "#include <IndustryStandard/Pci23.h>\n";
                    } else if (line.contains("pci30.h")) {
                        templine = "#include <IndustryStandard/Pci30.h>\n";
                    } else if (line.contains("pci.h")) {
                        templine = "#include <IndustryStandard/Pci.h>\n";
                    } else if (line.contains("acpi.h")) {
                        templine = "#include <IndustryStandard/Acpi.h>\n";
                    } else if (line.contains("scsi.h")) {
                        templine = "#include <IndustryStandard/Scsi.h>\n";
                    } else if (line.contains("usb.h")) {
                        templine = "#include <IndustryStandard/Usb.h>\n";
                    } else {
                        templine = MigrationTool.MIGRATIONCOMMENT
                                + mtrinclude.group();
                    }
                }
                mtrinclude.appendReplacement(wholebuffer, templine);
            }
			mtrinclude.appendTail(wholebuffer);
			return wholebuffer.toString();
		}

		public boolean recognize(String filename) {
			return filename.contains(".c") || filename.contains(".h")
					|| filename.contains(".dxs");
		}

		public String namechange(String oldname) {
			return oldname;
		}
	}

	// -----------------------------------ForDoAll-----------------------------------//
	public void run(String filepath) throws Exception {
		String name = mi.temppath + File.separator
				+ filepath.replace(mi.temppath + File.separator, "");
		if (commentlaplace.recognize(name)) {
			commentlaplace.transform(name, name);
		}
	}

	public boolean filter(File dir) {
		return true;
	}

	// -----------------------------------ForDoAll-----------------------------------//

	public final void setModuleInfo(ModuleInfo m) {
		mi = m;
	}

	public static final void aimAt(ModuleInfo mi) throws Exception {
		modulereader.setModuleInfo(mi);
		modulereader.ModuleScan();
	}
}
