/*
* Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


/**
 @file
 @internalComponent 
*/
#include <e32std.h>
#include <e32base.h>
#include <badesca.h>
#include <txtglobl.h>
#include <frmtview.h>
#include <txtfmlyr.h>
#include <txtrich.h>
#include <frmtlay.h>

#include <coemain.h>
#include <coeaui.h>

#include <e32test.h>
#include "bandtestfileoutput.h"
#include "bandvalidator.h"
#include "testenvironment.h"
#include "richcontrol.h"

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include "TAGMA_INTERNAL.H"
#endif

EXPORT_C CBandValidator* CBandValidator::NewLC(const CTextView& aTestView, const CTextView& aReferenceView, CFileOutput& aOutput)
	{
	CBandValidator* self = new(ELeave) CBandValidator(aTestView, aReferenceView, aOutput);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

CBandValidator::CBandValidator(const CTextView& aTestView, const CTextView& aReferenceView, CFileOutput& aOutput)
	:iFileOutput(aOutput), iTestView(aTestView), iReferenceView(aReferenceView)
	{
	}
	
void CBandValidator::ConstructL()
	{
	iFailLineNumberInPara = -1;
	iFailLineNumber = -1;
	}

EXPORT_C CBandValidator::~CBandValidator()
	{
	}

TBool CBandValidator::LineInfosAreEqualL( TTmLineInfo& aLineUnderTest,  TTmLineInfo& aLineReference)
	{
	return (aLineUnderTest.iFlags == aLineReference.iFlags);
	}
	
EXPORT_C TBool CBandValidator::ValidateL(const TDes& aDesc)
   	{
	iFailLineNumberInPara = -1;
	iFailLineNumber = -1;
   //get line info and text contents for the test view
   	RArray<TTmLineInfo> infoUnderTest;
   	CleanupClosePushL(infoUnderTest);
   	if(!(GetVisibleLineInfosL(iTestView, infoUnderTest)))
   		{
RDebug::Printf("fail: can't find test lines");

   	  	iFileOutput.WriteOperationDetails(aDesc);
  		CleanupStack::PopAndDestroy();
   		return EFalse;
   		}
   	
   	CDesCArrayFlat* contentsUnderTest = new(ELeave) CDesCArrayFlat (10);
   	CleanupStack::PushL(contentsUnderTest);
   	GetLineContentsL(iTestView, infoUnderTest, *contentsUnderTest);
  	
  //get line info and text contents for the reference view	
  	RArray<TTmLineInfo> infoReference;
  	CleanupClosePushL(infoReference);
  	if(!(GetReferenceLineInfosL(infoUnderTest, infoReference)))
  		{
RDebug::Printf("fail: can't find matching reference lines infos");
  	  	iFileOutput.WriteOperationDetails(aDesc);
  		CleanupStack::PopAndDestroy(3);
  		return EFalse;
  		}
   
  	CDesCArrayFlat* contentsReference = new(ELeave) CDesCArrayFlat (10);
  	CleanupStack::PushL(contentsReference);
  	GetLineContentsL(iReferenceView, infoReference, *contentsReference);

  	TBool lineCountDiff = EFalse;
	TInt lineCountForTest = infoUnderTest.Count()<=infoReference.Count()? infoUnderTest.Count(): infoReference.Count();
//now validate the line infos
	if(!(infoUnderTest.Count() == infoReference.Count()))
		{
		lineCountDiff = ETrue;
		RDebug::Printf("fail: test line count differs from reference line count");
		}	
  	TBool lineInfoDiff = EFalse;
	for (TInt i = 0; i < lineCountForTest; i++)
		{
		if(!(LineInfosAreEqualL(infoUnderTest[i], infoReference[i])))
			{
			lineInfoDiff = ETrue;
RDebug::Printf("fail: flag mismatch for line number %d: test flags:%d, reference flags %d", i, infoUnderTest[i].iFlags, infoReference[i].iFlags);
		  	//write out the test text contents, and the reference text contents
  			iFileOutput.WriteOperationDetails(aDesc);
  			iFileOutput.WriteVisibleTestLinesL(*contentsUnderTest);
  			iFileOutput.WriteVisibleReferenceLinesL(*contentsReference);
  			break;
			}
		}
	for (TInt i = 0; i < lineCountForTest; i++)
		{
		TPtrC lineUnderTest = (*contentsUnderTest)[i];
		TPtrC lineReference = (*contentsReference)[i];
		if(!(lineUnderTest==lineReference))
			{
			iFailLineNumberInPara = infoUnderTest[i].iLineInPar;
			iFailLineNumber = infoUnderTest[i].iLineNumber;
RDebug::Printf("fail:line contents mismatch: test line: %S, reference line: %S", &lineUnderTest, &lineReference);
		  	//write out the test text contents, and the reference text contents
  			iFileOutput.WriteOperationDetails(aDesc);
  			iFileOutput.WriteVisibleTestLinesL(*contentsUnderTest);
  			iFileOutput.WriteVisibleReferenceLinesL(*contentsReference);
			CleanupStack::PopAndDestroy(4);
			return EFalse;
			}
		}
	CleanupStack::PopAndDestroy(4);
	if (lineCountDiff || lineInfoDiff)
		{
		return EFalse;
		}
	return ETrue;
	}
	
TBool CBandValidator::GetVisibleLineInfosL(const CTextView& aView, RArray<TTmLineInfo>& aVisibleLines)
	{
	const CTextLayout* layout = aView.Layout();
	__ASSERT_ALWAYS(layout, User::Invariant());
	const CTmTextLayout& tmLayout = layout->TagmaTextLayout();
//get first visible line
//this function usefully returns 0 if the line is not found...that might be a problem
	TInt firstLineNumber = 0;
	TTmLineInfo startLine;	
	if(!(tmLayout.LineNumberToLine(firstLineNumber, startLine)))
		{
		return EFalse;
		}
//get last visible line
	TPoint bottom(0, (aView.ViewRect().Height()-1));//-1 is a not nice.
	TTmPosInfo2 endPosInfo;
	TTmLineInfo endLine;	
	if(!(layout->FindXyPos(bottom, endPosInfo, &endLine)))
		{
		//if we can't find any formatting at the bottom of the view rectangle,
		//this may be because we're at the end of the document. 
		//Figure out if this is so
		if(tmLayout.EndChar() >= tmLayout.Source()->DocumentLength())
			{
			//it is, so just get the last formatted line
			if(!(tmLayout.LineNumberToLine((tmLayout.Lines())-1, endLine)))
				{
				return EFalse;
				}
			}
		else
			{
			return EFalse;
			}
		}
//add first, last, and everything in between
	User::LeaveIfError(aVisibleLines.Append(startLine));
	TInt lineNumber = startLine.iLineNumber+1;
	TInt lastLineNumber = endLine.iLineNumber;
	while (lineNumber <= lastLineNumber)
		{
		TTmLineInfo lineInfo;
		if(!(tmLayout.LineNumberToLine(lineNumber, lineInfo)))
			{
			return EFalse;
			}
		User::LeaveIfError(aVisibleLines.Append(lineInfo));
		lineNumber++;
		}
	return ETrue;	
	}
	
TBool CBandValidator::GetReferenceLineInfosL(const RArray<TTmLineInfo>& aVisibleLines, RArray<TTmLineInfo>& aReferenceLineInfos)
	{
	aReferenceLineInfos.Reset();
	TInt lineCount = aVisibleLines.Count();
	const CTmTextLayout& tmLayoutReference = iReferenceView.Layout()->TagmaTextLayout();	
	for (TInt i = 0; i < lineCount; i++)
		{
		TInt lineStartPos = aVisibleLines[i].iStart;
		TTmDocPos pos(lineStartPos, ETrue);
		TTmPosInfo2 ignore;
		TTmLineInfo referenceLine;
		if(!(tmLayoutReference.FindDocPos(pos, ignore, referenceLine)))
			{
			return EFalse;
			}
		User::LeaveIfError(aReferenceLineInfos.Append(referenceLine));
		}
	return ETrue;
	}
	
void CBandValidator::GetLineContentsL(const CTextView& aView, const RArray<TTmLineInfo>& aLineInfos, CDesCArray& aLineContents)
	{
//get text contents of test line
	const CTextLayout* layout = aView.Layout();
	__ASSERT_ALWAYS(layout, User::Invariant());
	const CTmTextLayout& tmTestLayout = layout->TagmaTextLayout();
	
	TInt lineCount = aLineInfos.Count();	
	for (TInt i = 0; i < lineCount; i++)
		{
		TBuf<256> lineContent;
		TInt needed = 0;		
		tmTestLayout.GetDisplayedTextL(aLineInfos[i].iLineNumber, lineContent, needed);
		__ASSERT_ALWAYS(needed <= lineContent.Length(), User::Invariant());
		aLineContents.AppendL(lineContent);
		}	
	}
	

