// Copyright (C)  2003  Dominique Devriese <devriese@kde.org>

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.

#include "special_constructors.h"

#include "calcpaths.h"
#include "common.h"
#include "conic-common.h"
#include "guiaction.h"
#include "kigpainter.h"

#include "../kig/kig_part.h"
#include "../modes/construct_mode.h"
#include "../objects/bogus_imp.h"
#include "../objects/centerofcurvature_type.h"
#include "../objects/circle_imp.h"
#include "../objects/conic_imp.h"
#include "../objects/conic_types.h"
#include "../objects/cubic_imp.h"
#include "../objects/intersection_types.h"
#include "../objects/inversion_type.h"
#include "../objects/line_imp.h"
#include "../objects/line_type.h"
#include "../objects/locus_imp.h"
#include "../objects/object_calcer.h"
#include "../objects/object_drawer.h"
#include "../objects/object_factory.h"
#include "../objects/object_holder.h"
#include "../objects/object_imp.h"
#include "../objects/object_type.h"
#include "../objects/other_imp.h"
#include "../objects/other_type.h"
#include "../objects/point_imp.h"
#include "../objects/point_type.h"
#include "../objects/polygon_imp.h"
#include "../objects/polygon_type.h"
#include "../objects/tangent_type.h"
#include "../objects/text_imp.h"
#include "../objects/transform_types.h"

#include <tqpen.h>

#include <tdelocale.h>

#include <algorithm>
#include <functional>

class ConicConicIntersectionConstructor
  : public StandardConstructorBase
{
protected:
  ArgsParser mparser;
public:
  ConicConicIntersectionConstructor();
  ~ConicConicIntersectionConstructor();

  void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
                   const KigDocument& ) const;
  std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
  void plug( KigPart* doc, KigGUIAction* kact );

  bool isTransform() const;
};

class ConicLineIntersectionConstructor
  : public MultiObjectTypeConstructor
{
public:
  ConicLineIntersectionConstructor();
  ~ConicLineIntersectionConstructor();
};

class ArcLineIntersectionConstructor
  : public MultiObjectTypeConstructor
{
public:
  ArcLineIntersectionConstructor();
  ~ArcLineIntersectionConstructor();
};

ConicRadicalConstructor::ConicRadicalConstructor()
  : StandardConstructorBase(
    I18N_NOOP( "Radical Lines for Conics" ),
    I18N_NOOP( "The lines constructed through the intersections "
               "of two conics.  This is also defined for "
               "non-intersecting conics." ),
    "conicsradicalline", mparser ),
    mtype( ConicRadicalType::instance() ),
    mparser( mtype->argsParser().without( IntImp::stype() ) )
{
}

ConicRadicalConstructor::~ConicRadicalConstructor()
{
}

void ConicRadicalConstructor::drawprelim(
  const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& doc ) const
{
  if ( parents.size() == 2 && parents[0]->imp()->inherits( ConicImp::stype() ) &&
       parents[1]->imp()->inherits( ConicImp::stype() ) )
  {
    Args args;
    std::transform( parents.begin(), parents.end(),
                    std::back_inserter( args ), std::mem_fn( &ObjectCalcer::imp ) );
    for ( int i = -1; i < 2; i += 2 )
    {
      IntImp root( i );
      IntImp zeroindex( 1 );
      args.push_back( &root );
      args.push_back( &zeroindex );
      ObjectImp* data = mtype->calc( args, doc );
      drawer.draw( *data, p, true );
      delete data; data = 0;
      args.pop_back();
      args.pop_back();
    };
  };
}

std::vector<ObjectHolder*> ConicRadicalConstructor::build( const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) const
{
  using namespace std;
  std::vector<ObjectHolder*> ret;
  ObjectCalcer* zeroindexcalcer = new ObjectConstCalcer( new IntImp( 1 ) );
  for ( int i = -1; i < 2; i += 2 )
  {
    std::vector<ObjectCalcer*> args;
    std::copy( os.begin(), os.end(), back_inserter( args ) );
    args.push_back( new ObjectConstCalcer( new IntImp( i ) ) );
    // we use only one zeroindex dataobject, so that if you switch one
    // radical line around, then the other switches along..
    args.push_back( zeroindexcalcer );
    ret.push_back(
      new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) );
  };
  return ret;
}

static const struct ArgsParser::spec argsspecpp[] =
{
  { PointImp::stype(), I18N_NOOP( "Moving Point" ),
    I18N_NOOP( "Select the moving point, which will be moved around while drawing the locus..." ), false },
  { PointImp::stype(), I18N_NOOP( "Following Point" ),
    I18N_NOOP( "Select the following point, whose locations the locus will be drawn through..." ), true }
};

LocusConstructor::LocusConstructor()
  : StandardConstructorBase( I18N_NOOP( "Locus" ), I18N_NOOP( "A locus" ),
                             "locus", margsparser ),
    margsparser( argsspecpp, 2 )
{
}

LocusConstructor::~LocusConstructor()
{
}

void LocusConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
                                   const KigDocument& ) const
{
  // this function is rather ugly, but it is necessary to do it this
  // way in order to play nice with Kig's design..

  if ( parents.size() != 2 ) return;
  const ObjectTypeCalcer* constrained = dynamic_cast<ObjectTypeCalcer*>( parents.front() );
  const ObjectCalcer* moving = parents.back();
  if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
  {
    // moving is in fact the constrained point.. swap them..
    moving = parents.front();
    constrained = dynamic_cast<const ObjectTypeCalcer*>( parents.back() );
    assert( constrained );
  };
  assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) );

  const ObjectImp* oimp = constrained->parents().back()->imp();
  if( !oimp->inherits( CurveImp::stype() ) )
    oimp = constrained->parents().front()->imp();
  assert( oimp->inherits( CurveImp::stype() ) );
  const CurveImp* cimp = static_cast<const CurveImp*>( oimp );

  ObjectHierarchy hier( constrained, moving );

  LocusImp limp( cimp->copy(), hier );
  drawer.draw( limp, p, true );
}

const int LocusConstructor::wantArgs(
 const std::vector<ObjectCalcer*>& os, const KigDocument&, const KigWidget&
 ) const
{
  int ret = margsparser.check( os );
  if ( ret == ArgsParser::Invalid ) return ret;
  else if ( os.size() != 2 ) return ret;
  if ( dynamic_cast<ObjectTypeCalcer*>( os.front() ) &&
       static_cast<ObjectTypeCalcer*>( os.front() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
  {
    std::set<ObjectCalcer*> children = getAllChildren( os.front() );
    return children.find( os.back() ) != children.end() ? ret : ArgsParser::Invalid;
  }
  if ( dynamic_cast<ObjectTypeCalcer*>( os.back() ) &&
       static_cast<ObjectTypeCalcer*>( os.back() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
  {
    std::set<ObjectCalcer*> children = getAllChildren( os.back() );
    return children.find( os.front() ) != children.end() ? ret : ArgsParser::Invalid;
  }
  return ArgsParser::Invalid;
}

std::vector<ObjectHolder*> LocusConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
{
  std::vector<ObjectHolder*> ret;
  assert( parents.size() == 2 );

  ObjectTypeCalcer* constrained = dynamic_cast<ObjectTypeCalcer*>( parents.front() );
  ObjectCalcer* moving = parents.back();
  if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
  {
    // moving is in fact the constrained point.. swap them..
    moving = parents.front();
    constrained = dynamic_cast<ObjectTypeCalcer*>( parents.back() );
    assert( constrained );
  };
  assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) );

  ret.push_back( ObjectFactory::instance()->locus( constrained, moving ) );
  return ret;
}

TQString LocusConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& os,
                                   const KigDocument&, const KigWidget& ) const
{
  if ( dynamic_cast<const ObjectTypeCalcer*>( &o ) &&
       static_cast<const ObjectTypeCalcer&>( o ).type()->inherits( ObjectType::ID_ConstrainedPointType ) &&
       ( os.empty() || !dynamic_cast<ObjectTypeCalcer*>( os[0] ) ||
         !static_cast<const ObjectTypeCalcer*>( os[0] )->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
    ) return i18n( "Moving Point" );
  else return i18n( "Dependent Point" );
}

void ConicRadicalConstructor::plug( KigPart*, KigGUIAction* )
{
}

void LocusConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool ConicRadicalConstructor::isTransform() const
{
  return mtype->isTransform();
}

bool LocusConstructor::isTransform() const
{
  return false;
}

/*
 * generic polygon constructor
 */

PolygonBNPTypeConstructor::PolygonBNPTypeConstructor()
  : mtype( PolygonBNPType::instance() )
{
}

PolygonBNPTypeConstructor::~PolygonBNPTypeConstructor()
{
}

const TQString PolygonBNPTypeConstructor::descriptiveName() const
{
  return i18n("Polygon by Its Vertices");
}

const TQString PolygonBNPTypeConstructor::description() const
{
  return i18n("Construct a polygon by giving its vertices");
}

const TQCString PolygonBNPTypeConstructor::iconFileName( const bool ) const
{
  return "kig_polygon";
}

const bool PolygonBNPTypeConstructor::isAlreadySelectedOK(
 const std::vector<ObjectCalcer*>& os, const int& pos ) const
{
  if ( pos == 0 && os.size() >= 3 ) return true;
  return false;
}

const int PolygonBNPTypeConstructor::wantArgs( const std::vector<ObjectCalcer*>& os,
                                             const KigDocument&,
                                             const KigWidget& ) const
{
  int count=os.size() - 1;

  for ( int i = 0; i <= count; i++ )
  {
    if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid;
  }
  if ( count < 3 ) return ArgsParser::Valid;
  if ( os[0] == os[count] ) return ArgsParser::Complete;
  return ArgsParser::Valid;
}

void PolygonBNPTypeConstructor::handleArgs(
  const std::vector<ObjectCalcer*>& os, KigPart& d,
  KigWidget& v ) const
{
  std::vector<ObjectHolder*> bos = build( os, d.document(), v );
  for ( std::vector<ObjectHolder*>::iterator i = bos.begin();
        i != bos.end(); ++i )
  {
    (*i)->calc( d.document() );
  }

  d.addObjects( bos );
}

void PolygonBNPTypeConstructor::handlePrelim(
  KigPainter& p, const std::vector<ObjectCalcer*>& os,
  const KigDocument& d, const KigWidget&
  ) const
{
  uint count = os.size();
  if ( count < 2 ) return;

  for ( uint i = 0; i < count; i++ )
  {
    assert ( os[i]->imp()->inherits( PointImp::stype() ) );
  }

  std::vector<ObjectCalcer*> args = os;
  p.setBrushStyle( TQt::NoBrush );
  p.setBrushColor( TQt::red );
  p.setPen( TQPen ( TQt::red,  1) );
  p.setWidth( -1 ); // -1 means the default width for the object being
                    // drawn..

  ObjectDrawer drawer( TQt::red );
  drawprelim( drawer, p, args, d );
}

TQString PolygonBNPTypeConstructor::useText( const ObjectCalcer&, const std::vector<ObjectCalcer*>& os,
                                          const KigDocument&, const KigWidget& ) const
{
  if ( os.size() > 3 )
    return i18n("... with this vertex (click on the first vertex to terminate construction)");
  else return i18n("Construct a polygon with this vertex");
}

TQString PolygonBNPTypeConstructor::selectStatement(
  const std::vector<ObjectCalcer*>&, const KigDocument&,
  const KigWidget& ) const
{
  return i18n("Select a point to be a vertex of the new polygon...");
}

void PolygonBNPTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
                                   const KigDocument& ) const
{
  if ( parents.size() < 2 ) return;

  std::vector<Coordinate> points;

  for ( uint i = 0; i < parents.size(); ++i )
  {
    const Coordinate vertex =
        static_cast<const PointImp*>( parents[i]->imp() )->coordinate();
    points.push_back( vertex );
  }

  if ( parents.size() == 2 )
  {
    SegmentImp segment = SegmentImp( points[0], points[1] );
    drawer.draw( segment, p, true );
  } else {
    PolygonImp polygon = PolygonImp( points );
    drawer.draw( polygon, p, true );
  }
}

std::vector<ObjectHolder*> PolygonBNPTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
{
  uint count = parents.size() - 1;
  assert ( count >= 3 );
  std::vector<ObjectCalcer*> args;
  for ( uint i = 0; i < count; ++i ) args.push_back( parents[i] );
  ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args );
  ObjectHolder* h = new ObjectHolder( calcer );
  std::vector<ObjectHolder*> ret;
  ret.push_back( h );
  return ret;
}

void PolygonBNPTypeConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool PolygonBNPTypeConstructor::isTransform() const
{
  return false;
}

/*
 * construction of polygon vertices
 */

static const struct ArgsParser::spec argsspecpv[] =
{
  { PolygonImp::stype(), I18N_NOOP( "Polygon" ),
    I18N_NOOP( "Construct the vertices of this polygon..." ), true }
};

PolygonVertexTypeConstructor::PolygonVertexTypeConstructor()
  : StandardConstructorBase( I18N_NOOP( "Vertices of a Polygon" ),
       I18N_NOOP( "The vertices of a polygon." ),
       "polygonvertices", margsparser ),
    mtype( PolygonVertexType::instance() ),
    margsparser( argsspecpv, 1 )
{
}

PolygonVertexTypeConstructor::~PolygonVertexTypeConstructor()
{
}

void PolygonVertexTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
                                   const KigDocument& ) const
{
  if ( parents.size() != 1 ) return;

  const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
  const std::vector<Coordinate> points = polygon->points();

  int sides = points.size();
  for ( int i = 0; i < sides; ++i )
  {
    PointImp point = PointImp( points[i] );
    drawer.draw( point, p, true );
  }
}

std::vector<ObjectHolder*> PolygonVertexTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
{
  std::vector<ObjectHolder*> ret;
  assert( parents.size() == 1 );
  const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
  const std::vector<Coordinate> points = polygon->points();

  int sides = points.size();

  for ( int i = 0; i < sides; ++i )
  {
    ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) );
    std::vector<ObjectCalcer*> args( parents );
    args.push_back( d );
    ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) );
  }
  return ret;
}

void PolygonVertexTypeConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool PolygonVertexTypeConstructor::isTransform() const
{
  return false;
}

/*
 * construction of polygon sides
 */

static const struct ArgsParser::spec argsspecps[] =
{
  { PolygonImp::stype(), I18N_NOOP( "Polygon" ),
    I18N_NOOP( "Construct the sides of this polygon..." ), false }
};

PolygonSideTypeConstructor::PolygonSideTypeConstructor()
  : StandardConstructorBase( I18N_NOOP( "Sides of a Polygon" ),
       I18N_NOOP( "The sides of a polygon." ),
       "polygonsides", margsparser ),
    mtype( PolygonSideType::instance() ),
    margsparser( argsspecps, 1 )
{
}

PolygonSideTypeConstructor::~PolygonSideTypeConstructor()
{
}

void PolygonSideTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
                                   const KigDocument& ) const
{
  if ( parents.size() != 1 ) return;

  const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
  const std::vector<Coordinate> points = polygon->points();

  uint sides = points.size();
  for ( uint i = 0; i < sides; ++i )
  {
    uint nexti = ( i + 1 < sides )?(i + 1):0;
    SegmentImp segment = SegmentImp( points[i], points[nexti] );
    drawer.draw( segment, p, true );
  }
}

std::vector<ObjectHolder*> PolygonSideTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
{
  std::vector<ObjectHolder*> ret;
  assert( parents.size() == 1 );
  const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
  const std::vector<Coordinate> points = polygon->points();

  uint sides = points.size();

  for ( uint i = 0; i < sides; ++i )
  {
    ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) );
    std::vector<ObjectCalcer*> args( parents );
    args.push_back( d );
    ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) );
  }
  return ret;
}

void PolygonSideTypeConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool PolygonSideTypeConstructor::isTransform() const
{
  return false;
}

/*
 * polygon by center and vertex
 */

PolygonBCVConstructor::PolygonBCVConstructor()
  : mtype( PolygonBCVType::instance() )
{
}

PolygonBCVConstructor::~PolygonBCVConstructor()
{
}

const TQString PolygonBCVConstructor::descriptiveName() const
{
  return i18n("Regular Polygon with Given Center");
}

const TQString PolygonBCVConstructor::description() const
{
  return i18n("Construct a regular polygon with a given center and vertex");
}

const TQCString PolygonBCVConstructor::iconFileName( const bool ) const
{
  return "hexagonbcv";
}

const bool PolygonBCVConstructor::isAlreadySelectedOK(
 const std::vector<ObjectCalcer*>&, const int& ) const
{
  return false;
}

const int PolygonBCVConstructor::wantArgs( const std::vector<ObjectCalcer*>& os,
                                           const KigDocument&,
                                           const KigWidget& ) const
{
  if ( os.size() > 3 ) return ArgsParser::Invalid;

  uint imax = ( os.size() <= 2) ? os.size() : 2;
  for ( uint i = 0; i < imax; ++i )
    if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid;

  if ( os.size() < 3 ) return ArgsParser::Valid;

  if ( ! ( os[2]->imp()->inherits( BogusPointImp::stype() ) ) )
    return ArgsParser::Invalid;

  return ArgsParser::Complete;
}

void PolygonBCVConstructor::handleArgs(
  const std::vector<ObjectCalcer*>& os, KigPart& d,
  KigWidget& v ) const
{
  std::vector<ObjectHolder*> bos = build( os, d.document(), v );
  for ( std::vector<ObjectHolder*>::iterator i = bos.begin();
        i != bos.end(); ++i )
  {
    (*i)->calc( d.document() );
  }

  d.addObjects( bos );
}

void PolygonBCVConstructor::handlePrelim(
  KigPainter& p, const std::vector<ObjectCalcer*>& os,
  const KigDocument& d, const KigWidget&
  ) const
{
  if ( os.size() < 2 ) return;

  for ( uint i = 0; i < 2; i++ )
  {
    assert ( os[i]->imp()->inherits( PointImp::stype() ) );
  }

  Coordinate c = static_cast<const PointImp*>( os[0]->imp() )->coordinate();
  Coordinate v = static_cast<const PointImp*>( os[1]->imp() )->coordinate();

  int nsides = 6;
  bool moreinfo = false;
  int winding = 0;    // 0 means allow winding > 1
  if ( os.size() == 3 )
  {
    assert ( os[2]->imp()->inherits( BogusPointImp::stype() ) );
    Coordinate cntrl = static_cast<const PointImp*>( os[2]->imp() )->coordinate();
    nsides = computeNsides( c, v, cntrl, winding );
    moreinfo = true;
  }

  std::vector<ObjectCalcer*> args;
  args.push_back( os[0] );
  args.push_back( os[1] );
  ObjectConstCalcer* ns = new ObjectConstCalcer( new IntImp( nsides ) );
  args.push_back( ns );
  if ( winding > 1 )
  {
    ns = new ObjectConstCalcer( new IntImp( winding ) );
    args.push_back( ns );
  }

  p.setBrushStyle( TQt::NoBrush );
  p.setBrushColor( TQt::red );
  p.setPen( TQPen ( TQt::red,  1) );
  p.setWidth( -1 ); // -1 means the default width for the object being
                    // drawn..

  ObjectDrawer drawer( TQt::red );
  drawprelim( drawer, p, args, d );
  if ( moreinfo )
  {
    p.setPointStyle( 1 );
    p.setWidth( 6 );
    double ro = 1.0/(2.5);
    Coordinate where = getRotatedCoord( c, (1-ro)*c+ro*v, 4*M_PI/5.0 );
    PointImp ptn = PointImp( where );
    TextImp text = TextImp( "(5,2)", where, false );
    ptn.draw( p );
    text.draw( p );
    for ( int i = 3; i < 9; ++i )
    {
      where = getRotatedCoord( c, v, 2.0*M_PI/i );
      ptn = PointImp( where );
      ptn.draw( p );
      if ( i > 5 ) continue;
      text = TextImp( TQString( "(%1)" ).arg(i), where, false );
      text.draw( p );
    }
    p.setStyle( TQt::DotLine );
    p.setWidth( 1 );
    double radius = ( v - c ).length();
    CircleImp circle = CircleImp( c, radius );
    circle.draw( p );
    for ( int i = 2; i < 5; i++ )
    {
      ro = 1.0/(i+0.5);
      CircleImp circle = CircleImp( c, ro*radius );
      circle.draw( p );
    }
  }
  delete_all( args.begin() + 2, args.end() );
}

std::vector<ObjectHolder*> PolygonBCVConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
{
  assert ( parents.size() == 3 );
  std::vector<ObjectCalcer*> args;

  Coordinate c = static_cast<const PointImp*>( parents[0]->imp() )->coordinate();
  Coordinate v = static_cast<const PointImp*>( parents[1]->imp() )->coordinate();
  Coordinate cntrl = static_cast<const PointImp*>( parents[2]->imp() )->coordinate();

  args.push_back( parents[0] );
  args.push_back( parents[1] );
  int winding = 0;
  int nsides = computeNsides( c, v, cntrl, winding );
  ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( nsides ) );
  args.push_back( d );
  if ( winding > 1 )
  {
    d = new ObjectConstCalcer( new IntImp( winding ) );
    args.push_back( d );
  }

  ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args );
  ObjectHolder* h = new ObjectHolder( calcer );
  std::vector<ObjectHolder*> ret;
  ret.push_back( h );
  return ret;
}

TQString PolygonBCVConstructor::useText( const ObjectCalcer&, const std::vector<ObjectCalcer*>& os,
                                          const KigDocument&, const KigWidget& ) const
{
  switch ( os.size() )
  {
    case 1:
    return i18n( "Construct a regular polygon with this center" );
    break;

    case 2:
    return i18n( "Construct a regular polygon with this vertex" );
    break;

    case 3:
    Coordinate c = static_cast<const PointImp*>( os[0]->imp() )->coordinate();
    Coordinate v = static_cast<const PointImp*>( os[1]->imp() )->coordinate();
    Coordinate cntrl = static_cast<const PointImp*>( os[2]->imp() )->coordinate();
    int winding = 0;
    int nsides = computeNsides( c, v, cntrl, winding );

    if ( winding > 1 )
    {
      TQString result = TQString(
        i18n( "Adjust the number of sides (%1/%2)" )
        ).arg( nsides ).arg( winding );
      return result;
    } else
    {
      TQString result = TQString(
        i18n( "Adjust the number of sides (%1)" )
        ).arg( nsides );
      return result;
    }
    break;
  }

  return "";
}

TQString PolygonBCVConstructor::selectStatement(
  const std::vector<ObjectCalcer*>& os, const KigDocument&,
  const KigWidget& ) const
{
  switch ( os.size() )
  {
    case 1:
    return i18n( "Select the center of the new polygon..." );
    break;

    case 2:
    return i18n( "Select a vertex for the new polygon..." );
    break;

    case 3:
    return i18n( "Move the cursor to get the desired number of sides..." );
    break;
  }

  return "";
}

void PolygonBCVConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
                                   const KigDocument& doc ) const
{
  if ( parents.size() < 3 || parents.size() > 4 ) return;

  assert ( parents[0]->imp()->inherits( PointImp::stype() ) &&
           parents[1]->imp()->inherits( PointImp::stype() ) &&
           parents[2]->imp()->inherits( IntImp::stype() ) );

  if ( parents.size() == 4 )
    assert ( parents[3]->imp()->inherits( IntImp::stype() ) );

  Args args;
  std::transform( parents.begin(), parents.end(),
               std::back_inserter( args ), std::mem_fn( &ObjectCalcer::imp ) );

  ObjectImp* data = mtype->calc( args, doc );
  drawer.draw( *data, p, true );
  delete data;
  data = 0;
}

void PolygonBCVConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool PolygonBCVConstructor::isTransform() const
{
  return false;
}

Coordinate PolygonBCVConstructor::getRotatedCoord( const Coordinate& c, 
               const Coordinate& v, double alpha ) const
{
  double cosalpha = cos(alpha);
  double sinalpha = sin(alpha);
  double dx = v.x - c.x;
  double dy = v.y - c.y;
  return c + Coordinate( cosalpha*dx - sinalpha*dy, sinalpha*dx + cosalpha*dy );
}

int PolygonBCVConstructor::computeNsides ( const Coordinate& c,
        const Coordinate& v, const Coordinate& cntrl, int& winding ) const
{
  Coordinate lvect = v - c;
  Coordinate rvect = cntrl - c;

  double angle = atan2( rvect.y, rvect.x ) - atan2( lvect.y, lvect.x );
  angle = fabs( angle/(2*M_PI) );
  while ( angle > 1 ) angle -= 1;
  if ( angle > 0.5 ) angle = 1 - angle;

  double realsides = 1.0/angle;    // this is bigger that 2
  if ( angle == 0. ) realsides = 3;
  if ( winding <= 0 )              // free to compute winding
  {
    winding = 1;
    double ratio = lvect.length()/rvect.length();
    winding = int ( ratio );
    if ( winding < 1 ) winding = 1;
    if ( winding > 50 ) winding = 50;
  }
  int nsides = int( winding*realsides + 0.5 );  // nsides/winding should be reduced!
  if ( nsides > 100 ) nsides = 100;     // well, 100 seems large enough!
  if ( nsides < 3 ) nsides = 3;
  while ( !relativePrimes ( nsides, winding ) ) ++nsides;
  return nsides;
}

/*
 * ConicConic intersection...
 */

static const ArgsParser::spec argsspectc[] = {
  { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true },
  { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true }
};

ConicConicIntersectionConstructor::ConicConicIntersectionConstructor()
  : StandardConstructorBase( "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
                             "curvelineintersection", mparser ),
    mparser( argsspectc, 2 )
{
}

ConicConicIntersectionConstructor::~ConicConicIntersectionConstructor()
{
}

void ConicConicIntersectionConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
                                                    const KigDocument& ) const
{
  if ( parents.size() != 2 ) return;
  assert ( parents[0]->imp()->inherits( ConicImp::stype() ) &&
           parents[1]->imp()->inherits( ConicImp::stype() ) );
  const ConicCartesianData conica =
    static_cast<const ConicImp*>( parents[0]->imp() )->cartesianData();
  const ConicCartesianData conicb =
    static_cast<const ConicImp*>( parents[1]->imp() )->cartesianData();
  bool ok = true;
  for ( int wr = -1; wr < 2; wr += 2 )
  {
    LineData radical = calcConicRadical( conica, conicb, wr, 1, ok );
    if ( ok )
    {
      for ( int wi = -1; wi < 2; wi += 2 )
      {
        Coordinate c = calcConicLineIntersect( conica, radical, 0.0, wi );
        if ( c.valid() ) {
          PointImp pi( c );
          drawer.draw( pi, p, true );
        }
      };
    };
  };
}

std::vector<ObjectHolder*> ConicConicIntersectionConstructor::build(
  const std::vector<ObjectCalcer*>& os, KigDocument& doc, KigWidget& ) const
{
  assert( os.size() == 2 );
  std::vector<ObjectHolder*> ret;
  ObjectCalcer* conica = os[0];
  ObjectConstCalcer* zeroindexdo = new ObjectConstCalcer( new IntImp( 1 ) );

  for ( int wr = -1; wr < 2; wr += 2 )
  {
    std::vector<ObjectCalcer*> args = os;
    args.push_back( new ObjectConstCalcer( new IntImp( wr ) ) );
    args.push_back( zeroindexdo );
    ObjectTypeCalcer* radical =
      new ObjectTypeCalcer( ConicRadicalType::instance(), args );
    radical->calc( doc );
    for ( int wi = -1; wi < 2; wi += 2 )
    {
      args.clear();
      args.push_back( conica );
      args.push_back( radical );
      args.push_back( new ObjectConstCalcer( new IntImp( wi ) ) );
      ret.push_back(
        new ObjectHolder(
          new ObjectTypeCalcer(
            ConicLineIntersectionType::instance(), args ) ) );
    };
  };
  return ret;
}

void ConicConicIntersectionConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool ConicConicIntersectionConstructor::isTransform() const
{
  return false;
}

ConicLineIntersectionConstructor::ConicLineIntersectionConstructor()
  : MultiObjectTypeConstructor(
    ConicLineIntersectionType::instance(),
    "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
    "curvelineintersection", -1, 1 )
{
}

ConicLineIntersectionConstructor::~ConicLineIntersectionConstructor()
{
}

ArcLineIntersectionConstructor::ArcLineIntersectionConstructor()
  : MultiObjectTypeConstructor(
    ArcLineIntersectionType::instance(),
    "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
    "curvelineintersection", -1, 1 )
{
}

ArcLineIntersectionConstructor::~ArcLineIntersectionConstructor()
{
}

TQString ConicRadicalConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>&,
                                          const KigDocument&, const KigWidget& ) const
{
  if ( o.imp()->inherits( CircleImp::stype() ) )
    return i18n( "Construct the Radical Lines of This Circle" );
  else
    return i18n( "Construct the Radical Lines of This Conic" );
}

/*
 * generic affinity and generic projectivity.  A unique affinity can be
 * obtained by specifying the image of three points (four for projectivity)
 * in the end we need, besides the object to be transformed, a total of
 * six point or (alternatively) two triangles; our affinity will map the
 * first triangle onto the second with corresponding ordering of their
 * vertices.  Since we allow for two different ways of specifying the six
 * points we shall use a Generic constructor, like that for intersections.
 */

GenericAffinityConstructor::GenericAffinityConstructor()
  : MergeObjectConstructor(
    I18N_NOOP( "Generic Affinity" ),
    I18N_NOOP( "The unique affinity that maps three points (or a triangle) onto three other points (or a triangle)" ),
    "genericaffinity" )
{
  SimpleObjectTypeConstructor* b2tr =
     new SimpleObjectTypeConstructor(
      AffinityB2TrType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "genericaffinity" );

  SimpleObjectTypeConstructor* gi3p =
     new SimpleObjectTypeConstructor(
      AffinityGI3PType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "genericaffinity" );

  merge( b2tr );
  merge( gi3p );
}

GenericAffinityConstructor::~GenericAffinityConstructor() {}

GenericProjectivityConstructor::GenericProjectivityConstructor()
  : MergeObjectConstructor(
    I18N_NOOP( "Generic Projective Transformation" ),
    I18N_NOOP( "The unique projective transformation that maps four points (or a quadrilateral) onto four other points (or a quadrilateral)" ),
    "genericprojectivity" )
{
  SimpleObjectTypeConstructor* b2qu =
     new SimpleObjectTypeConstructor(
      ProjectivityB2QuType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "genericprojectivity" );

  SimpleObjectTypeConstructor* gi4p =
     new SimpleObjectTypeConstructor(
      ProjectivityGI4PType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "genericprojectivity" );

  merge( b2qu );
  merge( gi4p );
}

GenericProjectivityConstructor::~GenericProjectivityConstructor() {}

/*
 * inversion of points, lines with respect to a circle
 */

InversionConstructor::InversionConstructor()
  : MergeObjectConstructor(
    I18N_NOOP( "Inversion of Point, Line or Circle" ),
    I18N_NOOP( "The inversion of a point, line or circle with respect to a circle" ),
    "inversion" )
{
  SimpleObjectTypeConstructor* pointobj =
     new SimpleObjectTypeConstructor(
      InvertPointType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "inversion" );

  SimpleObjectTypeConstructor* lineobj =
     new SimpleObjectTypeConstructor(
      InvertLineType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "inversion" );

  SimpleObjectTypeConstructor* segmentobj =
     new SimpleObjectTypeConstructor(
      InvertSegmentType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "inversion" );

  SimpleObjectTypeConstructor* circleobj =
     new SimpleObjectTypeConstructor(
      InvertCircleType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "inversion" );

  SimpleObjectTypeConstructor* arcobj =
     new SimpleObjectTypeConstructor(
      InvertArcType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "inversion" );

  merge( arcobj );
  merge( circleobj );
  merge( pointobj );
  merge( segmentobj );
  merge( lineobj );
}

InversionConstructor::~InversionConstructor() {}

/*
 * Transport of Measure
 */

MeasureTransportConstructor::MeasureTransportConstructor()
  : mtype( MeasureTransportType::instance() )
{
}

MeasureTransportConstructor::~MeasureTransportConstructor()
{
}

const TQString MeasureTransportConstructor::descriptiveName() const
{
  return i18n("Measure Transport");
}

const TQString MeasureTransportConstructor::description() const
{
  return i18n("Transport the measure of a segment or arc over a line or circle.");
}

const TQCString MeasureTransportConstructor::iconFileName( const bool ) const
{
  return "measuretransport";
}

const bool MeasureTransportConstructor::isAlreadySelectedOK(
 const std::vector<ObjectCalcer*>&, const int& ) const
{
  return false;
}

/*
 * we want the arguments in the exact order, this makes
 * the code simpler, but I guess it is also less confusing
 * to the user
 */

const int MeasureTransportConstructor::wantArgs(
                                const std::vector<ObjectCalcer*>& os,
                                const KigDocument&,
                                const KigWidget& ) const
{
  if ( os.size() == 0 ) return ArgsParser::Valid;

  if ( ! os[0]->imp()->inherits( SegmentImp::stype() ) &&
       ! os[0]->imp()->inherits( ArcImp::stype() ) )
    return ArgsParser::Invalid;

  if ( os.size() == 1 ) return ArgsParser::Valid;

  if ( ! os[1]->imp()->inherits( LineImp::stype() ) &&
       ! os[1]->imp()->inherits( CircleImp::stype() ) )
    return ArgsParser::Invalid;

  if ( os.size() == 2 ) return ArgsParser::Valid;

  if ( ! os[2]->imp()->inherits( PointImp::stype() ) )
    return ArgsParser::Invalid;

  // we here use the "isPointOnCurve", which relies on
  // "by construction" incidence, instead of a numerical
  // check
  if ( ! isPointOnCurve( os[2], os[1] ) )
    return ArgsParser::Invalid;

  if ( os.size() == 3 ) return ArgsParser::Complete;

  return ArgsParser::Invalid;
}

void MeasureTransportConstructor::handleArgs(
  const std::vector<ObjectCalcer*>& os, KigPart& d,
  KigWidget& v ) const
{
  std::vector<ObjectHolder*> bos = build( os, d.document(), v );
  for ( std::vector<ObjectHolder*>::iterator i = bos.begin();
        i != bos.end(); ++i )
  {
    (*i)->calc( d.document() );
  }

  d.addObjects( bos );
}

void MeasureTransportConstructor::handlePrelim(
  KigPainter& p, const std::vector<ObjectCalcer*>& os,
  const KigDocument& d, const KigWidget&
  ) const
{
  p.setBrushStyle( TQt::NoBrush );
  p.setBrushColor( TQt::red );
  p.setPen( TQPen ( TQt::red,  1) );
  p.setWidth( -1 ); // -1 means the default width for the object being
                    // drawn..

  ObjectDrawer drawer( TQt::red );
  drawprelim( drawer, p, os, d );
}

void MeasureTransportConstructor::drawprelim( const ObjectDrawer& drawer,
                         KigPainter& p,
                         const std::vector<ObjectCalcer*>& parents,
                         const KigDocument& doc ) const
{
  Args args;
  using namespace std;
  transform( parents.begin(), parents.end(),
             back_inserter( args ), mem_fn( &ObjectCalcer::imp ) );
  ObjectImp* data = mtype->calc( args, doc );
  drawer.draw( *data, p, true );
  delete data;
}

TQString MeasureTransportConstructor::useText( const ObjectCalcer& o,
                        const std::vector<ObjectCalcer*>& os,
                        const KigDocument&, const KigWidget& ) const
{
  if ( o.imp()->inherits( SegmentImp::stype() ) )
    return i18n("Segment to transport");
  if ( o.imp()->inherits( ArcImp::stype() ) )
    return i18n("Arc to transport");
  if ( o.imp()->inherits( LineImp::stype() ) )
    return i18n("Transport a measure on this line");
  if ( o.imp()->inherits( CircleImp::stype() ) )
    return i18n("Transport a measure on this circle");
  if ( o.imp()->inherits( PointImp::stype() ) )
  {
    if ( os[1]->imp()->inherits( CircleImp::stype() ) )
      return i18n("Start transport from this point of the circle");
    if ( os[1]->imp()->inherits( LineImp::stype() ) )
      return i18n("Start transport from this point of the line");
    else
      return i18n("Start transport from this point of the curve");
      // well, this isn't impemented yet, should never get here
  }
  return "";
}

TQString MeasureTransportConstructor::selectStatement(
  const std::vector<ObjectCalcer*>&, const KigDocument&,
  const KigWidget& ) const
{
//TODO
  return i18n("Select a point to be a vertex of the new polygon...");
}

std::vector<ObjectHolder*> MeasureTransportConstructor::build(
    const std::vector<ObjectCalcer*>& parents,
    KigDocument&, KigWidget& ) const
{
  assert ( parents.size() == 3 );
//  std::vector<ObjectCalcer*> args;
//  for ( uint i = 0; i < count; ++i ) args.push_back( parents[i] );
  ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, parents );
  ObjectHolder* h = new ObjectHolder( calcer );
  std::vector<ObjectHolder*> ret;
  ret.push_back( h );
  return ret;
}

void MeasureTransportConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool MeasureTransportConstructor::isTransform() const
{
  return false;
}

/*
 * Generic intersection
 */

GenericIntersectionConstructor::GenericIntersectionConstructor()
  : MergeObjectConstructor(
    I18N_NOOP( "Intersect" ),
    I18N_NOOP( "The intersection of two objects" ),
    "curvelineintersection" )
{
  // intersection type..
  // There is one "toplevel" object_constructor, that is composed
  // of multiple subconstructors..  First we build the
  // subconstructors:
  SimpleObjectTypeConstructor* lineline =
    new SimpleObjectTypeConstructor(
      LineLineIntersectionType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "curvelineintersection" );

  ObjectConstructor* lineconic =
    new ConicLineIntersectionConstructor();

  ObjectConstructor* arcline =
    new ArcLineIntersectionConstructor();

  MultiObjectTypeConstructor* linecubic =
    new MultiObjectTypeConstructor(
      LineCubicIntersectionType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "curvelineintersection", 1, 2, 3 );

  ObjectConstructor* conicconic =
    new ConicConicIntersectionConstructor();

  MultiObjectTypeConstructor* circlecircle =
    new MultiObjectTypeConstructor(
      CircleCircleIntersectionType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "circlecircleintersection", -1, 1 );

  SimpleObjectTypeConstructor* polygonline =
    new SimpleObjectTypeConstructor(
      PolygonLineIntersectionType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "curvelineintersection" );

  merge( lineline );
  merge( circlecircle );
  merge( lineconic );
  merge( linecubic );
  merge( conicconic );
  merge( arcline );
  merge( polygonline );
}

GenericIntersectionConstructor::~GenericIntersectionConstructor()
{
}

bool GenericIntersectionConstructor::isIntersection() const
{
  return true;
}

TQString GenericIntersectionConstructor::useText(
  const ObjectCalcer& o, const std::vector<ObjectCalcer*>& os,
  const KigDocument&, const KigWidget& ) const
{
  TQString preamble;
  switch (os.size())
  {
    case 1:
      if ( o.imp()->inherits( CircleImp::stype() ) )
        return i18n( "Intersect this Circle" );
      else if ( o.imp()->inherits( ConicImp::stype() ) )
        return i18n( "Intersect this Conic" );
      else if ( o.imp()->inherits( AbstractLineImp::stype() ) )
        return i18n( "Intersect this Line" );
      else if ( o.imp()->inherits( CubicImp::stype() ) )
        return i18n( "Intersect this Cubic Curve" );
      else if ( o.imp()->inherits( ArcImp::stype() ) )
        return i18n( "Intersect this Arc" );
      else if ( o.imp()->inherits( PolygonImp::stype() ) )
        return i18n( "Intersect this Polygon" );
      else assert( false );
      break;
    case 2:
      if ( o.imp()->inherits( CircleImp::stype() ) )
        return i18n( "with this Circle" );
      else if ( o.imp()->inherits( ConicImp::stype() ) )
        return i18n( "with this Conic" );
      else if ( o.imp()->inherits( AbstractLineImp::stype() ) )
        return i18n( "with this Line" );
      else if ( o.imp()->inherits( CubicImp::stype() ) )
        return i18n( "with this Cubic Curve" );
      else if ( o.imp()->inherits( ArcImp::stype() ) )
        return i18n( "with this Arc" );
      else if ( o.imp()->inherits( PolygonImp::stype() ) )
        return i18n( "with this Polygon" );
      else assert( false );
      break;
  }

  return TQString();
}

static const ArgsParser::spec argsspecMidPointOfTwoPoints[] =
{
  { PointImp::stype(), I18N_NOOP( "Construct Midpoint of This Point and Another One" ),
    I18N_NOOP( "Select the first of the points of which you want to construct the midpoint..." ), false },
  { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another one" ),
    I18N_NOOP( "Select the other of the points of which to construct the midpoint..." ), false }
};

MidPointOfTwoPointsConstructor::MidPointOfTwoPointsConstructor()
  : StandardConstructorBase( "Mid Point",
                             "Construct the midpoint of two points",
                             "bisection", mparser ),
    mparser( argsspecMidPointOfTwoPoints, 2 )
{
}

MidPointOfTwoPointsConstructor::~MidPointOfTwoPointsConstructor()
{
}

void MidPointOfTwoPointsConstructor::drawprelim(
  const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
  const KigDocument& ) const
{
  if ( parents.size() != 2 ) return;
  assert( parents[0]->imp()->inherits( PointImp::stype() ) );
  assert( parents[1]->imp()->inherits( PointImp::stype() ) );
  const Coordinate m =
    ( static_cast<const PointImp*>( parents[0]->imp() )->coordinate() +
      static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2;
  drawer.draw( PointImp( m ), p, true );
}

std::vector<ObjectHolder*> MidPointOfTwoPointsConstructor::build(
  const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& ) const
{
  ObjectTypeCalcer* seg = new ObjectTypeCalcer( SegmentABType::instance(), os );
  seg->calc( d );
  int index = seg->imp()->propertiesInternalNames().findIndex( "mid-point" );
  assert( index != -1 );
  ObjectPropertyCalcer* prop = new ObjectPropertyCalcer( seg, index );
  prop->calc( d );
  std::vector<ObjectHolder*> ret;
  ret.push_back( new ObjectHolder( prop ) );
  return ret;
}

void MidPointOfTwoPointsConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool MidPointOfTwoPointsConstructor::isTransform() const
{
  return false;
}

TestConstructor::TestConstructor( const ArgsParserObjectType* type, const char* descname,
                                  const char* desc, const char* iconfile )
  : StandardConstructorBase( descname, desc, iconfile, type->argsParser() ),
    mtype( type )
{
}

TestConstructor::~TestConstructor()
{
}

void TestConstructor::drawprelim( const ObjectDrawer&, KigPainter&, const std::vector<ObjectCalcer*>&,
                                  const KigDocument& ) const
{
  // not used, only here because of the wrong
  // ObjectConstructor-GUIAction design.  See the TODO
}

std::vector<ObjectHolder*> TestConstructor::build( const std::vector<ObjectCalcer*>&, KigDocument&,
                                                   KigWidget& ) const
{
  // not used, only here because of the wrong
  // ObjectConstructor-GUIAction design.  See the TODO
  std::vector<ObjectHolder*> ret;
  return ret;
}

void TestConstructor::plug( KigPart*, KigGUIAction* )
{
}

bool TestConstructor::isTransform() const
{
  return false;
}

bool TestConstructor::isTest() const
{
  return true;
}

BaseConstructMode* TestConstructor::constructMode( KigPart& doc )
{
  return new TestConstructMode( doc, mtype );
}

const int TestConstructor::wantArgs( const std::vector<ObjectCalcer*>& os,
                                     const KigDocument& d, const KigWidget& v ) const
{
  int ret = StandardConstructorBase::wantArgs( os, d, v );
  if ( ret == ArgsParser::Complete ) ret = ArgsParser::Valid;
  return ret;
}

TQString GenericIntersectionConstructor::selectStatement(
  const std::vector<ObjectCalcer*>& sel, const KigDocument&,
  const KigWidget& ) const
{
  if ( sel.size() == 0 )
    return i18n( "Select the first object to intersect..." );
  else
    return i18n( "Select the second object to intersect..." );
}

TangentConstructor::TangentConstructor()
  : MergeObjectConstructor(
    I18N_NOOP( "Tangent" ),
    I18N_NOOP( "The line tangent to a curve" ),
    "tangent" )
{
  SimpleObjectTypeConstructor* conic =
    new SimpleObjectTypeConstructor(
      TangentConicType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "tangentconic" );

  SimpleObjectTypeConstructor* arc =
    new SimpleObjectTypeConstructor(
      TangentArcType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "tangentarc" );

  SimpleObjectTypeConstructor* cubic =
    new SimpleObjectTypeConstructor(
      TangentCubicType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "tangentcubic" );

  SimpleObjectTypeConstructor* curve =
    new SimpleObjectTypeConstructor(
      TangentCurveType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "tangentcurve" );

  merge( conic );
  merge( arc );
  merge( cubic );
  merge( curve );
}

TangentConstructor::~TangentConstructor()
{
}

TQString TangentConstructor::useText(
  const ObjectCalcer& o, const std::vector<ObjectCalcer*>&,
  const KigDocument&, const KigWidget& ) const
{
  if ( o.imp()->inherits( CircleImp::stype() ) )
    return i18n( "Tangent to This Circle" );
  else if ( o.imp()->inherits( ConicImp::stype() ) )
    return i18n( "Tangent to This Conic" );
  else if ( o.imp()->inherits( ArcImp::stype() ) )
    return i18n( "Tangent to This Arc" );
  else if ( o.imp()->inherits( CubicImp::stype() ) )
    return i18n( "Tangent to This Cubic Curve" );
  else if ( o.imp()->inherits( CurveImp::stype() ) )
    return i18n( "Tangent to This Curve" );
  else if ( o.imp()->inherits( PointImp::stype() ) )
    return i18n( "Tangent at This Point" );
//  else assert( false );
  return TQString();
}

//TQString TangentConstructor::selectStatement(
//  const std::vector<ObjectCalcer*>& sel, const KigDocument&,
//  const KigWidget& ) const
//{
//  if ( sel.size() == 0 )
//    return i18n( "Select the object..." );
//  else
//    return i18n( "Select the point for the tangent to go through..." );
//}

/*
 * center of curvature of a curve
 */

CocConstructor::CocConstructor()
  : MergeObjectConstructor(
    I18N_NOOP( "Center Of Curvature" ),
    I18N_NOOP( "The center of the osculating circle to a curve" ),
    "centerofcurvature" )
{
  SimpleObjectTypeConstructor* conic =
    new SimpleObjectTypeConstructor(
      CocConicType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "cocconic" );

  SimpleObjectTypeConstructor* cubic =
    new SimpleObjectTypeConstructor(
      CocCubicType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "coccubic" );

  SimpleObjectTypeConstructor* curve =
    new SimpleObjectTypeConstructor(
      CocCurveType::instance(),
      "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
      "coccurve" );

  merge( conic );
  merge( cubic );
  merge( curve );
}

CocConstructor::~CocConstructor()
{
}

TQString CocConstructor::useText(
  const ObjectCalcer& o, const std::vector<ObjectCalcer*>&,
  const KigDocument&, const KigWidget& ) const
{
  if ( o.imp()->inherits( ConicImp::stype() ) )
    return i18n( "Center of Curvature of This Conic" );
  else if ( o.imp()->inherits( CubicImp::stype() ) )
    return i18n( "Center of Curvature of This Cubic Curve" );
  else if ( o.imp()->inherits( CurveImp::stype() ) )
    return i18n( "Center of Curvature of This Curve" );
  else if ( o.imp()->inherits( PointImp::stype() ) )
    return i18n( "Center of Curvature at This Point" );
  return TQString();
}

bool relativePrimes( int n, int p )
{
  if ( p > n ) return relativePrimes( p, n );
  assert ( p >= 0 );
  if ( p == 0 ) return false;
  if ( p == 1 ) return true;
  int d = int( n/p );
  return relativePrimes( p, n-d*p );
}

//TQString CocConstructor::selectStatement(
//  const std::vector<ObjectCalcer*>& sel, const KigDocument&,
//  const KigWidget& ) const
//{
//  if ( sel.size() == 0 )
//    return i18n( "Select the object..." );
//  else
//    return i18n( "Select the point where to compute the center of curvature..." );
//}
