/**************************************************************
 * 
 * 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_canvas.hxx"

#include <boost/bind.hpp>
#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
#include <basegfx/polygon/b2dpolygontriangulator.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include "surfaceproxy.hxx"

namespace canvas
{
	//////////////////////////////////////////////////////////////////////////////////
	// SurfaceProxy::SurfaceProxy
	//////////////////////////////////////////////////////////////////////////////////

	SurfaceProxy::SurfaceProxy( const canvas::IColorBufferSharedPtr& pBuffer,
								const PageManagerSharedPtr&          pPageManager ) :
		mpPageManager( pPageManager ),
        maSurfaceList(),
        mpBuffer( pBuffer )
    {
		const ::basegfx::B2ISize aImageSize(mpBuffer->getWidth(),mpBuffer->getHeight());
		const ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
		const sal_Int32 aPageSizeX(aPageSize.getX());
		const sal_Int32 aPageSizeY(aPageSize.getY());
		const sal_Int32 aImageSizeX(aImageSize.getX());
		const sal_Int32 aImageSizeY(aImageSize.getY());

		// see if the size of the colorbuffer is larger than the size
		// of a single page. if this is the case we divide the
		// colorbuffer into as many surfaces as we need to get the
		// whole area distributed.  otherwise (the colorbuffer is
		// smaller than the size of a single page) we search for free
		// pages or create a new one.
		// the incoming image is too large to fit into a single
		// page.  strategy: we split the image into rectangular
		// areas that are as large as the maximum page size
		// dictates and follow the strategy for fitting images.
		size_t dwNumSurfaces(0);
		for(sal_Int32 y=0; y<aImageSizeY; y+=aPageSizeY)
			for(sal_Int32 x=0; x<aImageSizeX; x+=aPageSizeX)
				++dwNumSurfaces;
		maSurfaceList.reserve(dwNumSurfaces);

		for(sal_Int32 y=0; y<aImageSizeY; y+=aPageSizeY)
		{
			for(sal_Int32 x=0; x<aImageSizeX; x+=aPageSizeX)
			{
				// the current surface is located at the position [x,y]
				// and has the size [min(restx,pagesizex),min(resty,pagesizey)
				::basegfx::B2IPoint aOffset(x,y);
				::basegfx::B2ISize aSize( ::std::min( aImageSize.getX()-x,
                                                      aPageSize.getX() ),
                                          ::std::min( aImageSize.getY()-y,
                                                      aPageSize.getY() ) );

				maSurfaceList.push_back(
					SurfaceSharedPtr(
						new Surface(
							mpPageManager,
							mpBuffer,
							aOffset,
							aSize)));
			}
		}
    }
	
	//////////////////////////////////////////////////////////////////////////////////
	// SurfaceProxy::setColorBufferDirty
	//////////////////////////////////////////////////////////////////////////////////

	void SurfaceProxy::setColorBufferDirty()
	{
		::std::for_each( maSurfaceList.begin(),
                         maSurfaceList.end(),
						 ::boost::mem_fn(&Surface::setColorBufferDirty));
	}

	//////////////////////////////////////////////////////////////////////////////////
	// SurfaceProxy::draw
	//////////////////////////////////////////////////////////////////////////////////

	bool SurfaceProxy::draw( double                         fAlpha,
                             const ::basegfx::B2DPoint&     rPos,
                             const ::basegfx::B2DHomMatrix& rTransform )
	{
		::std::for_each( maSurfaceList.begin(),
                         maSurfaceList.end(),
						 ::boost::bind( &Surface::draw,
                                        _1,
                                        fAlpha,
                                        ::boost::cref(rPos),
                                        ::boost::cref(rTransform)));

		return true;
	}

	//////////////////////////////////////////////////////////////////////////////////
	// SurfaceProxy::draw
	//////////////////////////////////////////////////////////////////////////////////

	bool SurfaceProxy::draw( double                         fAlpha,
                             const ::basegfx::B2DPoint&     rPos,
                             const ::basegfx::B2DRange&		rArea,
                             const ::basegfx::B2DHomMatrix& rTransform )
	{
		::std::for_each( maSurfaceList.begin(),
                         maSurfaceList.end(),
						 ::boost::bind(&Surface::drawRectangularArea,
                                       _1,
                                       fAlpha,
                                       ::boost::cref(rPos),
                                       ::boost::cref(rArea),
                                       ::boost::cref(rTransform)));

		return true;
	}

	//////////////////////////////////////////////////////////////////////////////////
	// SurfaceProxy::draw
	//////////////////////////////////////////////////////////////////////////////////

	bool SurfaceProxy::draw( double                           fAlpha,
                             const ::basegfx::B2DPoint&       rPos,
                             const ::basegfx::B2DPolyPolygon& rClipPoly,
                             const ::basegfx::B2DHomMatrix&   rTransform )
	{
		const ::basegfx::B2DPolygon& rTriangulatedPolygon(
            ::basegfx::triangulator::triangulate(rClipPoly)); 

#if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0
        // dump polygons
        OSL_TRACE( "Original clip polygon: %s\n"
                   "Triangulated polygon: %s\n",
                   rtl::OUStringToOString(
                       basegfx::tools::exportToSvgD( rClipPoly, true, true, false ),
                       RTL_TEXTENCODING_ASCII_US).getStr(),
                   rtl::OUStringToOString(
                       basegfx::tools::exportToSvgD( 
                           basegfx::B2DPolyPolygon(rTriangulatedPolygon), true, true, false ),
                       RTL_TEXTENCODING_ASCII_US).getStr() );
#endif

		::std::for_each( maSurfaceList.begin(),
                         maSurfaceList.end(),
						 ::boost::bind(&Surface::drawWithClip,
                                       _1,
                                       fAlpha,
                                       ::boost::cref(rPos),
                                       ::boost::cref(rTriangulatedPolygon),
                                       ::boost::cref(rTransform)));

		return true;
	}
}
