/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 * 
 *************************************************************/



#ifndef _TAGTEST_HXX_
#define _TAGTEST_HXX_

#include <tools/string.hxx>
#include <tools/list.hxx>
#include <hash_map> /* std::hashmap*/
#include <rtl/string.h>

class GSILine;

typedef sal_uInt16 TokenId;

#define TOK_INVALIDPOS  sal_uInt16( 0xFFFF )

class ParserMessage;

DECLARE_LIST( Impl_ParserMessageList, ParserMessage* )
class ParserMessageList;


struct equalByteString{
        bool operator()( const ByteString& rKey1, const ByteString& rKey2 ) const {
            return rKey1.CompareTo( rKey2 )==COMPARE_EQUAL;
    }
};
struct lessByteString{
        bool operator()( const ByteString& rKey1, const ByteString& rKey2 ) const {
            return rKey1.CompareTo( rKey2 )==COMPARE_LESS;
    }
};

struct hashByteString{
    size_t operator()( const ByteString& rName ) const{
        return rtl_str_hashCode_WithLength( rName.GetBuffer(), rName.Len());
    }
};



typedef std::hash_map<ByteString , String , hashByteString,equalByteString>
                                StringHashMap;

class TokenInfo
{
private:
    void SplitTag( ParserMessageList &rErrorList );

    String aTagName;
    StringHashMap aProperties;
    sal_Bool bClosed;    // tag is closed  <sdnf/>
    sal_Bool bCloseTag;  // tag is close Tag  </sdnf>


    sal_Bool bIsBroken;
    sal_Bool bHasBeenFixed;
    sal_Bool bDone;

public:

	String aTokenString;
	TokenId nId;
    sal_uInt16 nPos;            // Position in String

    TokenInfo():bClosed(sal_False),bCloseTag(sal_False),bIsBroken(sal_False),bHasBeenFixed(sal_False),bDone(sal_False),nId( 0 ){;}
explicit    TokenInfo( TokenId pnId, sal_uInt16 nP ):bClosed(sal_False),bCloseTag(sal_False),bIsBroken(sal_False),bHasBeenFixed(sal_False),bDone(sal_False),nId( pnId ),nPos(nP){;}
explicit    TokenInfo( TokenId pnId, sal_uInt16 nP, String paStr ):bClosed(sal_False),bCloseTag(sal_False),bIsBroken(sal_False),bHasBeenFixed(sal_False),bDone(sal_False),aTokenString( paStr ),nId( pnId ),nPos(nP) {;}
explicit    TokenInfo( TokenId pnId, sal_uInt16 nP, String paStr, ParserMessageList &rErrorList );

	String GetTagName() const;

    String MakeTag() const;

    /**
        Is the property to be ignored or does it have the default value anyways
    **/
    sal_Bool IsPropertyRelevant( const ByteString &aName, const String &aValue ) const;
    sal_Bool IsPropertyValueValid( const ByteString &aName, const String &aValue ) const;
    /**
        Does the property contain the same value for all languages
        e.g.: the href in a link tag
    **/
    sal_Bool IsPropertyInvariant( const ByteString &aName, const String &aValue ) const;
    /**
        a subset of IsPropertyInvariant but containing only those that are fixable
        we dont wat to fix e.g.: ahelp :: visibility
    **/
    sal_Bool IsPropertyFixable( const ByteString &aName ) const;
    sal_Bool MatchesTranslation( TokenInfo& rInfo, sal_Bool bGenErrors, ParserMessageList &rErrorList, sal_Bool bFixTags = sal_False ) const;

    sal_Bool IsDone() const { return bDone; }
    void SetDone( sal_Bool bNew = sal_True ) { bDone = bNew; }

    sal_Bool HasBeenFixed() const { return bHasBeenFixed; }
    void SetHasBeenFixed( sal_Bool bNew = sal_True ) { bHasBeenFixed = bNew; }
};


class ParserMessageList : public Impl_ParserMessageList
{
public:
    void AddError( sal_uInt16 nErrorNr, ByteString aErrorText, const TokenInfo &rTag );
    void AddWarning( sal_uInt16 nErrorNr, ByteString aErrorText, const TokenInfo &rTag );

    sal_Bool HasErrors();
};


#define TAG_GROUPMASK				0xF000
#define TAG_GROUPSHIFT				12

#define TAG_GROUP( nTag )			(( nTag & TAG_GROUPMASK ) >> TAG_GROUPSHIFT )
#define TAG_NOGROUP( nTag )			( nTag & ~TAG_GROUPMASK )	// ~ = Bitweises NOT

#define TAG_NOMORETAGS				0x0

#define TAG_GROUP_FORMAT			0x1
#define TAG_ON						0x100
#define TAG_BOLDON					( TAG_GROUP_FORMAT << TAG_GROUPSHIFT | TAG_ON | 0x001 )
#define TAG_BOLDOFF					( TAG_GROUP_FORMAT << TAG_GROUPSHIFT |          0x001 )
#define TAG_ITALICON				( TAG_GROUP_FORMAT << TAG_GROUPSHIFT | TAG_ON | 0x002 )
#define TAG_ITALICOFF				( TAG_GROUP_FORMAT << TAG_GROUPSHIFT |          0x002 )
#define TAG_UNDERLINEON				( TAG_GROUP_FORMAT << TAG_GROUPSHIFT | TAG_ON | 0x004 )
#define TAG_UNDERLINEOFF			( TAG_GROUP_FORMAT << TAG_GROUPSHIFT |          0x004 )

#define TAG_GROUP_NOTALLOWED		0x2
#define TAG_HELPID					( TAG_GROUP_NOTALLOWED << TAG_GROUPSHIFT | 0x001 )
#define TAG_MODIFY					( TAG_GROUP_NOTALLOWED << TAG_GROUPSHIFT | 0x002 )
#define TAG_REFNR					( TAG_GROUP_NOTALLOWED << TAG_GROUPSHIFT | 0x004 )

#define TAG_GROUP_STRUCTURE			0x3
#define TAG_NAME					( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x001 )
#define TAG_HREF					( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x002 )
#define TAG_AVIS					( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x004 )
#define TAG_AHID					( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x008 )

#define TAG_TITEL					( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x020 )
#define TAG_KEY						( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x040 )
#define TAG_INDEX					( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x080 )

#define TAG_REFSTART				( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x100 )

#define TAG_GRAPHIC					( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x200 )
#define TAG_NEXTVERSION				( TAG_GROUP_STRUCTURE << TAG_GROUPSHIFT | 0x400 )

#define TAG_GROUP_SYSSWITCH			0x4
#define TAG_WIN						( TAG_GROUP_SYSSWITCH << TAG_GROUPSHIFT | 0x001 )
#define TAG_UNIX					( TAG_GROUP_SYSSWITCH << TAG_GROUPSHIFT | 0x002 )
#define TAG_MAC						( TAG_GROUP_SYSSWITCH << TAG_GROUPSHIFT | 0x004 )
#define TAG_OS2						( TAG_GROUP_SYSSWITCH << TAG_GROUPSHIFT | 0x008 )

#define TAG_GROUP_PROGSWITCH		0x5
#define TAG_WRITER					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x001 )
#define TAG_CALC					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x002 )
#define TAG_DRAW					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x004 )
#define TAG_IMPRESS					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x008 )
#define TAG_SCHEDULE				( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x010 )
#define TAG_IMAGE					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x020 )
#define TAG_MATH					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x040 )
#define TAG_CHART					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x080 )
#define TAG_OFFICE					( TAG_GROUP_PROGSWITCH << TAG_GROUPSHIFT | 0x100 )


#define TAG_GROUP_META				0x6
#define TAG_OFFICEFULLNAME			( TAG_GROUP_META << TAG_GROUPSHIFT | 0x001 )
#define TAG_OFFICENAME				( TAG_GROUP_META << TAG_GROUPSHIFT | 0x002 )
#define TAG_OFFICEPATH				( TAG_GROUP_META << TAG_GROUPSHIFT | 0x004 )
#define TAG_OFFICEVERSION			( TAG_GROUP_META << TAG_GROUPSHIFT | 0x008 )
#define TAG_PORTALNAME				( TAG_GROUP_META << TAG_GROUPSHIFT | 0x010 )
#define TAG_PORTALFULLNAME			( TAG_GROUP_META << TAG_GROUPSHIFT | 0x020 )
#define TAG_PORTALPATH				( TAG_GROUP_META << TAG_GROUPSHIFT | 0x040 )
#define TAG_PORTALVERSION			( TAG_GROUP_META << TAG_GROUPSHIFT | 0x080 )
#define TAG_PORTALSHORTNAME			( TAG_GROUP_META << TAG_GROUPSHIFT | 0x100 )


#define TAG_GROUP_SINGLE            0x7
#define TAG_REFINSERT               ( TAG_GROUP_SINGLE << TAG_GROUPSHIFT | 0x001 )


#define TAG_GROUP_MULTI				0x8
#define TAG_END						( TAG_GROUP_MULTI << TAG_GROUPSHIFT | 0x010 )
#define TAG_ELSE					( TAG_GROUP_MULTI << TAG_GROUPSHIFT | 0x020 )
#define TAG_AEND					( TAG_GROUP_MULTI << TAG_GROUPSHIFT | 0x040 )
#define TAG_VERSIONEND				( TAG_GROUP_MULTI << TAG_GROUPSHIFT | 0x080 )
#define TAG_ENDGRAPHIC				( TAG_GROUP_MULTI << TAG_GROUPSHIFT | 0x100 )

#define TAG_GROUP_MISC				0x9
#define TAG_COMMONSTART				( TAG_GROUP_MISC << TAG_GROUPSHIFT | 0x001 )
#define TAG_COMMONEND				( TAG_GROUP_MISC << TAG_GROUPSHIFT | 0x002 )

#define TAG_UNKNOWN_TAG				( TAG_GROUP_MULTI << TAG_GROUPSHIFT | 0x800 )

DECLARE_LIST( TokenListImpl, TokenInfo* )

class TokenList : private TokenListImpl
{
private:

    TokenList&   operator =( const TokenList& rList );
//                { TokenListImpl::operator =( rList ); return *this; }


public:
	using TokenListImpl::Count;


    TokenList() : TokenListImpl(){};
    ~TokenList(){ Clear(); };

	void		Clear()
		{
			for ( sal_uLong i = 0 ; i < Count() ; i++ )
				delete TokenListImpl::GetObject( i );
			TokenListImpl::Clear();
		}
	void		Insert( TokenInfo p, sal_uLong nIndex = LIST_APPEND )
		{ TokenListImpl::Insert( new TokenInfo(p), nIndex ); }
/*    TokenInfo		Remove( sal_uLong nIndex )
		{
			TokenInfo aT = GetObject( nIndex );
			delete TokenListImpl::GetObject( nIndex );
			TokenListImpl::Remove( nIndex );
			return aT;
		}*/
//    TokenInfo		Remove( TokenInfo p ){ return Remove( GetPos( p ) ); }
//    TokenInfo		GetCurObject() const { return *TokenListImpl::GetCurObject(); }
    TokenInfo&		GetObject( sal_uLong nIndex ) const
		{
//			if ( TokenListImpl::GetObject(nIndex) )
				return *TokenListImpl::GetObject(nIndex);
//			else
//				return TokenInfo();
		}
/*    sal_uLong		GetPos( const TokenInfo p ) const
		{
			for ( sal_uLong i = 0 ; i < Count() ; i++ )
				if ( p == GetObject( i ) )
					return i;
			return LIST_ENTRY_NOTFOUND;
		}*/

    TokenList( const TokenList& rList );
/*		{
			for ( sal_uLong i = 0 ; i < rList.Count() ; i++ )
			{
				Insert( rList.GetObject( i ), LIST_APPEND );
			}
		}*/
};

class ParserMessage
{
	sal_uInt16 nErrorNr;
	ByteString aErrorText;
	sal_uInt16 nTagBegin,nTagLength;

protected:
    ParserMessage( sal_uInt16 PnErrorNr, ByteString PaErrorText, const TokenInfo &rTag );
public:

	sal_uInt16 GetErrorNr() { return nErrorNr; }
	ByteString GetErrorText() { return aErrorText; }

	sal_uInt16 GetTagBegin() { return nTagBegin; }
	sal_uInt16 GetTagLength() { return nTagLength; }

    virtual ~ParserMessage() {}
    virtual sal_Bool IsError() =0;
    virtual ByteString Prefix() =0;
};

class ParserError : public ParserMessage
{
public:
    ParserError( sal_uInt16 PnErrorNr, ByteString PaErrorText, const TokenInfo &rTag );

    virtual sal_Bool IsError() {return sal_True;};
    virtual ByteString Prefix() {return "Error:"; };
};

class ParserWarning : public ParserMessage
{
public:
    ParserWarning( sal_uInt16 PnErrorNr, ByteString PaErrorText, const TokenInfo &rTag );

    virtual sal_Bool IsError() {return sal_False;};
    virtual ByteString Prefix() {return "Warning:"; };
};

class SimpleParser
{
private:
	sal_uInt16 nPos;
	String aSource;
	String aLastToken;
	TokenList aTokenList;

    TokenInfo aNextTag;     // to store closetag in case of combined tags like <br/>

	String GetNextTokenString( ParserMessageList &rErrorList, sal_uInt16 &rTokeStartPos );

public:
	SimpleParser();
	void Parse( String PaSource );
	TokenInfo GetNextToken( ParserMessageList &rErrorList );
	static String GetLexem( TokenInfo const &aToken );
	TokenList& GetTokenList(){ return aTokenList; }
};

class TokenParser
{
    sal_Bool match( const TokenInfo &aCurrentToken, const TokenId &aExpectedToken );
	sal_Bool match( const TokenInfo &aCurrentToken, const TokenInfo &aExpectedToken );
	void ParseError( sal_uInt16 nErrNr, ByteString aErrMsg, const TokenInfo &rTag );
	void Paragraph();
	void PfCase();
	void PfCaseBegin();
	void AppCase();
	void AppCaseBegin();
	void CaseEnd();
	void SimpleTag();
	void TagPair();
	void TagRef();

	SimpleParser aParser;
	TokenInfo aTag;

	TokenId nPfCaseOptions;
	TokenId nAppCaseOptions;
	sal_Bool bPfCaseActive ,bAppCaseActive;

	TokenId nActiveRefTypes;

	ParserMessageList *pErrorList;

public:
	TokenParser();
	void Parse( const String &aCode, ParserMessageList* pList );
//	ParserMessageList& GetErrors(){ return aErrorList; }
//	sal_Bool HasErrors(){ return ( aErrorList.Count() > 0 ); }
	TokenList& GetTokenList(){ return aParser.GetTokenList(); }
};

class LingTest
{
private:
	TokenParser aReferenceParser;
	TokenParser aTesteeParser;
	ParserMessageList aCompareWarningList;
	void CheckTags( TokenList &aReference, TokenList &aTestee, sal_Bool bFixTags );
    sal_Bool IsTagMandatory( TokenInfo const &aToken, TokenId &aMetaTokens );
    String aFixedTestee;
public:
	void CheckReference( GSILine *aReference );
	void CheckTestee( GSILine *aTestee, sal_Bool bHasSourceLine, sal_Bool bFixTags );

//	ParserMessageList& GetReferenceErrors(){ return aReferenceParser.GetErrors(); }
//	sal_Bool HasReferenceErrors(){ return aReferenceParser.HasErrors(); }

//	ParserMessageList& GetTesteeErrors(){ return aTesteeParser.GetErrors(); }
//	sal_Bool HasTesteeErrors(){ return aTesteeParser.HasErrors(); }

	ParserMessageList& GetCompareWarnings(){ return aCompareWarningList; }
	sal_Bool HasCompareWarnings(){ return ( aCompareWarningList.Count() > 0 ); }

    String GetFixedTestee(){ return aFixedTestee; }
};

#endif

