// ==========================================================================
// $Id: thumbnail.cpp,v 1.3 2017/11/25 02:25:52 jlang Exp $
// CSI2372 example Code for lecture 16 - C++11
// ==========================================================================
// (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: thumbnail.cpp,v $
// Revision 1.3  2017/11/25 02:25:52  jlang
// added rvalue reference functions
//
// Revision 1.2  2013/11/25 17:27:43  jlang
// Created a move example - rule of 5
//
// ==========================================================================
#include <vector>
#include <iostream>
#include <algorithm>

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

// This example uses a compile switch 
#define MOVE_DEF


// class that holds a dynamically sized pattern
class ThumbNail {
  unsigned int d_size;
  unsigned char* d_pattern;
public:	
  ThumbNail();
  ThumbNail( unsigned char* _in, unsigned int _size);
  ThumbNail(const ThumbNail& _otn);
  ~ThumbNail();
  ThumbNail& operator=(const ThumbNail& _otn);
#ifdef MOVE_DEF
  // Move cctor
  ThumbNail( ThumbNail&& _otn ) noexcept;
  // Move assignment
  ThumbNail& operator=(ThumbNail&& _otn) noexcept; 
#endif
  ThumbNail& scramble() &&;
  ThumbNail scramble() const &;
  void set(unsigned char* _in, unsigned int _size);
  friend ostream& operator<<(ostream& _os, const ThumbNail& _tn);
};

ThumbNail::ThumbNail(): d_size(0), d_pattern(0) 
{
}


ThumbNail::ThumbNail( unsigned char* _in, unsigned int _size) : d_size( _size ) {
  cerr << "ThumbNail(unsigned char* _in, unsigned int _size)" << endl;
  d_pattern = new unsigned char[d_size];
  for (int i=0;i<_size;++i){
    d_pattern[i]=_in[i];
  }
  return;
}

ThumbNail::ThumbNail(const ThumbNail& _otn): d_size(_otn.d_size), d_pattern(0)
{
  cerr << "ThumbNail(const ThumbNail& _otn)" << endl;
  if (d_size>0){
    d_pattern = new unsigned char[d_size];
    for (int i=0;i<d_size;++i){
      d_pattern[i]=_otn.d_pattern[i];
    }	
  }	
}

#ifdef MOVE_DEF
ThumbNail::ThumbNail( ThumbNail&& _otn ) noexcept {
  cerr << "ThumbNail(ThumbNail&& _otn)" << endl;
  // Stealing all resources from _otn
  d_size = _otn.d_size;
  d_pattern = _otn.d_pattern;
  // Leaving _otn in a defined state
   _otn.d_size = 0;
   _otn.d_pattern = nullptr;
}
#endif

ThumbNail::~ThumbNail() {
  cerr << "~ThumbNail" << endl;
  if (d_pattern) delete[] d_pattern;
}

ThumbNail& ThumbNail::operator=(const ThumbNail& _otn) {
  cerr << "ThumbNail& operator=(const ThumbNail& _otn)" << endl;
  if (this != &_otn ) {
    // save to delete
    if (d_pattern) delete[] d_pattern;
    d_pattern = new unsigned char[d_size];
    d_size = _otn.d_size;
    for (int i=0;i<_otn.d_size;++i){
      d_pattern[i]=_otn.d_pattern[i];
    }
  }
  return *this;
}

#ifdef MOVE_DEF
ThumbNail& ThumbNail::operator=(ThumbNail&& _otn) noexcept { 
  cerr << "ThumbNail& operator=(ThumbNail&& _otn)" << endl;
  if (this != &_otn ) {
    // save to delete
    if (d_pattern) delete[] d_pattern;
    d_pattern = _otn.d_pattern;
    d_size = _otn.d_size;
    _otn.d_size = 0;
    _otn.d_pattern = nullptr;
  }
  return *this;
}
#endif


void ThumbNail::set(unsigned char* _in, unsigned int _size) {
  if (d_pattern) delete[] d_pattern;
  d_size = _size;
  d_pattern = new unsigned char[d_size];
  for (int i=0;i<_size;++i){
    d_pattern[i]=_in[i];
  }
  return;
}

ThumbNail& ThumbNail::scramble() && {
  cout << "ThumbNail& ThumbNail::scramble() &&" << endl;
  if (d_pattern) {
    std::random_shuffle(d_pattern,d_pattern+d_size);
  }
  return *this;
}

ThumbNail ThumbNail::scramble() const & {
  cout << "ThumbNail ThumbNail::scramble() const &" << endl;
  ThumbNail res(*this);
  if (res.d_pattern) {
    std::random_shuffle(res.d_pattern,res.d_pattern+res.d_size);
  }
  return res;
}
          


ostream& operator<<(ostream& _os, const ThumbNail& _tn) {
  if ( _tn.d_size > 0 ) {
    for (int i=0;i<_tn.d_size; ++i) {
      _os << _tn.d_pattern[i];
    }
  } else {
    _os << "--empty--";
  }
  return _os;
}

ThumbNail foo() {
  unsigned char pattern[] = "xxyy";
  ThumbNail res(pattern,4);
  return res;
}


int main() {
  std::vector<ThumbNail> vec;
  unsigned char res[] = "x1y2z1a3";

  cout << "===================" << endl;
  for ( int i=0; i<4; ++i ) {
    vec.push_back( ThumbNail(res,i*2+2));
  }
  cout << "===================" << endl;
  for ( auto& tn : vec ) {
    cout << tn << endl;
  }
  cout << "===================" << endl;
  ThumbNail tn = *vec.rbegin();
  vec.clear();
  cout << tn << endl;
  cout << "===================" << endl;
  // Use of assignment
  ThumbNail tn2; // default ctor
  cout << tn2 << endl;
  tn2 = tn; // regular assignment
  cout << tn2 << endl;
  // tn2 reset
  tn2.set( nullptr, 0 );
  cout << tn2 << endl;
  tn2 = std::move(tn);
  cout << tn2 << endl;
  cout << "===================" << endl;
  tn = tn2.scramble();
  cout << tn2 << endl;
  cout << tn << endl;
  cout << "===================" << endl;
  tn = foo().scramble();
  cout << tn << endl;  
  return 0;
}

    
