/**************************************************************
 * 
 * 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 <osl/diagnose.h>

#include <basegfx/curve/b2dcubicbezier.hxx>

#include <basegfx/tools/debugplotter.hxx>
#include <boost/bind.hpp>


namespace basegfx
{
    namespace
    {
        void outputHeader( const ::rtl::OString& rTitle,
                           ::std::ostream* 		 pStm )
        {
            // output gnuplot setup
            if( pStm )
            {
                *pStm << "#!/usr/bin/gnuplot -persist" << ::std::endl <<
                    "#" << ::std::endl <<
                    "# automatically generated by basegfx, don't change!" << ::std::endl <<
                    "#" << ::std::endl <<
                    "#   --- " << (const sal_Char*)rTitle << " ---" << ::std::endl <<
                    "#" << ::std::endl <<
                    "set parametric" << ::std::endl <<
                    "# set terminal postscript eps enhanced color " << ::std::endl <<
                    "# set output \"plot.eps\"" << ::std::endl <<
                    // This function plots a cubic bezier curve. P,q,r,s
                    // are the control point elements of the corresponding
                    // output coordinate component (i.e. x components for
                    // the x plot, and y components for the y plot)
                    "cubicBezier(p,q,r,s,t) = p*(1-t)**3+q*3*(1-t)**2*t+r*3*(1-t)*t**2+s*t**3" << ::std::endl <<
                    // This function plots the derivative of a cubic
                    // bezier curve. P,q,r,s are the control point
                    // components of the _original_ curve
                    "cubicBezDerivative(p,q,r,s,t) = 3*(q-p)*(1-t)**2+6*(r-q)*(1-t)*t+3*(s-r)*t**2" << ::std::endl <<
                    // Plot a line's component of a line between a and b
                    // (where a and b should be the corresponding
                    // components of the line's start and end point,
                    // respectively)
                    "line(p,q,r) = p*(1-t)+q*t" << ::std::endl <<
                    // Plot a line's x component of a line in implicit
                    // form ax + by + c = 0
                    "implicitLineX(a,b,c,t) = a*-c + t*-b" << ::std::endl <<									 
                    // Plot a line's y component of a line in implicit
                    // form ax + by + c = 0
                    "implicitLineY(a,b,c,t) = b*-c + t*a" << ::std::endl <<									 	 
                    "pointmarkx(c,t) = c-0.03*t" << ::std::endl <<										 // hack for displaying single points in parametric form
                    "pointmarky(c,t) = c+0.03*t" << ::std::endl <<										 // hack for displaying single points in parametric form
                    "# end of setup" << ::std::endl;
            }
            else
            {
                OSL_TRACE( "#!/usr/bin/gnuplot -persist\n",
                           "#\n",
                           "# automatically generated by basegfx, don't change!\n",
                           "#\n",
                           "#   --- %s ---\n",
                           "#\n",
                           "set parametric\n",
                           // This function plots a cubic bezier curve. P,q,r,s
                           // are the control point elements of the corresponding
                           // output coordinate component (i.e. x components for
                           // the x plot, and y components for the y plot)
                           "cubicBezier(p,q,r,s,t) = p*(1-t)**3+q*3*(1-t)**2*t+r*3*(1-t)*t**2+s*t**3\n",
                           // This function plots the derivative of a cubic
                           // bezier curve. P,q,r,s are the control point
                           // components of the _original_ curve
                           "cubicBezDerivative(p,q,r,s,t) = 3*(q-p)*(1-t)**2+6*(r-q)*(1-t)*t+3*(s-r)*t**2\n",
                           // Plot a line's component of a line between a and b
                           // (where a and b should be the corresponding
                           // components of the line's start and end point,
                           // respectively)
                           "line(p,q,r) = p*(1-t)+q*t\n",
                           // Plot a line's x component of a line in implicit
                           // form ax + by + c = 0
                           "implicitLineX(a,b,c,t) = a*-c + t*-b\n",									 
                           // Plot a line's y component of a line in implicit
                           // form ax + by + c = 0
                           "implicitLineY(a,b,c,t) = b*-c + t*a\n",									 	 
                           "pointmarkx(c,t) = c-0.03*t\n",										 // hack for displaying single points in parametric form
                           "pointmarky(c,t) = c+0.03*t\n",										 // hack for displaying single points in parametric form
                           "# end of setup\n",
                           (const sal_Char*)rTitle );
            }
        }

        class Writer
        {
        public:
            Writer( ::std::ostream* pStm ) :
                mpStream( pStm )
            {
            }
            
            void outputPoint( const ::std::pair< B2DPoint, ::rtl::OString >& rElem )
            {
                if( mpStream )
                    *mpStream << " " << rElem.first.getX() << "\t" << rElem.first.getY() << ::std::endl;
                else
                    OSL_TRACE( " %f\t%f\n", rElem.first.getX(), rElem.first.getY() );
            }
            
            void outputVector( const ::std::pair< B2DVector, ::rtl::OString >& rElem )
            {
                if( mpStream )
                    *mpStream << " " << rElem.first.getX() << "\t" << rElem.first.getY() << ::std::endl << ::std::endl;
                else
                    OSL_TRACE( " %f\t%f\n\n", rElem.first.getX(), rElem.first.getY() );
            }
            
            void outputRect( const ::std::pair< B2DRange, ::rtl::OString >& rElem )
            {
                const double nX0( rElem.first.getMinX() );
                const double nY0( rElem.first.getMinY() );
                const double nX1( rElem.first.getMaxX() );
                const double nY1( rElem.first.getMaxY() );
                
                if( mpStream )
                    *mpStream << " " 
                              << nX0 << "\t" << nY0 << "\t"
                              << nX1 << "\t" << nY0 << "\t"
                              << nX1 << "\t" << nY1 << "\t"
                              << nX0 << "\t" << nY1 << "\t"
                              << nX0 << "\t" << nY0 << ::std::endl << ::std::endl;

                else
                    OSL_TRACE( " %f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n\n", 
                               nX0, nY0,
                               nX1, nY0,
                               nX1, nY1,
                               nX0, nY1,
                               nX0, nY0 );
            }

        private:
            ::std::ostream* 	mpStream;            
        };
    }

    DebugPlotter::DebugPlotter( const sal_Char* pTitle ) :
        maTitle( pTitle ),
        maPoints(),
        maVectors(),
        maRanges(),
        maPolygons(),
        mpOutputStream(NULL)
    {
    }

    DebugPlotter::DebugPlotter( const sal_Char* pTitle,
                                ::std::ostream& rOutputStream ) :
        maTitle( pTitle ),
        maPoints(),
        maVectors(),
        maRanges(),
        maPolygons(),
        mpOutputStream(&rOutputStream)
    {
    }

    DebugPlotter::~DebugPlotter()
    {
        const bool bHavePoints( !maPoints.empty() );
        const bool bHaveVectors( !maVectors.empty() );
        const bool bHaveRanges( !maRanges.empty() );
        const bool bHavePolygons( !maPolygons.empty() );

        if( bHavePoints ||
            bHaveVectors ||
            bHaveRanges ||
            bHavePolygons )
        {
            outputHeader( maTitle, mpOutputStream );
            
            print( "\n\n# parametric primitive output\n"
                   "plot [t=0:1] \\\n" );

            // output plot declarations for used entities
            bool bNeedColon( false );
            if( bHavePoints )
            {
                print( " '-' using ($1):($2) title \"Points\" with points" );
                bNeedColon = true;
            }
            if( bHaveVectors )
            {
                if( bNeedColon )
                    print( ", \\\n" );

                print( " '-' using ($1):($2) title \"Vectors\" with lp" );
                bNeedColon = true;
            }
            if( bHaveRanges )
            {
                if( bNeedColon )
                    print( ", \\\n" );

                print( " '-' using ($1):($2) title \"Ranges\" with lines" );
                bNeedColon = true;
            }
            if( bHavePolygons )
            {
                const ::std::size_t nSize( maPolygons.size() );
                for( ::std::size_t i=0; i<nSize; ++i )
                {
                    if( maPolygons.at(i).first.areControlPointsUsed() )
                    {
                        const B2DPolygon& rCurrPoly( maPolygons.at(i).first );
                        
                        const sal_uInt32 nCount( rCurrPoly.count() );
                        for( sal_uInt32 k=0; k<nCount; ++k )
                        {
                            if( bNeedColon )
                                print( ", \\\n" );

                            const B2DPoint& rP0( rCurrPoly.getB2DPoint(k) );
                            const B2DPoint& rP1( rCurrPoly.getNextControlPoint(k) );
                            const B2DPoint& rP2( rCurrPoly.getPrevControlPoint((k + 1) % nCount) );
                            const B2DPoint& rP3( k+1<nCount ? rCurrPoly.getB2DPoint(k+1) : rCurrPoly.getB2DPoint(k) );

                            if( mpOutputStream )
                                *mpOutputStream << "  cubicBezier(" 
                                                << rP0.getX() << ","
                                    << rP1.getX() << ","
                                    << rP2.getX() << ","
                                    << rP3.getX() << ",t), \\\n   cubicBezier("
                                    << rP0.getY() << ","
                                    << rP1.getY() << ","
                                    << rP2.getY() << ","
                                    << rP3.getY() << ",t)";
                            else
                                OSL_TRACE( "  cubicBezier(%f,%f,%f,%f,t), \\\n"
                                           "   cubicBezier(%f,%f,%f,%f,t)",
                                           rP0.getX(),
                                           rP1.getX(),
                                           rP2.getX(),
                                           rP3.getX(),
                                           rP0.getY(),
                                           rP1.getY(),
                                           rP2.getY(),
                                           rP3.getY() );

                            bNeedColon = true;
                        }
                    }
                    else
                    {
                        if( bNeedColon )
                            print( ", \\\n" );
                        
                        if( mpOutputStream )
                            *mpOutputStream << " '-' using ($1):($2) title \"Polygon "
                                            << (const sal_Char*)maPolygons.at(i).second << "\" with lp";
                        else
                            OSL_TRACE( " '-' using ($1):($2) title \"Polygon %s\" with lp",
                                       (const sal_Char*)maPolygons.at(i).second );

                        bNeedColon = true;
                    }
                }
            }

            if( bHavePoints )
            {
                Writer aWriter( mpOutputStream );

                ::std::for_each( maPoints.begin(),
                                 maPoints.end(),
                                 ::boost::bind( &Writer::outputPoint,
                                                ::boost::ref( aWriter ),
                                                _1 ) );
                print( "e\n" );
            }

            if( bHaveVectors )
            {
                Writer aWriter( mpOutputStream );

                ::std::for_each( maVectors.begin(),
                                 maVectors.end(),
                                 ::boost::bind( &Writer::outputVector,
                                                ::boost::ref( aWriter ),
                                                _1 ) );
                print( "e\n" );
            }

            if( bHaveRanges )
            {
                Writer aWriter( mpOutputStream );

                ::std::for_each( maRanges.begin(),
                                 maRanges.end(),
                                 ::boost::bind( &Writer::outputRect,
                                                ::boost::ref( aWriter ),
                                                _1 ) );
                print( "e\n" );
            }

            if( bHavePolygons )
            {
                const ::std::size_t nSize( maPolygons.size() );
                for( ::std::size_t i=0; i<nSize; ++i )
                {
                    if( !maPolygons.at(i).first.areControlPointsUsed() )
                    {
                        const B2DPolygon& rCurrPoly( maPolygons.at(i).first );

                        const sal_uInt32 nCount( rCurrPoly.count() );
                        for( sal_uInt32 k=0; k<nCount; ++k )
                        {
                            const B2DPoint& rP( rCurrPoly.getB2DPoint(k) );

                            if( mpOutputStream )
                                *mpOutputStream << " " << rP.getX() << "," << rP.getY();
                            else
                                OSL_TRACE( " %f,%f",
                                           rP.getX(),
                                           rP.getX() );
                        }

                        print( "\ne\n" );
                    }
                }
            }
        }
    }

    void DebugPlotter::plot( const B2DPoint& rPoint,
                             const sal_Char* pTitle )
    {
        maPoints.push_back( ::std::make_pair( rPoint,
                                              ::rtl::OString( pTitle ) ) );
    }

    void DebugPlotter::plot( const B2DVector&	rVec,
                             const sal_Char* 	pTitle )
    {
        maVectors.push_back( ::std::make_pair( rVec,
                                               ::rtl::OString( pTitle ) ) );
    }

    void DebugPlotter::plot( const B2DCubicBezier&	rBezier,
                             const sal_Char* 		pTitle )
    {
        B2DPolygon aPoly;
        aPoly.append(rBezier.getStartPoint());
		aPoly.appendBezierSegment(rBezier.getControlPointA(), rBezier.getControlPointB(), rBezier.getEndPoint());
        maPolygons.push_back( ::std::make_pair( aPoly,
                                                ::rtl::OString( pTitle ) ) );
    }

    void DebugPlotter::plot( const B2DRange& rRange,
                             const sal_Char* pTitle )
    {
        maRanges.push_back( ::std::make_pair( rRange,
                                              ::rtl::OString( pTitle ) ) );
    }

    void DebugPlotter::plot( const B2DPolygon&	rPoly,
                             const sal_Char* 	pTitle )
    {
        maPolygons.push_back( ::std::make_pair( rPoly,
                                                ::rtl::OString( pTitle ) ) );
    }

    void DebugPlotter::plot( const B2DPolyPolygon&	rPoly,
                             const sal_Char* 		pTitle )
    {
        const ::rtl::OString aTitle( pTitle );
        const sal_uInt32 nCount( rPoly.count() );
        for( sal_uInt32 i=0; i<nCount; ++i )
            maPolygons.push_back( ::std::make_pair( rPoly.getB2DPolygon( i ),
                                                    aTitle ) );
    }

    void DebugPlotter::print( const sal_Char* pStr )
    {
        if( mpOutputStream )
            *mpOutputStream << pStr;
        else
            OSL_TRACE( pStr );
    }
}
