/*-------------------------------------------------------------------------
 * drawElements Quality Program Test Executor
 * ------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Test log parser.
 *//*--------------------------------------------------------------------*/

#include "xeTestLogParser.hpp"
#include "deString.h"

using std::string;
using std::vector;
using std::map;

namespace xe
{

TestLogParser::TestLogParser (TestLogHandler* handler)
	: m_handler		(handler)
	, m_inSession	(false)
{
}

TestLogParser::~TestLogParser (void)
{
}

void TestLogParser::reset (void)
{
	m_containerParser.clear();
	m_currentCaseData.clear();
	m_sessionInfo	= SessionInfo();
	m_inSession		= false;
}

void TestLogParser::parse (const deUint8* bytes, size_t numBytes)
{
	m_containerParser.feed(bytes, numBytes);

	for (;;)
	{
		ContainerElement element = m_containerParser.getElement();

		if (element == CONTAINERELEMENT_INCOMPLETE)
			break;

		switch (element)
		{
			case CONTAINERELEMENT_BEGIN_SESSION:
			{
				if (m_inSession)
					throw Error("Unexpected #beginSession");

				m_handler->setSessionInfo(m_sessionInfo);
				m_inSession = true;
				break;
			}

			case CONTAINERELEMENT_END_SESSION:
			{
				if (!m_inSession)
					throw Error("Unexpected #endSession");

				m_inSession = false;
				break;
			}

			case CONTAINERELEMENT_SESSION_INFO:
			{
				if (m_inSession)
					throw Error("Unexpected #sessionInfo");

				const char*		attribute	= m_containerParser.getSessionInfoAttribute();
				const char*		value		= m_containerParser.getSessionInfoValue();

				if (deStringEqual(attribute, "releaseName"))
					m_sessionInfo.releaseName = value;
				else if (deStringEqual(attribute, "releaseId"))
					m_sessionInfo.releaseId = value;
				else if (deStringEqual(attribute, "targetName"))
					m_sessionInfo.targetName = value;
				else if (deStringEqual(attribute, "candyTargetName"))
					m_sessionInfo.candyTargetName = value;
				else if (deStringEqual(attribute, "configName"))
					m_sessionInfo.configName = value;
				else if (deStringEqual(attribute, "resultName"))
					m_sessionInfo.resultName = value;
				else if (deStringEqual(attribute, "timestamp"))
					m_sessionInfo.timestamp = value;

				// \todo [2012-06-09 pyry] What to do with unknown/duplicate attributes? Currently just ignored.
				break;
			}

			case CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT:
			{
				if (!m_inSession)
					throw Error("Unexpected #beginTestCaseResult");

				const char* casePath = m_containerParser.getTestCasePath();
				m_currentCaseData = m_handler->startTestCaseResult(casePath);

				// Clear and set to running state.
				m_currentCaseData->setDataSize(0);
				m_currentCaseData->setTestResult(TESTSTATUSCODE_RUNNING, "Running");

				m_handler->testCaseResultUpdated(m_currentCaseData);
				break;
			}

			case CONTAINERELEMENT_END_TEST_CASE_RESULT:
				if (m_currentCaseData)
				{
					// \todo [2012-06-16 pyry] Parse status code already here?
					m_currentCaseData->setTestResult(TESTSTATUSCODE_LAST, "");
					m_handler->testCaseResultComplete(m_currentCaseData);
				}
				m_currentCaseData.clear();
				break;

			case CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT:
				if (m_currentCaseData)
				{
					TestStatusCode	statusCode	= TESTSTATUSCODE_CRASH;
					const char*		reason		= m_containerParser.getTerminateReason();
					try
					{
						statusCode = getTestStatusCode(reason);
					}
					catch (const xe::ParseError&)
					{
						// Could not map status code.
					}
					m_currentCaseData->setTestResult(statusCode, reason);
					m_handler->testCaseResultComplete(m_currentCaseData);
				}
				m_currentCaseData.clear();
				break;

			case CONTAINERELEMENT_END_OF_STRING:
				if (m_currentCaseData)
				{
					// Terminate current case.
					m_currentCaseData->setTestResult(TESTSTATUSCODE_TERMINATED, "Unexpected end of string");
					m_handler->testCaseResultComplete(m_currentCaseData);
				}
				m_currentCaseData.clear();
				break;

			case CONTAINERELEMENT_TEST_LOG_DATA:
				if (m_currentCaseData)
				{
					int offset			= m_currentCaseData->getDataSize();
					int	numDataBytes	= m_containerParser.getDataSize();

					m_currentCaseData->setDataSize(offset+numDataBytes);
					m_containerParser.getData(m_currentCaseData->getData()+offset, numDataBytes, 0);

					m_handler->testCaseResultUpdated(m_currentCaseData);
				}
				break;

			default:
				throw ContainerParseError("Unknown container element");
		}

		m_containerParser.advance();
	}
}

} // xe