/**************************************************************
 * 
 * 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_basegfx.hxx"
#include <basegfx/range/b2dpolyrange.hxx>

#include <basegfx/range/b2drange.hxx>
#include <basegfx/range/b2drangeclipper.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>

#include <boost/bind.hpp>
#include <boost/tuple/tuple.hpp>
#include <algorithm>
#include <vector>

static basegfx::B2VectorOrientation flipOrientation( 
    basegfx::B2VectorOrientation eOrient)
{
    return eOrient == basegfx::ORIENTATION_POSITIVE ? 
        basegfx::ORIENTATION_NEGATIVE : basegfx::ORIENTATION_POSITIVE;
}

namespace basegfx
{
    class ImplB2DPolyRange
    {
        void updateBounds()
        {
            maBounds.reset();
            std::for_each(maRanges.begin(),
                          maRanges.end(),
                          boost::bind(
                              (void (B2DRange::*)(const B2DRange&))(
                 &B2DRange::expand),
                              boost::ref(maBounds),
                              _1));
        }

    public:
        ImplB2DPolyRange() :
            maBounds(),
            maRanges(),
            maOrient()
        {}

        explicit ImplB2DPolyRange( const B2DPolyRange::ElementType& rElem ) :
            maBounds( boost::get<0>(rElem) ),
            maRanges( 1, boost::get<0>(rElem) ),
            maOrient( 1, boost::get<1>(rElem) )
        {}

        explicit ImplB2DPolyRange( const B2DRange& rRange, B2VectorOrientation eOrient ) :
            maBounds( rRange ),
            maRanges( 1, rRange ),
            maOrient( 1, eOrient )
        {}

        bool operator==(const ImplB2DPolyRange& rRHS) const
        {
            return maRanges == rRHS.maRanges && maOrient == rRHS.maOrient;
        }

        sal_uInt32 count() const
        {
            return maRanges.size();
        }

        B2DPolyRange::ElementType getElement(sal_uInt32 nIndex) const
        {
            return boost::make_tuple(maRanges[nIndex],
                                     maOrient[nIndex]);
        }

        void setElement(sal_uInt32 nIndex, const B2DPolyRange::ElementType& rElement )
        {
            maRanges[nIndex] = boost::get<0>(rElement);
            maOrient[nIndex] = boost::get<1>(rElement);
            updateBounds();
        }

        void setElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient )
        {
            maRanges[nIndex] = rRange;
            maOrient[nIndex] = eOrient;
            updateBounds();
        }

        void insertElement(sal_uInt32 nIndex, const B2DPolyRange::ElementType& rElement, sal_uInt32 nCount)
        {
            maRanges.insert(maRanges.begin()+nIndex, nCount, boost::get<0>(rElement));
            maOrient.insert(maOrient.begin()+nIndex, nCount, boost::get<1>(rElement));
            maBounds.expand(boost::get<0>(rElement));
        }

        void insertElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
        {
            maRanges.insert(maRanges.begin()+nIndex, nCount, rRange);
            maOrient.insert(maOrient.begin()+nIndex, nCount, eOrient);
            maBounds.expand(rRange);
        }

        void appendElement(const B2DPolyRange::ElementType& rElement, sal_uInt32 nCount)
        {
            maRanges.insert(maRanges.end(), nCount, boost::get<0>(rElement));
            maOrient.insert(maOrient.end(), nCount, boost::get<1>(rElement));
            maBounds.expand(boost::get<0>(rElement));
        }

        void appendElement(const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
        {
            maRanges.insert(maRanges.end(), nCount, rRange);
            maOrient.insert(maOrient.end(), nCount, eOrient);
            maBounds.expand(rRange);
        }

        void insertPolyRange(sal_uInt32 nIndex, const ImplB2DPolyRange& rPolyRange)
        {
            maRanges.insert(maRanges.begin()+nIndex, rPolyRange.maRanges.begin(), rPolyRange.maRanges.end());
            maOrient.insert(maOrient.begin()+nIndex, rPolyRange.maOrient.begin(), rPolyRange.maOrient.end());
            updateBounds();
        }

        void appendPolyRange(const ImplB2DPolyRange& rPolyRange)
        {
            maRanges.insert(maRanges.end(), 
                            rPolyRange.maRanges.begin(), 
                            rPolyRange.maRanges.end());
            maOrient.insert(maOrient.end(), 
                            rPolyRange.maOrient.begin(), 
                            rPolyRange.maOrient.end());
            updateBounds();
        }

        void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
        {
            maRanges.erase(maRanges.begin()+nIndex,maRanges.begin()+nIndex+nCount);
            maOrient.erase(maOrient.begin()+nIndex,maOrient.begin()+nIndex+nCount);
            updateBounds();
        }

        void clear()
        {
            std::vector<B2DRange> aTmpRanges;
            std::vector<B2VectorOrientation> aTmpOrient;

            maRanges.swap(aTmpRanges);
            maOrient.swap(aTmpOrient);

            maBounds.reset();
        }

        void flip()
        {
            std::for_each(maOrient.begin(),
                          maOrient.end(),
                          boost::bind(
                              &flipOrientation,
                              _1));
        }

        B2DRange getBounds() const
        {
            return maBounds;
        }

		template< typename ValueType > bool isInside( const ValueType& rValue ) const
        {
            if( !maBounds.isInside( rValue ) )
                return false;

            // cannot use boost::bind here, since isInside is overloaded.
            // It is currently not possible to resolve the overload
            // by considering one of the other template arguments.
            std::vector<B2DRange>::const_iterator 		aCurr( maRanges.begin() );
            const std::vector<B2DRange>::const_iterator aEnd ( maRanges.end() );
            while( aCurr != aEnd )
                if( aCurr->isInside( rValue ) )
                    return true;

            return false;
        }

		bool overlaps( const B2DRange& rRange ) const
        {
            if( !maBounds.overlaps( rRange ) )
                return false;

            const std::vector<B2DRange>::const_iterator aEnd( maRanges.end() );
            return std::find_if( maRanges.begin(), 
                                 aEnd,
                                 boost::bind<bool>( boost::mem_fn( &B2DRange::overlaps ),
                                                    _1,
                                                    boost::cref(rRange) ) ) != aEnd;
        }

        B2DPolyPolygon solveCrossovers() const
        {
            return tools::solveCrossovers(maRanges,maOrient);
        }

        const B2DRange* begin() const
        {
            if(maRanges.empty())
                return 0;
            else
                return &maRanges.front();
        }

        const B2DRange* end() const
        {
            if(maRanges.empty())
                return 0;
            else
                return (&maRanges.back())+1;
        }

        B2DRange* begin()
        {
            if(maRanges.empty())
                return 0;
            else
                return &maRanges.front();
        }

        B2DRange* end()
        {
            if(maRanges.empty())
                return 0;
            else
                return (&maRanges.back())+1;
        }

    private:
        B2DRange						 maBounds;
        std::vector<B2DRange>			 maRanges;
        std::vector<B2VectorOrientation> maOrient;
    };

    B2DPolyRange::B2DPolyRange() :
        mpImpl()
    {}

    B2DPolyRange::~B2DPolyRange()
    {}

    B2DPolyRange::B2DPolyRange( const ElementType& rElem ) :
        mpImpl( ImplB2DPolyRange( rElem ) )
    {}
    
    B2DPolyRange::B2DPolyRange( const B2DRange& rRange, B2VectorOrientation eOrient ) :
        mpImpl( ImplB2DPolyRange( rRange, eOrient ) )
    {}

    B2DPolyRange::B2DPolyRange( const B2DPolyRange& rRange ) :
        mpImpl( rRange.mpImpl )
    {}

    B2DPolyRange& B2DPolyRange::operator=( const B2DPolyRange& rRange )
    {
        mpImpl = rRange.mpImpl;
        return *this;
    }

    void B2DPolyRange::makeUnique()
    {
        mpImpl.make_unique();
    }

    bool B2DPolyRange::operator==(const B2DPolyRange& rRange) const
    {
        if(mpImpl.same_object(rRange.mpImpl))
            return true;

        return ((*mpImpl) == (*rRange.mpImpl));
    }

    bool B2DPolyRange::operator!=(const B2DPolyRange& rRange) const
    {
        return !(*this == rRange);
    }

    sal_uInt32 B2DPolyRange::count() const
    {
        return mpImpl->count();
    }

    B2DPolyRange::ElementType B2DPolyRange::getElement(sal_uInt32 nIndex) const
    {
        return mpImpl->getElement(nIndex);
    }

    void B2DPolyRange::setElement(sal_uInt32 nIndex, const ElementType& rElement )
    {
        mpImpl->setElement(nIndex, rElement);
    }

    void B2DPolyRange::setElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient )
    {
        mpImpl->setElement(nIndex, rRange, eOrient );
    }

    void B2DPolyRange::insertElement(sal_uInt32 nIndex, const ElementType& rElement, sal_uInt32 nCount)
    {
        mpImpl->insertElement(nIndex, rElement, nCount );
    }

    void B2DPolyRange::insertElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
    {
        mpImpl->insertElement(nIndex, rRange, eOrient, nCount );
    }

    void B2DPolyRange::appendElement(const ElementType& rElement, sal_uInt32 nCount)
    {
        mpImpl->appendElement(rElement, nCount);
    }

    void B2DPolyRange::appendElement(const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
    {
        mpImpl->appendElement(rRange, eOrient, nCount );
    }

    void B2DPolyRange::insertPolyRange(sal_uInt32 nIndex, const B2DPolyRange& rRange)
    {
        mpImpl->insertPolyRange(nIndex, *rRange.mpImpl);
    }

    void B2DPolyRange::appendPolyRange(const B2DPolyRange& rRange)
    {
        mpImpl->appendPolyRange(*rRange.mpImpl);
    }

    void B2DPolyRange::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
    {
        mpImpl->remove(nIndex, nCount);
    }

    void B2DPolyRange::clear()
    {
        mpImpl->clear();
    }

    void B2DPolyRange::flip()
    {
        mpImpl->flip();
    }

    B2DRange B2DPolyRange::getBounds() const
    {
        return mpImpl->getBounds();
    }

    bool B2DPolyRange::isInside( const B2DTuple& rTuple ) const
    {
        return mpImpl->isInside(rTuple);
    }
    
    bool B2DPolyRange::isInside( const B2DRange& rRange ) const
    {
        return mpImpl->isInside(rRange);
    }

    bool B2DPolyRange::overlaps( const B2DRange& rRange ) const
    {
        return mpImpl->overlaps(rRange);
    }

    B2DPolyPolygon B2DPolyRange::solveCrossovers() const
    {
        return mpImpl->solveCrossovers();
    }

    const B2DRange* B2DPolyRange::begin() const
    {
        return mpImpl->begin();
    }

    const B2DRange* B2DPolyRange::end() const
    {
        return mpImpl->end();
    }

    B2DRange* B2DPolyRange::begin()
    {
        return mpImpl->begin();
    }

    B2DRange* B2DPolyRange::end()
    {
        return mpImpl->end();
    }

} // end of namespace basegfx

// eof