// main2.cpp
// A demonstration of a database buffer
// made simpler by smart pointers.

#define DEBUG

#include "anyclass.hpp"

class BufferedDatabase
 {
  public:
    BufferedDatabase (void){};
    ~BufferedDatabase (void);

    void getRecPtr (
        int recNum,
        RefCntPtr(anyClass)& ptr);

  private:
    enum {bfrSize=3};

    RefCntPtr(anyClass) bfr[bfrSize];

    // these functions would actually
    // read from the disk DB.
    // for now, we'll just pretend
    void getRecWithNew (
        int recNum,
        RefCntPtr(anyClass)& ptr);

    void putRec (
        RefCntPtr(anyClass)& ptr);
 };


void BufferedDatabase::getRecPtr (
        int recNum,
        RefCntPtr(anyClass)& ptr)
  {
    // first, see if it is in the buffer

    for (int i = 0; i < bfrSize; ++i)
        if (  bfr[i]
           && (bfr[i]->intVal == recNum)
           )
          {
            ptr = bfr[i];
            return;
          };

    // if we made it to here, then it
    // is not in the buffer, so get it
    // from disk;

    RefCntPtr(anyClass) tmp;
    getRecWithNew (recNum, tmp);

    // return if not found
    if (0 == tmp)
      {
        ptr = 0;
        return;
      };

    // see if it can be inserted
    // into buffer
    for (i = 0; i < bfrSize; ++i)
        if (0 == bfr[i])
          {
            bfr[i] = tmp;
            ptr = tmp;
            return;
          };

    ptr = 0;

    // if it didn't return, then there
    // is no space - will have to find
    // some.
    for (i = 0; i < bfrSize; ++i)

      // reference count of 1 means the
      // only pointer to this object
      // is bfr[i].  Since nothing
      // outside this routine is pointing
      // to it, we can get rid of it.

        if (bfr[i]->refCnt() == 1)
          {
            putRec (bfr[i]);
            bfr[i] = tmp;
            ptr = tmp;
            return;
          };

    // if still didn't return,
    // out of space

    cout << "BUFFER FULL\n\n";
    // obviously, running out of space
    // is a bad thing - a real-world
    // scheme wouldn't use as array,
    // but some form of dynamic
    // allocation

  };


void BufferedDatabase::getRecWithNew (
        int recNum,
        RefCntPtr(anyClass)& ptr)
  {
    // would get record from database
    // again, we'll just fake it

    // pretend that records 1000
    // and above don't exist

    if (recNum >= 1000)
      {
        ptr = 0;
        return;
      };

    ptr = new anyClass;

    // pretend that it's
    // getting a record from disk
    ptr->intVal = recNum;
    ptr->string = "something";

    cout << "getting from disk - rec # "
         << ptr->intVal << "\n\n";
  };


void BufferedDatabase::putRec (
        RefCntPtr(anyClass)& ptr)
  {
    cout << "putting to disk - rec # "
         << ptr->intVal << "\n\n";
  };


BufferedDatabase::~BufferedDatabase(void)
  {
    for (int i = 0; i < bfrSize; ++i)
        if (bfr[i])
          {
            putRec (bfr[i]);
            bfr[i] = 0;
          };
  };


void main (void)
  {
   cout << "start of main\n\n";
   BufferedDatabase db;

   RefCntPtr(anyClass) ptrA, ptrB,
                       ptrC, ptrD;

   db.getRecPtr (1, ptrA);
   ptrA->show();

   db.getRecPtr (2, ptrA);
   db.getRecPtr (3, ptrA);
   db.getRecPtr (4, ptrA);

   db.getRecPtr (2, ptrA);
   db.getRecPtr (3, ptrB);
   db.getRecPtr (4, ptrC);
   db.getRecPtr (5, ptrD);
   if (!ptrD)
     cout << "getRecFailed\n\n";

   db.getRecPtr (1005, ptrD);
   ptrD->show();//an error

   cout << "end of main\n\n";
  };
