#include "SimpleSerialAnalyzerResults.h"
#include <AnalyzerHelpers.h>
#include "SimpleSerialAnalyzer.h"
#include "SimpleSerialAnalyzerSettings.h"
#include <iostream>
#include <fstream>

SimpleSerialAnalyzerResults::SimpleSerialAnalyzerResults( SimpleSerialAnalyzer* analyzer, SimpleSerialAnalyzerSettings* settings )
:	AnalyzerResults(),
	mSettings( settings ),
	mAnalyzer( analyzer )
{
}

SimpleSerialAnalyzerResults::~SimpleSerialAnalyzerResults()
{
}

#ifndef NEW_SDK
	#define AddTabularText(...)	do { } while(0)
	#define ClearTabularText(...)	do { } while(0)
#endif

static const char* getFuncNameFromBit(U32 bit) {

	const char * funcNm;

	switch (bit) {
		case FUNC_CONTROLLER:
			funcNm = "controller";
			break;
		case FUNC_MEMORY:
			funcNm = "memory";
			break;
		case FUNC_LCD:
			funcNm = "LCD";
			break;
		case FUNC_CLOCK:
			funcNm = "clock";
			break;
		case FUNC_MICROPHONE:
			funcNm = "microphone";
			break;
		case FUNC_AR_GUN:
			funcNm = "AR Gun";
			break;
		case FUNC_KEYBOARD:
			funcNm = "keyboard";
			break;
		case FUNC_LIGHT_GUN:
			funcNm = "Light Gun";
			break;
		case FUNC_VIBRATION_PACK:
			funcNm = "Vibra Pack";
			break;
		case FUNC_MOUSE:
			funcNm = "mouse";
			break;
		default:
			funcNm = NULL;
	}

	return funcNm;
}
static const char *getCmdName(U8 cmd)
{
	switch (cmd) {
		case CMD_REQ_DEV_NFO:
			return "Request device information";

		case CMD_REX_ETD_NFO:
			return "Request extended device information";

		case CMD_RST_DEV:
			return "Reset device";

		case CMD_SHTDWN_DEV:
			return "Shutdown device";

		case RSP_DEV_NFO:
			return "Device information";

		case RSP_EXTD_NFO:
			return "Extended device information";

		case RSP_CMD_ACK:
			return "Command ACK";

		case RSP_DATA_XFER:
			return "Data Xfer";

		case CMD_GET_COND:
			return "Get condition";

		case CMD_GET_MEM_NFO:
			return "Get mem info";

		case CMD_BLK_RD:
			return "Block Read";

		case CMD_BLK_WR:
			return "Block Write";

		case CMD_FLSH_ICON_OFF:
			return "Turn off Flash Icon";

		case CMD_SET_COND:
			return "Set Condition";

		case CMD_ERROR_CODE:
			return "ERR: see err code";

		case CMD_ERROR_PLZ_RESEND_PACKET:
			return "Please resend packet";

		case CMD_ERROR_CMD_UNKNOWN:
			return "ERR: unknown pkt";

		case CMD_ERROR_FUNC_UNKNOWN:
			return "ERR: unknown func #";

		default:
			return NULL;
	}
}

void SimpleSerialAnalyzerResults::GenerateText( U64 frame_index, Channel* channel /* can be NULL */, DisplayBase display_base, bool forTabular)
{
	Frame frame = GetFrame( frame_index );

	static const char *funcNm;
	char number_str[1024] = {0,};
	U8 cmd = 0, numChars = 4;
	int i;

	switch(frame.mType) {
		case FRAME_TYPE_RAW_BIT:
			sprintf(number_str, "%cp%u", frame.mData2 == 0 ? '?' : (char)frame.mData1 + '0', (unsigned)frame.mData2);
			
			if(forTabular) {
				AddTabularText( number_str );
				break;
			}
			AddResultString( number_str );
			break;

		case FRAME_TYPE_START:
			if(forTabular) {
				AddTabularText( "START" );
				break;
			}
			AddResultString( "START" );
			AddResultString( "S" );
			break;
		case FRAME_TYPE_STOP:
			if(forTabular) {
				AddTabularText( "STOP" );
				break;
			}
			AddResultString( "STOP" );
			AddResultString( "P" );
			break;
		case FRAME_TYPE_BYTE:
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 8, number_str, sizeof(number_str) );
			if(forTabular)
				AddTabularText( number_str );
			else
				AddResultString( number_str );
			break;

		case FRAME_TYPE_WORD:
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 32, number_str, sizeof(number_str) );
			if(forTabular)
				AddTabularText( number_str );
			else
				AddResultString( number_str );
			break;

		case FRAME_TYPE_INCOMPL_WORD:
			sprintf(number_str, "Incomplete word (%u bytes): ", (unsigned)frame.mData2);
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, frame.mData2 * 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			if(forTabular) {
				AddTabularText( number_str );
				break;
			}
			AddResultString( number_str );
			strcpy(number_str, "INCOMPL ");
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, frame.mData2 * 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			AddResultString( number_str );
			break;

		case FRAME_TYPE_CRC:
			if (frame.mData1 == frame.mData2) {
				strcpy(number_str, "CRC ");
				AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
				if(forTabular) {
					AddTabularText( number_str );
					break;
				}
				AddResultString( number_str );

				AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 8, number_str, sizeof(number_str) );
				AddResultString( number_str );
				AddResultString( "CRC" );
			}
			else {
				AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 8, number_str, sizeof(number_str) );
				strcat(number_str, " != ");
				AnalyzerHelpers::GetNumberString( frame.mData2, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
				if(forTabular) {
					AddTabularText( number_str );
					break;
				}
				AddResultString( number_str );
				AddResultString( "CRC ERR" );
			}
			break;
			
		case FRAME_TYPE_HEADER:
			cmd = (U8)(frame.mData1 >> 24);

			strcpy(number_str, "CMD ");
			AnalyzerHelpers::GetNumberString( cmd, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));

			if (getCmdName(cmd)) {
				strcat(number_str, " (");
				strcat(number_str, getCmdName(cmd));
				strcat(number_str, ")");
			}
			strcat(number_str, " To: ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 16) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));

			strcat(number_str, " From: ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 8) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));

			strcat(number_str, " Datawords: ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 0) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			
			if(forTabular) {
				AddTabularText( number_str );
				break;
			}
			AddResultString( number_str );

			//shorter
			strcpy(number_str, "C ");
			AnalyzerHelpers::GetNumberString( cmd, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));

			if (getCmdName(cmd)) {
				strcat(number_str, " (");
				strcat(number_str, getCmdName(cmd));
				strcat(number_str, ")");
			}
			strcat(number_str, " ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 8) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, "->");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 16) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, " L:");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 0) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			
			AddResultString( number_str );

			//shorter
			strcpy(number_str, "[");
			AnalyzerHelpers::GetNumberString( cmd, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, "] ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 8) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, "->");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 16) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ", ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 0) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			AddResultString( number_str );

			break;

		case FRAME_TYPE_FUNC:
			funcNm = getFuncNameFromBit(frame.mData1);
			if (!funcNm)
					funcNm = "Unknown Function";
			
			sprintf(number_str, "func '%s' (", funcNm);
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 32, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ")");
			if(forTabular) {
				AddTabularText( number_str );
				break;
			}
			AddResultString( number_str );

			AddResultString( funcNm );

			sprintf(number_str, "f=", funcNm);
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 32, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			AddResultString( number_str );
			break;

		case FRAME_TYPE_FUNCS_ORRED:
			strcpy(number_str, "funcs ");
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 32, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));

			if (!forTabular)		//only add short if multiple allowed
				AddResultString( number_str );

			strcat(number_str, ": ");

			while (frame.mData1) {
				
				U32 bit = frame.mData1 &~ (frame.mData1 - 1);	//get lowest bit

				frame.mData1 &=~ bit;							//clear bit

				funcNm = getFuncNameFromBit(bit);

				if (funcNm)
					strcat(number_str, funcNm);
				else {
					strcat(number_str, "UNK(");
					AnalyzerHelpers::GetNumberString( bit, display_base, 32, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
					strcat(number_str, ")");
				}

				if (frame.mData1)
					strcat(number_str, ", ");
			}

			if(forTabular)
				AddTabularText( number_str );
			else
				AddResultString( number_str );

			break;

		case FRAME_TYPE_2b_AND_2c:
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 24) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ", ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 16) & 0xFF, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ", ");
			numChars = 2;
			frame.mData1 <<= 16;
			//fallthrough

		case FRAME_TYPE_4c:
			AnalyzerHelpers::GetNumberString( (frame.mData1 & 0xFFFFFFFF) >> ((4 - numChars) * 8), display_base, numChars * 4, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, " ('");
			for (i = 0; i < numChars; i++, frame.mData1 <<= 8)
				sprintf(number_str + strlen(number_str), "%c", (frame.mData1 >> 24) & 0xFF);
			strcat(number_str, "')");

			if(forTabular)
				AddTabularText( number_str );
			else
				AddResultString( number_str );
			break;

		case FRAME_TYPE_2_LE_s:
			AnalyzerHelpers::GetNumberString( frame.mData1, display_base, numChars * 4, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, " {");
			i = (frame.mData1 >> 16) & 0xFFFF;
			i = (i << 8) | (i >> 8);
			i &= 0xFFFF;
			AnalyzerHelpers::GetNumberString( i, display_base, numChars * 4, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ",");
			i = frame.mData1 & 0xFFFF;
			i = (i << 8) | (i >> 8);
			i &= 0xFFFF;
			AnalyzerHelpers::GetNumberString( i, display_base, numChars * 4, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, "}");

			if(forTabular)
				AddTabularText( number_str );
			else
				AddResultString( number_str );

			break;

		case FRAME_TYPE_PT_PH_BLK:
			strcpy(number_str, "pt ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 24) & 0xff, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ", phase ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 16) & 0xff, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ", block ");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 0) & 0xffff, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			if(forTabular) {
				AddTabularText( number_str );
				break;
			}
			AddResultString( number_str );

			strcpy(number_str, "pt=");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 24) & 0xff, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, ",ph=");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 16) & 0xff, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			strcat(number_str, " blk=");
			AnalyzerHelpers::GetNumberString( (frame.mData1 >> 0) & 0xffff, display_base, 8, number_str + strlen(number_str), sizeof(number_str) - strlen(number_str));
			AddResultString( number_str );
			break;

		default:
			sprintf(number_str, "Unknown result type %u\n", frame.mType);
			if(forTabular) {
				AddTabularText( number_str );
				break;
			}
			AddResultString( number_str );

			//shorter
			AddResultString( "???" );
			break;
	}
}

void SimpleSerialAnalyzerResults::GenerateBubbleText( U64 frame_index, Channel& channel, DisplayBase display_base )
{
	ClearResultStrings();
	GenerateText(frame_index, &channel, display_base, false);
}

void SimpleSerialAnalyzerResults::GenerateExportFile( const char* file, DisplayBase display_base, U32 export_type_user_id )
{
	//todo
}

void SimpleSerialAnalyzerResults::GenerateFrameTabularText( U64 frame_index, DisplayBase display_base )
{
	ClearTabularText();
	GenerateText(frame_index, NULL, display_base, true);
}

void SimpleSerialAnalyzerResults::GeneratePacketTabularText( U64 packet_id, DisplayBase display_base )
{
	ClearResultStrings();
	AddResultString( "not supported" );
}

void SimpleSerialAnalyzerResults::GenerateTransactionTabularText( U64 transaction_id, DisplayBase display_base )
{
	ClearResultStrings();
	AddResultString( "not supported" );
}