/*
  CoreLinux++ 
  Copyright (C) 1999 CoreLinux Consortium
  
   The CoreLinux++ Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The CoreLinux++ Library Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  
*/   

/** \example examp2.cpp
   This example is to show use of the STL that CoreLinux wraps in macros.
   There was no purpose to wrapping them except to simplify the type
   defines to be consistent with CoreLinux++ C++ Standards naming
   conventions.It is assumed that the reader has some knowledge of
   the STL.
*/                   


#include <Common.hpp>
#include <Map.hpp>      // Defines map and multi-map


using namespace corelinux;
using namespace std;

#include <iostream>
#include <exception>

//
// A map is like a set in regards to the keys, it
// does not allow multiple key values. For our example
// a dictionary does allow multiple keys so we use
// a multi-map
//

CORELINUX_MULTIMAP(
		   AbstractStringPtr, 
		   AbstractStringPtr, 
		   less<AbstractStringPtr>,
		   Dictionary );

enum  DictionaryType
{
   MAIN,
   USER
};

//
// Static entries to feed the dictionaries
//

struct   _Entry
{
   CharCptr theKey;
   CharCptr theDefinition;
};

DECLARE_TYPE( struct _Entry, Entry );

//
// In module function prototypes
//

int   main( void );                       

//
// Allow user to feed the dictionaries
//

void  initializeDictionary( DictionaryRef, DictionaryType );

//
// Generic print facility
//

void  dumpDictionary( DictionaryRef, AbstractStringCref );

//
// Allow merge into main dictionary
//

//
// Assertion and Exception handlers
//

void  handleAssertion( AssertionCref aAssert );
void  handleException( ExceptionCref );   


int main( void )
{
   //
   // Practice gracefull exception management
   //

   try
   {
      // Initialize the starter

      Dictionary  mainBook;
      initializeDictionary( mainBook, MAIN );
      dumpDictionary(mainBook,StringUtf8("Members of Main Dictionary"));

      // Emulate new entries added

      Dictionary  userBook;
      initializeDictionary( userBook, USER );
      dumpDictionary(userBook,StringUtf8("Members of User Dictionary"));

      //
      // Create a union of entries. We use a simple method
      // (and probable not as efficient) as the routines
      // available in stl_algo.h.
      //
      // You can consider which you would use based on
      // what behavior you want a merge to have. 
      //

      Dictionary  updatedBook(mainBook);
      updatedBook.insert(userBook.begin(),userBook.end());
      dumpDictionary(updatedBook,StringUtf8("Members of NEW Main Dictionary"));

      //
      // Now we do the cleanup as I don't think it will
      // happen on it's own
      //

      DictionaryIterator   begin( updatedBook.begin() );
      DictionaryIterator   end( updatedBook.begin() );

      for( ; begin != end; ++begin )
      {
         delete (*begin).first;
         delete (*begin).second;
      }

      updatedBook.clear();
      mainBook.clear();
      userBook.clear();

   }
   catch( AssertionRef aAssert )
   {
      handleAssertion(aAssert);
   }
   catch( ExceptionRef aException )
   {
      handleException(aException);
   }
   catch( std::exception & e )
   {
      cerr  << e.what() << endl;
   }
   catch( ... )
   {
      cerr  << "Unknown exception." << endl;
   }

   return 0;               
}

//
// Initialize dictionary feeds the provided collection
// with dictionary entries based on the enumerator.
// Various assertions are provided.

Entry mainEntries[] =
{
   {"abstract class",
      "A class whose primary purpose is to define an interface"},
   {"class",
      "A class defines an object's interface and implementation"},
   {"concrete class",
      "A class having no abstract operations"}
};

Entry userEntries[] =
{
   {"monkey",
      "A person who stays up late creating code examples"},
   {"dog house",
      "Where the monkey has to sleep"},
   {"concrete class",
      "The class of material that a monkey gets hit with in the dog house"}
};

void  initializeDictionary
   ( 
      DictionaryRef  aDictionary,
      DictionaryType aTypeDictionary
   )
{
   Count       aCount(0);        // Generic entry counter
   EntryCptr   aHead(NULLPTR);   // Generic entry pointer

   //
   // Determine which dictionary to feed.
   // Extension suggestion: Have input come from
   // external source (keyboard, stream, etc)
   //

   if( aTypeDictionary == MAIN )
   {
      aCount = sizeof( mainEntries );
      aHead = mainEntries;
   }
   else if( aTypeDictionary == USER )
   {
      aCount = sizeof( userEntries );
      aHead = userEntries;
   }
   else
   {
      // Where missing something!

      NEVER_GET_HERE;
   }

   aCount /= sizeof( Entry );

   //
   // Logic assertions
   //

   CHECK( aCount > 1 );
   CHECK( aHead != NULLPTR );

   // Feed the dictionary

   for( Count x = 0; x < aCount; ++x )
   {
      aDictionary.insert
         ( 
            Dictionary::value_type
            (
               new StringUtf8(aHead[x].theKey),
               new StringUtf8(aHead[x].theDefinition)
            )
         );
   }

}

//
// General routine for dumping a dictionary to cout
//

void  dumpDictionary( DictionaryRef aRef , AbstractStringCref aHeader )
{
   REQUIRE( aHeader.supportsStandardInterface() == true );
   REQUIRE( aHeader.getElementByteCount() == sizeof(Char) );

   const string & aStdImpl = dynamic_cast<const std::string &>(aHeader);

   cout  << endl << aStdImpl << endl << endl;

   DictionaryIterator   begin = aRef.begin();

   //
   // First we test to insure that we can handle the string
   // implementation using StringUtf8
   //

   if( (*begin).first->supportsStandardInterface() == true &&
       (*begin).first->isUtf8() == true )
   {
      DictionaryIterator   end = aRef.end();
      StringUtf8Ptr        aKey(NULLPTR);
      StringUtf8Ptr        aValue(NULLPTR);
      for( ; begin != end; ++begin )
      {
         aKey = dynamic_cast<StringUtf8Ptr>((*begin).first);
         aValue = dynamic_cast<StringUtf8Ptr>((*begin).second);
         cout  << *aKey << " = " << *aValue << endl;
      }

   }
   else
   {
      throw Exception("Unable to support string type",LOCATION);
   }

}

//
// Peform default (just show it)
//

void  handleAssertion( AssertionCref aAssert )
{
   cerr << aAssert.getFile() << ":" << aAssert.getLine() << ":" << 
      "Assertion: ";

   if( aAssert.getType() == Assertion::NEVERGETHERE )
   {
      cerr << "NEVER_GET_HERE";
   }
   else
   {
      if( aAssert.getType() == Assertion::REQUIRE )
      {
         cerr  << "REQUIRE";
      }
      else if( aAssert.getType() == Assertion::ENSURE )
      {
         cerr  << "ENSURE";
      }
      else if( aAssert.getType() == Assertion::CHECK )
      {
         cerr  << "CHECK";
      }
      else 
      {
         cerr  << "ASSERT";
      }
      cerr << "( " << aAssert.getWhy() << " )";
   }

   cerr << endl;
}

void  handleException( ExceptionCref aExcp )
{
   cerr << aExcp.getFile() << ":" << aExcp.getLine() << ":" <<
      "Exception: " << aExcp.getWhy() << endl;
}

/*
   Common rcs information do not modify
   $Author: prudhomm $
   $Revision: 1.5 $
   $Date: 2000/08/31 22:49:01 $
   $Locker:  $
*/


