// Purpose.  Flyweight                  #include <iostream.h>
//
// Discussion.  Trying to use objects   const int X = 6;
// at very low levels of granularity    const int Y = 10;
// is nice, but the overhead may be
// prohibitive.  Flyweight suggests     class Gazillion {
// removing the non-shareable state     public:
// from the class, and having the cli-     Gazillion( int in ) {
// ent supply it when methods are             val1_ = in;
// called.  This places more respon-          cout << "ctor: "<< val1_<<endl; }
// sibility on the client, but, con-       ~Gazillion() {
// siderably fewer instances of the           cout << val1_ << ' '; }
// Flyweight class are now created.        void report( int in ) {
// Sharing of these instances is fa-          cout << val1_ << in << ' '; }
// cilitated by introducing a Factory   private:
// class that maintains a "cache" of       int  val1_;
// existing Flyweights.                 };
//
// In this example, the "X" state is    class Factory {
// considered shareable (within each    public:
// row anyways), and the "Y" state has     static Gazillion* getFly(int in) {
// been externalized (it is supplied          if ( ! pool_[in])
// by the client when report() is                pool_[in] =
// called).                                              new Gazillion( in );
                                              return pool_[in];
#include <iostream.h>                      }
                                           static void cleanUp() {
const int X = 6;                              cout << "dtors: ";
const int Y = 10;                             for (int i=0; i < X; i++)
                                                 if (pool_[i])
class Gazillion {                                   delete pool_[i];
public:                                       cout << endl;
   Gazillion() {                           }
      val1_ = num_ / Y;                 private:
      val2_ = num_ % Y;                    static Gazillion*  pool_[X];
      num_++;                           };
   }
   void report() {                      Gazillion*  Factory::pool_[]  = {
      cout << val1_ << val2_ << ' ';                             0,0,0,0,0,0 };
   }
private:                                void main( void )
   int    val1_;                        {
   int    val2_;                           for (int i=0; i < X; i++)
   static int num_;                        {
};                                            for (int j=0; j < Y; j++)
                                                 Factory::getFly(i)->report(j);
int Gazillion::num_ = 0;                      cout << endl;
                                           }
void main( void )                          Factory::cleanUp();
{                                       }
   Gazillion  matrix[X][Y];
   for (int i=0; i < X; i++)            // ctor: 0
   {                                    // 00 01 02 03 04 05 06 07 08 09
      for (int j=0; j < Y; j++)         // ctor: 1
         matrix[i][j].report();         // 10 11 12 13 14 15 16 17 18 19
      cout << endl;                     // ctor: 2
   }                                    // 20 21 22 23 24 25 26 27 28 29
}                                       // ctor: 3
                                        // 30 31 32 33 34 35 36 37 38 39
// 00 01 02 03 04 05 06 07 08 09        // ctor: 4
// 10 11 12 13 14 15 16 17 18 19        // 40 41 42 43 44 45 46 47 48 49
// 20 21 22 23 24 25 26 27 28 29        // ctor: 5
// 30 31 32 33 34 35 36 37 38 39        // 50 51 52 53 54 55 56 57 58 59
// 40 41 42 43 44 45 46 47 48 49        // dtors: 0 1 2 3 4 5
// 50 51 52 53 54 55 56 57 58 59



// Purpose.  Flyweight design pattern demo.
// 
// Discussion.  Flyweight describes how to share objects, so that their
// use at fine granularities is not cost prohibitive.  A key concept is
// the distinction between "intrinsic" and "extrinsic" state.  Intrinsic
// state consists of information that is independent of the flyweight's
// context - information that is sharable (i.e. each Icon's name, width,
// and height).  It is stored in the flyweight (i.e. the Icon class).
// Extrinsic state cannot be shared, it depends on and varies with the
// flyweight's context (i.e. the x,y position that each Icon instance's
// upper left corner will be drawn at).  Extrinsic state is stored or
// computed by the client and is passed to the flyweight when an operation
// is invoked.  Clients should not instantiate Flyweights directly, they
// should obtain them exclusively from a FlyweightFactory object to ensure
// they are shared properly.

#include <iostream.h>
#include <string.h>

class Icon {
public:
	Icon( char* fileName ) {
		strcpy( _name, fileName );
		if ( ! strcmp(fileName, "go"))     { _width = 20;  _height = 20; }
		if ( ! strcmp(fileName, "stop"))   { _width = 40;  _height = 40; }
		if ( ! strcmp(fileName, "select")) { _width = 60;  _height = 60; }
		if ( ! strcmp(fileName, "undo"))   { _width = 30;  _height = 30; } }
	const char* getName() { return _name; }
	draw( int x, int y ) {
		cout << "   drawing " << _name << ": upper left (" << x << "," << y
		<< ") - lower right (" << x + _width << "," << y + _height << ")"
		<< endl; }
private:
	char  _name[20];
	int   _width;
	int   _height;
};


class FlyweightFactory {
public:
	static Icon* getIcon( char* name ) {
		for (int i=0; i < _numIcons; i++)
			if ( ! strcmp( name, _icons[i]->getName() ))
				return _icons[i];
		_icons[_numIcons] = new Icon( name );
		return _icons[_numIcons++]; }
	static void reportTheIcons() {
		cout << "Active Flyweights: ";
		for (int i=0; i < _numIcons; i++)
			cout << _icons[i]->getName() << " ";
		cout << endl; }
private:
	enum { MAX_ICONS = 5 };
	static int    _numIcons;
	static Icon*  _icons[MAX_ICONS];
};

int   FlyweightFactory::_numIcons = 0;
Icon* FlyweightFactory::_icons[];


class DialogBox {
public:
	DialogBox( int x, int y, int incr ) : _iconsOriginX(x), _iconsOriginY(y),
		_iconsXIncrement(incr) { }
	virtual void draw() = 0;
protected:
	Icon* _icons[3];
	int   _iconsOriginX;
	int   _iconsOriginY;
	int   _iconsXIncrement;
};

class FileSelection : public DialogBox {
public:
	FileSelection( Icon* first, Icon* second, Icon* third ) :
		DialogBox(100,100,100) {
		_icons[0] = first;
		_icons[1] = second;
		_icons[2] = third; }
	void draw() {
		cout << "drawing FileSelection:" << endl;
		for (int i=0; i < 3; i++)
			_icons[i]->draw( _iconsOriginX + (i * _iconsXIncrement),
				_iconsOriginY ); }
};

class CommitTransaction : public DialogBox {
public:
	CommitTransaction( Icon* first, Icon* second, Icon* third ) :
		DialogBox(150,150,150) {
		_icons[0] = first;
		_icons[1] = second;
		_icons[2] = third; }
	void draw() {
		cout << "drawing CommitTransaction:" << endl;
		for (int i=0; i < 3; i++)
			_icons[i]->draw( _iconsOriginX + (i * _iconsXIncrement),
				_iconsOriginY ); }
};


void main() {
	DialogBox* dialogs[2];
	dialogs[0] = new FileSelection(
		FlyweightFactory::getIcon("go"),
		FlyweightFactory::getIcon("stop"),
		FlyweightFactory::getIcon("select") );
	dialogs[1] = new CommitTransaction(
		FlyweightFactory::getIcon("select"),
		FlyweightFactory::getIcon("stop"),
		FlyweightFactory::getIcon("undo") );

	for (int i=0; i < 2; i++)
		dialogs[i]->draw();

	FlyweightFactory::reportTheIcons();
}

// drawing FileSelection:
//    drawing go: upper left (100,100) - lower right (120,120)
//    drawing stop: upper left (200,100) - lower right (240,140)
//    drawing select: upper left (300,100) - lower right (360,160)
// drawing CommitTransaction:
//    drawing select: upper left (150,150) - lower right (210,210)
//    drawing stop: upper left (300,150) - lower right (340,190)
//    drawing undo: upper left (450,150) - lower right (480,180)
// Active Flyweights: go stop select undo 
