/*******************************************************************************
 * Copyright (c) 2011 Ericsson and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Ericsson - Initial implementation
 *******************************************************************************/
package org.eclipse.cdt.dsf.mi.service.command.output;

import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Parses the GDB "tdump" command printout, as returned by GDB, to make it a
 * bit more human-friendly.
 * <p>
 * See bottom of this file for a raw example of what is
 * returned by "tdump"
 * @since 4.0
 */
public class CLITraceDumpInfo extends MIInfo {

	// Here is what this pattern looks-for - the first line of the tdump printout:
	//~"Data collected at tracepoint 2, trace frame 555:\n"
	private static final Pattern RESULT_PATTERN_TPINFO = Pattern.compile(
			"Data collected at tracepoint (\\d+), trace frame (\\d+)",  Pattern.CASE_INSENSITIVE); //$NON-NLS-1$

	// raw output of command
	private String fOutput = null;
	private String fParsedOutput = null;

	// tdump parsed info
	private String fTracepointNum = null;
	private String fTraceFrameNumber = null;
	/* 
	 * Timestamp, if present in tracepoint frame data
	 * Note: not yet available in printout of command
	 * "tdump" -> revisit when it is.
	 */
	private String timestamp = null;
	// keep the tdump header in parsed result or not - by default we keep
	private static final boolean KEEP_HEADER = true;

	/**
	 * Constructor.
	 * @param out the output of the tdump printout
	 */
	public CLITraceDumpInfo(MIOutput out) {
		super(out);
		parse(KEEP_HEADER);
	}
	
	/**
	 * Alternative constructor.  Use this one to have control if the tdump
	 * header is kept or not in the result.  
	 * @param out the output of the tdump printout
	 * @param keepHeader keep the tdump header in result or not
	 */
	public CLITraceDumpInfo(MIOutput out, boolean keepHeader) {
		super(out);
		parse(keepHeader);
	}

	
	/**
	 * Do a quick parse of the tdump printout.  The tdump command printout is 
	 * split in short pieces (records), each one wrapped like this:
	 * <p>
	 * ~"eax            0x10"
	 * <p>
	 * Also, tabs and newlines are represented by symbols: \n and \t . 
	 * <p> 
	 * In this method, we strip the wrapping off each record and translate the 
	 * symbols to their value.   The resulting string is not parsed further.
	 * <p>
	 * See an example of a tdump printout at the end of this file.
	 */
	private void parse(boolean keepHeader) {
		final Pattern RESULT_PATTERN_UNWRAPRECORD = Pattern.compile("~\"(.*)\"",  Pattern.CANON_EQ); //$NON-NLS-1$
		StringBuffer buf = new StringBuffer();
		String unwrapped;
		if (isDone()) {
			MIOutput out = getMIOutput();
			// save raw output of command
			fOutput = out.toString();
			
			MIOOBRecord[] oobs = out.getMIOOBRecords();
			for (MIOOBRecord oob : oobs) {
				if (oob instanceof MIConsoleStreamOutput) {
					Matcher matcher = RESULT_PATTERN_UNWRAPRECORD.matcher(oob.toString());
					if (matcher.find()) {
						unwrapped = matcher.group(1);
						// accumulate the records into a buffer
						buf.append(unwrapped);
					}
				}
			}
			// convert buffer into string
			fParsedOutput = buf.toString();
			// extract the tracepoint and frame numbers
			Matcher matcher = RESULT_PATTERN_TPINFO.matcher(fParsedOutput);	
			if (matcher.find()) {
				fTracepointNum = matcher.group(1).trim();
				fTraceFrameNumber = matcher.group(2).trim();
			}

			// command result has the substrings "\n" and "\t" in it. 
			// replace them by their actual meaning (real newline and tab)
			fParsedOutput = fParsedOutput.replaceAll("\\\\n", "\n");   //$NON-NLS-1$ //$NON-NLS-2$
			fParsedOutput = fParsedOutput.replaceAll("\\\\t+", "\t");   //$NON-NLS-1$ //$NON-NLS-2$

			// Optionaly remove the header line from tdump printout 
			if(!keepHeader) {
				fParsedOutput = fParsedOutput.replaceFirst("Data collected at tracepoint \\d+, trace frame \\d+:\\n", "");   //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
	}


	/**
	 * @return the raw output of tdump.
	 */
	public String getOutput() {
		return fOutput;
	}

	/**
	 * @return a String containing the semi-parsed
	 * register and local variables, as listed by
	 * tdump.
	 */
	public String getContent() {
		return fParsedOutput;
	}
	
	/**
	 * @return the tracepoint number
	 */
	public String getTracepointNumber() {
		return fTracepointNum;
	}


	/**
	 * @return the trace's frame number
	 */
	public String getFrameNumber() {
		return fTraceFrameNumber;
	}

	/**
	 * @return the timestamp of the tracepoint frame
	 */
	public String getTimestamp() {
		return timestamp;
	}
}


/*  
 * Example of raw output from command tdump:

&"tdump\n"
~"Data collected at tracepoint 2, trace frame 555:\n"
~"eax            0x10"
~"\t16"
~"\n"
~"ecx            0x0"
~"\t0"
~"\n"
~"edx            0x0"
~"\t0"
~"\n"
~"ebx            0x11"
~"\t17"
~"\n"
~"esp            0xbfec14c0"
~"\t0xbfec14c0\n"
~"ebp            0xbfec1508"
~"\t0xbfec1508\n"
~"esi            0x0"
~"\t0"
~"\n"
~"edi            0x0"
~"\t0"
~"\n"
~"eip            0x8048520"
~"\t0x8048520 <factorial1(unsigned long long)+45>\n"
~"eflags         0x216"
~"\t[ PF AF IF ]"
~"\n"
~"cs             0x73"
~"\t115"
~"\n"
~"ss             0x7b"
~"\t123"
~"\n"
~"ds             0x7b"
~"\t123"
~"\n"
~"es             0x7b"
~"\t123"
~"\n"
~"fs             0x0"
~"\t0"
~"\n"
~"gs             0x33"
~"\t51"
~"\n"
~"st0            0"
~"\t(raw 0x00000000000000000000)\n"
~"st1            0"
~"\t(raw 0x00000000000000000000)\n"
~"st2            0"
~"\t(raw 0x00000000000000000000)\n"
~"st3            0"
~"\t(raw 0x00000000000000000000)\n"
~"st4            0"
~"\t(raw 0x00000000000000000000)\n"
~"st5            0"
~"\t(raw 0x00000000000000000000)\n"
~"st6            0"
~"\t(raw 0x00000000000000000000)\n"
~"st7            0"
~"\t(raw 0x00000000000000000000)\n"
~"fctrl          0x37f"
~"\t895"
~"\n"
~"fstat          0x0"
~"\t0"
~"\n"
~"ftag           0xffff"
~"\t65535"
~"\n"
~"fiseg          0x73"
~"\t115"
~"\n"
~"fioff          0x718d9c"
~"\t7441820"
~"\n"
~"foseg          0x7b"
~"\t123"
~"\n"
~"fooff          0x754928"
~"\t7686440"
~"\n"
~"fop            0x599"
~"\t1433"
~"\n"
~"xmm0           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"xmm1           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"xmm2           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"xmm3           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"xmm4           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"xmm5           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"xmm6           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"xmm7           {v4_float = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_double = {0x0"
~", 0x0"
~"}"
~", v16_int8 = {0x0"
~" <repeats 16 times>}"
~", v8_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v4_int32 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v2_int64 = {0x0"
~", 0x0"
~"}"
~", uint128 = 0x00000000000000000000000000000000"
~"}"
~"\n"
~"mxcsr          0x1f80"
~"\t[ IM DM ZM OM UM PM ]"
~"\n"
~"mm0            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"mm1            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"mm2            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"mm3            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"mm4            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"mm5            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"mm6            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"mm7            {uint64 = 0x0"
~", v2_int32 = {0x0"
~", 0x0"
~"}"
~", v4_int16 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~", v8_int8 = {0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~", 0x0"
~"}"
~"}"
~"\n"
~"a = 16"
~"\n"
27^done
 */
