/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svtools.hxx"

#include <string.h>
// #include <math.h>
#include <vcl/svapp.hxx>
#include <tools/urlobj.hxx>
#ifndef _WRKWIN_HXX //autogen
#include <vcl/wrkwin.hxx>
#endif
#include <sot/formats.hxx>

#include "svl/urihelper.hxx"
#include <svtools/imap.hxx>
#include <svtools/imap.hxx>
#include <svtools/imapobj.hxx>
#include <svtools/imaprect.hxx>
#include <svtools/imapcirc.hxx>
#include <svtools/imappoly.hxx>

#include <string.h>
#include <math.h>

#define NOTEOL(c) ((c)!='\0')


TYPEINIT0_AUTOFACTORY( ImageMap );


/******************************************************************************/
/******************************************************************************/


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapObject::AppendCERNCoords( const Point& rPoint100, ByteString& rStr ) const
{
	const Point	aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MAP_100TH_MM ) ) );

	rStr += '(';
	rStr += ByteString::CreateFromInt32( aPixPt.X() );
	rStr += ',';
	rStr += ByteString::CreateFromInt32( aPixPt.Y() );
	rStr += ") ";
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapObject::AppendNCSACoords( const Point& rPoint100, ByteString& rStr ) const
{
	const Point	aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MAP_100TH_MM ) ) );

	rStr += ByteString::CreateFromInt32( aPixPt.X() );
	rStr += ',';
	rStr += ByteString::CreateFromInt32( aPixPt.Y() );
	rStr += ' ';
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapObject::AppendCERNURL( ByteString& rStr, const String& rBaseURL ) const
{
    rStr += ByteString( String(URIHelper::simpleNormalizedMakeRelative( rBaseURL, aURL )), gsl_getSystemTextEncoding() );
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapObject::AppendNCSAURL( ByteString& rStr, const String& rBaseURL ) const
{
    rStr += ByteString( String(URIHelper::simpleNormalizedMakeRelative( rBaseURL, aURL )), gsl_getSystemTextEncoding() );
	rStr += ' ';
}


/******************************************************************************/
/******************************************************************************/


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapRectangleObject::WriteCERN( SvStream& rOStm, const String& rBaseURL ) const
{
	ByteString aStr( "rectangle " );

	AppendCERNCoords( aRect.TopLeft(), aStr );
	AppendCERNCoords( aRect.BottomRight(), aStr );
    AppendCERNURL( aStr, rBaseURL );

	rOStm.WriteLine( aStr );
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapRectangleObject::WriteNCSA( SvStream& rOStm, const String& rBaseURL ) const
{
	ByteString aStr( "rect " );

    AppendNCSAURL( aStr, rBaseURL );
	AppendNCSACoords( aRect.TopLeft(), aStr );
	AppendNCSACoords( aRect.BottomRight(), aStr );

	rOStm.WriteLine( aStr );
}


/******************************************************************************/
/******************************************************************************/


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapCircleObject::WriteCERN( SvStream& rOStm, const String& rBaseURL ) const
{
	ByteString aStr( "circle " );

	AppendCERNCoords( aCenter, aStr );
    aStr += ByteString::CreateFromInt32(nRadius);
	aStr += ' ';
    AppendCERNURL( aStr, rBaseURL );

	rOStm.WriteLine( aStr );
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapCircleObject::WriteNCSA( SvStream& rOStm, const String& rBaseURL ) const
{
	ByteString aStr( "circle " );

    AppendNCSAURL( aStr, rBaseURL );
	AppendNCSACoords( aCenter, aStr );
	AppendNCSACoords( aCenter + Point( nRadius, 0 ), aStr );

	rOStm.WriteLine( aStr );
}


/******************************************************************************/
/******************************************************************************/


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapPolygonObject::WriteCERN( SvStream& rOStm, const String& rBaseURL  ) const
{
	ByteString		aStr( "polygon " );
	const sal_uInt16	nCount = aPoly.GetSize();

	for ( sal_uInt16 i = 0; i < nCount; i++ )
		AppendCERNCoords( aPoly[ i ], aStr );

    AppendCERNURL( aStr, rBaseURL );

	rOStm.WriteLine( aStr );
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void IMapPolygonObject::WriteNCSA( SvStream& rOStm, const String& rBaseURL  ) const
{
	ByteString		aStr( "poly " );
	const sal_uInt16	nCount = Min( aPoly.GetSize(), (sal_uInt16) 100 );

    AppendNCSAURL( aStr, rBaseURL );

	for ( sal_uInt16 i = 0; i < nCount; i++ )
		AppendNCSACoords( aPoly[ i ], aStr );

	rOStm.WriteLine( aStr );
}


/******************************************************************************/
/******************************************************************************/


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void ImageMap::Write( SvStream& rOStm, sal_uLong nFormat, const String& rBaseURL ) const
{
	switch( nFormat )
	{
        case( IMAP_FORMAT_BIN ) : Write( rOStm, rBaseURL );
        case( IMAP_FORMAT_CERN ) : ImpWriteCERN( rOStm, rBaseURL ); break;
        case( IMAP_FORMAT_NCSA ) : ImpWriteNCSA( rOStm, rBaseURL ); break;

		default:
		break;
	}
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void ImageMap::ImpWriteCERN( SvStream& rOStm, const String& rBaseURL ) const
{
	IMapObject* pObj;
	sal_uInt16		nCount = (sal_uInt16) maList.Count();

	for ( sal_uInt16 i = 0; i < nCount; i++ )
	{
		pObj = GetIMapObject( i );

		switch( pObj->GetType() )
		{
			case( IMAP_OBJ_RECTANGLE ):
                ( (IMapRectangleObject*) pObj )->WriteCERN( rOStm, rBaseURL );
			break;

			case( IMAP_OBJ_CIRCLE ):
                ( (IMapCircleObject*) pObj )->WriteCERN( rOStm, rBaseURL );
			break;

			case( IMAP_OBJ_POLYGON ):
                ( (IMapPolygonObject*) pObj )->WriteCERN( rOStm, rBaseURL );
			break;

			default:
			break;
		}
	}
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void ImageMap::ImpWriteNCSA( SvStream& rOStm, const String& rBaseURL  ) const
{
	IMapObject* pObj;
	sal_uInt16		nCount = (sal_uInt16) maList.Count();

	for ( sal_uInt16 i = 0; i < nCount; i++ )
	{
		pObj = GetIMapObject( i );

			switch( pObj->GetType() )
		{
			case( IMAP_OBJ_RECTANGLE ):
                ( (IMapRectangleObject*) pObj )->WriteNCSA( rOStm, rBaseURL );
			break;

			case( IMAP_OBJ_CIRCLE ):
                ( (IMapCircleObject*) pObj )->WriteNCSA( rOStm, rBaseURL );
			break;

			case( IMAP_OBJ_POLYGON ):
                ( (IMapPolygonObject*) pObj )->WriteNCSA( rOStm, rBaseURL );
			break;

			default:
			break;
		}
	}
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

sal_uLong ImageMap::Read( SvStream& rIStm, sal_uLong nFormat, const String& rBaseURL  )
{
	sal_uLong nRet = IMAP_ERR_FORMAT;

	if ( nFormat == IMAP_FORMAT_DETECT )
		nFormat = ImpDetectFormat( rIStm );

	switch ( nFormat )
	{
        case ( IMAP_FORMAT_BIN )    : Read( rIStm, rBaseURL ); break;
        case ( IMAP_FORMAT_CERN )   : nRet = ImpReadCERN( rIStm, rBaseURL ); break;
        case ( IMAP_FORMAT_NCSA )   : nRet = ImpReadNCSA( rIStm, rBaseURL ); break;

		default:
		break;
	}

	if ( !rIStm.GetError() )
		nRet = IMAP_ERR_OK;

	return nRet;
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

sal_uLong ImageMap::ImpReadCERN( SvStream& rIStm, const String& rBaseURL )
{
	ByteString aStr;

	// alten Inhalt loeschen
	ClearImageMap();

	while ( rIStm.ReadLine( aStr ) )
        ImpReadCERNLine( aStr, rBaseURL );

	return IMAP_ERR_OK;
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void ImageMap::ImpReadCERNLine( const ByteString& rLine, const String& rBaseURL  )
{
	ByteString	aStr( rLine );
	ByteString	aToken;

	aStr.EraseLeadingChars( ' ' );
	aStr.EraseLeadingChars( '\t' );
	aStr.EraseAllChars( ';' );
	aStr.ToLowerAscii();

	const char*	pStr = aStr.GetBuffer();
	char		cChar = *pStr++;

		// Anweisung finden
	while( ( cChar >= 'a' ) && ( cChar <= 'z' ) && NOTEOL( cChar ) )
	{
		aToken += cChar;
		cChar = *pStr++;
	}

	if ( NOTEOL( cChar ) )
	{
		if ( ( aToken == "rectangle" ) || ( aToken == "rect" ) )
		{
			const Point		aTopLeft( ImpReadCERNCoords( &pStr ) );
			const Point		aBottomRight( ImpReadCERNCoords( &pStr ) );
            const String    aURL( ImpReadCERNURL( &pStr, rBaseURL ) );
			const Rectangle	aRect( aTopLeft, aBottomRight );

			IMapRectangleObject* pObj = new IMapRectangleObject( aRect, aURL, String(), String(), String(), String() );
			maList.Insert( pObj, LIST_APPEND );
		}
		else if ( ( aToken == "circle" ) || ( aToken == "circ" ) )
		{
			const Point		aCenter( ImpReadCERNCoords( &pStr ) );
			const long		nRadius = ImpReadCERNRadius( &pStr );
            const String    aURL( ImpReadCERNURL( &pStr, rBaseURL ) );

			IMapCircleObject* pObj = new IMapCircleObject( aCenter, nRadius, aURL, String(), String(), String(), String() );
			maList.Insert( pObj, LIST_APPEND );
		}
		else if ( ( aToken == "polygon" ) || ( aToken == "poly" ) )
		{
			const sal_uInt16	nCount = aStr.GetTokenCount( '(' ) - 1;
			Polygon			aPoly( nCount );
			String			aURL;

			for ( sal_uInt16 i = 0; i < nCount; i++ )
				aPoly[ i ] = ImpReadCERNCoords( &pStr );

            aURL = ImpReadCERNURL( &pStr, rBaseURL );

			IMapPolygonObject* pObj = new IMapPolygonObject( aPoly, aURL, String(), String(), String(), String() );
			maList.Insert( pObj, LIST_APPEND );
		}
	}
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

Point ImageMap::ImpReadCERNCoords( const char** ppStr )
{
	String	aStrX;
	String	aStrY;
	Point	aPt;
	char	cChar = *(*ppStr)++;

	while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
		cChar = *(*ppStr)++;

	if ( NOTEOL( cChar ) )
	{
		while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
		{
			aStrX += cChar;
			cChar = *(*ppStr)++;
		}

		if ( NOTEOL( cChar ) )
		{
			while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
				cChar = *(*ppStr)++;

			while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
			{
				aStrY += cChar;
				cChar = *(*ppStr)++;
			}

			if ( NOTEOL( cChar ) )
				while( NOTEOL( cChar ) && ( cChar != ')' ) )
					cChar = *(*ppStr)++;

			aPt = Point( aStrX.ToInt32(), aStrY.ToInt32() );
		}
	}

	return aPt;
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

long ImageMap::ImpReadCERNRadius( const char** ppStr )
{
	String	aStr;
	char	cChar = *(*ppStr)++;

	while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
		cChar = *(*ppStr)++;

	if ( NOTEOL( cChar ) )
	{
		while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
		{
			aStr += cChar;
			cChar = *(*ppStr)++;
		}
	}

	return aStr.ToInt32();
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

String ImageMap::ImpReadCERNURL( const char** ppStr, const String& rBaseURL )
{
	String	aStr( String::CreateFromAscii( *ppStr ) );

	aStr.EraseLeadingChars( ' ' );
	aStr.EraseLeadingChars( '\t' );
	aStr.EraseTrailingChars( ' ' );
	aStr.EraseTrailingChars( '\t' );

    return INetURLObject::GetAbsURL( rBaseURL, aStr );
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

sal_uLong ImageMap::ImpReadNCSA( SvStream& rIStm, const String& rBaseURL )
{
	ByteString aStr;

	// alten Inhalt loeschen
	ClearImageMap();

	while ( rIStm.ReadLine( aStr ) )
        ImpReadNCSALine( aStr, rBaseURL );

	return IMAP_ERR_OK;
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

void ImageMap::ImpReadNCSALine( const ByteString& rLine, const String& rBaseURL )
{
	ByteString	aStr( rLine );
	ByteString	aToken;

	aStr.EraseLeadingChars( ' ' );
	aStr.EraseLeadingChars( '\t' );
	aStr.EraseAllChars( ';' );
	aStr.ToLowerAscii();

	const char*	pStr = aStr.GetBuffer();
	char		cChar = *pStr++;

		// Anweisung finden
	while( ( cChar >= 'a' ) && ( cChar <= 'z' ) && NOTEOL( cChar ) )
	{
		aToken += cChar;
		cChar = *pStr++;
	}

	if ( NOTEOL( cChar ) )
	{
		if ( aToken == "rect" )
		{
            const String    aURL( ImpReadNCSAURL( &pStr, rBaseURL ) );
			const Point		aTopLeft( ImpReadNCSACoords( &pStr ) );
			const Point		aBottomRight( ImpReadNCSACoords( &pStr ) );
			const Rectangle	aRect( aTopLeft, aBottomRight );

			IMapRectangleObject* pObj = new IMapRectangleObject( aRect, aURL, String(), String(), String(), String() );
			maList.Insert( pObj, LIST_APPEND );
		}
		else if ( aToken == "circle" )
		{
            const String    aURL( ImpReadNCSAURL( &pStr, rBaseURL ) );
			const Point		aCenter( ImpReadNCSACoords( &pStr ) );
			const Point		aDX( aCenter - ImpReadNCSACoords( &pStr ) );
			long			nRadius = (long) sqrt( (double) aDX.X() * aDX.X() +
												   (double) aDX.Y() * aDX.Y() );

			IMapCircleObject* pObj = new IMapCircleObject( aCenter, nRadius, aURL, String(), String(), String(), String() );
			maList.Insert( pObj, LIST_APPEND );
		}
		else if ( aToken == "poly" )
		{
			const sal_uInt16	nCount = aStr.GetTokenCount( ',' ) - 1;
            const String    aURL( ImpReadNCSAURL( &pStr, rBaseURL ) );
			Polygon			aPoly( nCount );

			for ( sal_uInt16 i = 0; i < nCount; i++ )
				aPoly[ i ] = ImpReadNCSACoords( &pStr );

			IMapPolygonObject* pObj = new IMapPolygonObject( aPoly, aURL, String(), String(), String(), String() );
			maList.Insert( pObj, LIST_APPEND );
		}
	}
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

String ImageMap::ImpReadNCSAURL( const char** ppStr, const String& rBaseURL )
{
	String	aStr;
	char	cChar = *(*ppStr)++;

	while( NOTEOL( cChar ) && ( ( cChar == ' ' ) || ( cChar == '\t' ) ) )
		cChar = *(*ppStr)++;

	if ( NOTEOL( cChar ) )
	{
		while( NOTEOL( cChar ) && ( cChar != ' ' ) && ( cChar != '\t' ) )
		{
			aStr += cChar;
			cChar = *(*ppStr)++;
		}
	}

    return INetURLObject::GetAbsURL( rBaseURL, aStr );
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

Point ImageMap::ImpReadNCSACoords( const char** ppStr )
{
	String	aStrX;
	String	aStrY;
	Point	aPt;
	char	cChar = *(*ppStr)++;

	while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
		cChar = *(*ppStr)++;

	if ( NOTEOL( cChar ) )
	{
		while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
		{
			aStrX += cChar;
			cChar = *(*ppStr)++;
		}

		if ( NOTEOL( cChar ) )
		{
			while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
				cChar = *(*ppStr)++;

			while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
			{
				aStrY += cChar;
				cChar = *(*ppStr)++;
			}

			aPt = Point( aStrX.ToInt32(), aStrY.ToInt32() );
		}
	}

	return aPt;
}


/******************************************************************************
|*
|*
|*
\******************************************************************************/

sal_uLong ImageMap::ImpDetectFormat( SvStream& rIStm )
{
	sal_uLong	nPos = rIStm.Tell();
	sal_uLong	nRet = IMAP_FORMAT_BIN;
	char	cMagic[6];

	rIStm.Read( cMagic, sizeof( cMagic ) );

	// Falls wir kein internes Format haben,
	// untersuchen wir das Format
	if ( memcmp( cMagic, IMAPMAGIC, sizeof( cMagic ) ) )
	{
		ByteString	aStr;
		long		nCount = 128;

		rIStm.Seek( nPos );
		while ( rIStm.ReadLine( aStr ) && nCount-- )
		{
			aStr.ToLowerAscii();

			if ( ( aStr.Search( "rect" ) != STRING_NOTFOUND ) ||
				 ( aStr.Search( "circ" ) != STRING_NOTFOUND ) ||
				 ( aStr.Search( "poly" ) != STRING_NOTFOUND ) )
			{
				if ( ( aStr.Search( '(' ) != STRING_NOTFOUND ) &&
					 ( aStr.Search( ')' ) != STRING_NOTFOUND ) )
				{
					nRet = IMAP_FORMAT_CERN;
				}
				else
					nRet = IMAP_FORMAT_NCSA;

				break;
			}
		}
	}

	rIStm.Seek( nPos );

	return nRet;
}
