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

namespace canvas
{
	Page::Page( const IRenderModuleSharedPtr &rRenderModule ) :
		mpRenderModule(rRenderModule),
		mpSurface(rRenderModule->createSurface(::basegfx::B2ISize()))
	{
	}

	void Page::validate()
	{
		if(!(isValid()))
		{
			::std::for_each( mpFragments.begin(),
							 mpFragments.end(),
							 ::boost::mem_fn(&PageFragment::refresh));
		}
	}

	bool Page::isValid() const
	{
		return mpSurface && mpSurface->isValid();
	}

	FragmentSharedPtr Page::allocateSpace( const ::basegfx::B2ISize& rSize )
	{
		SurfaceRect rect(rSize);
		if(insert(rect))
		{
			FragmentSharedPtr pFragment(new PageFragment(rect,this));
			mpFragments.push_back(pFragment);
			return pFragment;
		}

		return FragmentSharedPtr();
	}

	bool Page::nakedFragment( const FragmentSharedPtr& pFragment )
	{
		SurfaceRect rect(pFragment->getSize());
		if(insert(rect))
		{
			pFragment->setPage(this);
			mpFragments.push_back(pFragment);
			return true;
		}

		return false;
	}

	void Page::free( const FragmentSharedPtr& pFragment )
	{
		// the fragment passes as argument is no longer
		// dedicated to this page. either it is about to
		// be relocated to some other page or it will
		// currently be deleted. in either case, simply
		// remove the reference from our internal storage.
		FragmentContainer_t::iterator it(
			std::remove(
				mpFragments.begin(),mpFragments.end(),pFragment));
		mpFragments.erase(it,mpFragments.end());
	}

	bool Page::insert( SurfaceRect& r )
	{
		const FragmentContainer_t::const_iterator aEnd(mpFragments.end());
		FragmentContainer_t::const_iterator       it(mpFragments.begin());
		while(it != aEnd)
		{
			const SurfaceRect &rect = (*it)->getRect();
            const sal_Int32 x = rect.maPos.getX();
            const sal_Int32 y = rect.maPos.getY();
            // to avoid interpolation artifacts from other textures,
            // one pixel gap between them
            const sal_Int32 w = rect.maSize.getX()+1;
            const sal_Int32 h = rect.maSize.getY()+1;

			// probe location to the right
			r.maPos.setX(x+w);
            r.maPos.setY(y);
            if(isValidLocation(r))
				return true;

			// probe location at bottom
            r.maPos.setX(x);
            r.maPos.setY(y+h);
            if(isValidLocation(r))
				return true;

			++it;
		}

		r.maPos.setX(0);
		r.maPos.setY(0);

		return isValidLocation(r);
	}

	bool Page::isValidLocation( const SurfaceRect& r ) const
	{
		// the rectangle passed as argument has a valid
		// location if and only if there's no intersection
		// with existing areas.
		SurfaceRect aBoundary(mpRenderModule->getPageSize()-basegfx::B2IVector(1,1));
		if( !r.inside(aBoundary) )
			return false;

		const FragmentContainer_t::const_iterator aEnd(mpFragments.end());
		FragmentContainer_t::const_iterator       it(mpFragments.begin());
		while(it != aEnd)
		{
            if(r.intersection((*it)->getRect()))
                return false;

			++it;
		}

		return true;
	}
}
