// ==========================================================================
// $Id: function_ptr.cpp,v 1.2 2017/11/15 17:53:51 jlang Exp $
// CSI2372 example Code for lecture 12
// ==========================================================================
// (C)opyright:
//
//   Jochen Lang
//   SITE, University of Ottawa
//   800 King Edward Ave.
//   Ottawa, On., K1N 6N5
//   Canada. 
//   http://www.site.uottawa.ca
// 
// Creator: jlang (Jochen Lang)
// Email:   jlang@site.uottawa.ca
// ==========================================================================
// $Log: function_ptr.cpp,v $
// Revision 1.2  2017/11/15 17:53:51  jlang
// Update for 2017.
//
// Revision 1.1  2010/11/22 16:40:53  jlang
// Added function ptr example without templates
//
// Revision 1.1  2006/11/08 01:41:35  jlang
//
// ==========================================================================
#include <iostream>

using std::cout;
using std::endl;
using std::ostream;

class Point2D;

bool lessThan(const Point2D&, const Point2D& );
bool equals(const Point2D&, const Point2D& );
ostream& operator<<( ostream&, const Point2D& );


class Point2D {
  int d_x;
  int d_y;
public:
  Point2D( int _x, int _y );
  void print() const;

  friend ostream& operator<<( ostream&, const Point2D& );
  friend bool lessThan(const Point2D&, const Point2D& );
  friend bool equals(const Point2D&, const Point2D& );
};

Point2D::Point2D( int _x, int _y ) : d_x(_x), d_y(_y)
{}


ostream& operator<<( ostream& os, const Point2D& pt ) {
  os << "( ";
  os << pt.d_x << ", " << pt.d_y;
  os << " )" << endl;
}

bool lessThan(const Point2D& ptA, const Point2D& ptB ) {
  return ptA.d_y < ptB.d_y;
}

bool equals(const Point2D& ptA, const Point2D& ptB ) {
  return ptA.d_x == ptB.d_x && ptA.d_y == ptB.d_y;
}

// Function taking a function ptr as an argument
const Point2D& compareA( const Point2D& ptA, const Point2D& ptB,
			 bool cAB(const Point2D&, const Point2D&) ) {
  if ( cAB( ptA, ptB )) return ptA;
  return ptB;
}

const Point2D& compareLess( const Point2D& ptA, const Point2D& ptB ) {
    if ( lessThan( ptA, ptB )) return ptA;
  return ptB;
}

// Old style typedef or C++11 using syntax
// typedef bool (*pt_compare)(const Point2D&, const Point2D&);
using pt_compare=bool (*)(const Point2D&, const Point2D&);


// Function taking a function ptr as an argument with typedef
const Point2D& compareB( const Point2D& ptA, const Point2D& ptB,
			 pt_compare cAB ) {
  if ( cAB( ptA, ptB )) return ptA;
  return ptB;
}


int main() {
  Point2D ptA(1,0), ptB(0,1);
  // not using typedef
  if ( lessThan( ptA, ptB )) {
    cout << "ptA is less than ptB -- direct" << endl; 
  }
  bool (*ptr) ( const Point2D&, const Point2D& ) = &lessThan;
  if ( (*ptr)(ptA,ptB) ) {
    cout << "ptA is less than ptB -- explicit *" << endl; 
  }
  if ( ptr(ptA,ptB)) {
    cout << "ptA is less than ptB -- implicit *" << endl; 
  }
  // passing function ptr argument
  
  Point2D ptC = compareA( ptA, ptB, ptr );
  cout << "Result compareA (lessThan): " << ptC << endl; 
  // passing a different ptr
  Point2D ptD = compareA( ptA, ptB, equals );
  cout << "Result compareA (equals): " << ptD << endl; 
  
  // using a typedef
  pt_compare c = &lessThan;
  if ( (*c)(ptA,ptB)) {
    cout << "ptA is less than ptB -- typedef, explicit *" << endl; 
  }
  if ( c(ptA,ptB)) {
    cout << "ptA is less than ptB -- typedef, implicit *" << endl; 
  }
  // passing function ptr argument -- no differnce here
  Point2D ptE = compareB( ptA, ptB, ptr );
  cout << "Result compareB (lessThan): " << ptE << endl; 

  Point2D ptF = compareB( ptA, ptB, equals );
  cout << "Result compareB (equals): " << ptF << endl; 
  return 0;
}
