/////////////////////////////////////////////////////////////////////
// Loader.cpp - Demonstrate loading "Reflection" Library           //
//                                                                 //
// Language:    Visual Studio 2005 C++                             //
// Platform:    Dell Dimension 9150, Windows XP Pro, SP 2          //
// Application: Prototype for CSE687 Project #4b - Reflection      //
// Author:      Jim Fawcett, CST 2-187, Syracuse University        //
//              (315) 443-3948, jfawcett@twcny.rr.com              //
/////////////////////////////////////////////////////////////////////

#include <windows.h>  // note: no reflection.h included here
#include <iostream>
#include <string>

void main()
{
  std::cout << "\n  Test Reflector Loader";
  std::cout << "\n =======================\n";

  /////////////////////////////////////////////////////////
  // load ReflectionProto.dll library

  HMODULE hmod = LoadLibrary(L"ReflectionProto");
  if(!hmod)
  {
    std::cout << "\n  library load failed";
    return;
  }

  /////////////////////////////////////////////////////////
  // find global function const char* GetTypes()

  typedef const char* (*GT)();
  GT GetTypes = (GT)GetProcAddress(hmod,"GetTypes");
  if(!GetTypes)
  {
    std::cout << "\n  GetProcAddress failed for GetTypes";
    return;
  }
  std::cout << GetTypes();

  /////////////////////////////////////////////////////////
  // find const char* GetMethods(const char* type)

  typedef const char* (*GM)(const char*);
  GM GetMethods = (GM)GetProcAddress(hmod,"GetMethods");
  if(!GetMethods)
  {
    std::cout << "\n  GetProcAddress failed";
    return;
  }
  std::cout << GetMethods("reflector");

  /////////////////////////////////////////////////////////
  // find const char* GetParams(const char* type, const char* method)

  typedef const char* (*GP)(const char*, const char*);
  GP GetParams = (GP)GetProcAddress(hmod,"GetParams");
  if(!GetParams)
  {
    std::cout << "\n  GetProcAddress failed";
    return;
  }
  std::cout << GetParams("reflector","show");

  FreeLibrary(hmod);
  std::cout << std::endl;
}
#ifndef REFLECTION_H
#define REFLECTION_H
/////////////////////////////////////////////////////////////////////
// reflection.h - Getting started with reflection mechanism        //
//                                                                 //
// Language:    Visual Studio 2005 C++                             //
// Platform:    Dell Dimension 9150, Windows XP Pro, SP 2          //
// Application: Prototype for CSE687 Project #4b - Reflection      //
// Author:      Jim Fawcett, CST 2-187, Syracuse University        //
//              (315) 443-3948, jfawcett@twcny.rr.com              //
/////////////////////////////////////////////////////////////////////

#include <set>
#include <map>
#include <list>
#include <string>

///////////////////////////////////////////////////////////
// Telling Windows to export definitions from this module
//
// Note: the library project defines DLL_LIB, users don't

#ifdef DLL_LIB
#define REFLAPI _declspec(dllexport)
#else
#define REFLAPI _declspec(dllimport)
#endif

///////////////////////////////////////////////////////////
// Defining a data structure to hold reflection information

typedef std::set<std::string> sets;
typedef std::multiset<std::string> msets;
typedef std::list<std::string> Params;              // order is important   
typedef std::multimap<std::string,Params> Methods;  // overloads
typedef std::map<std::string,Methods> Types;        // types are unique

///////////////////////////////////////////////////////////
// The reflector class does all the work

class REFLAPI reflector
{
public:
  reflector();
  virtual ~reflector() {}
  sets getTypes();
  msets getMethods(const std::string& type);
  Params getParams(const std::string& type, const std::string& method);
  template <typename T>
  std::string show(const std::string& msg, const T& stuff);
protected:
  Types types;
};

///////////////////////////////////////////////////////////
// Define global C functions to get unmangled names

extern "C" { REFLAPI const char* GetTypes(); }
extern "C" { REFLAPI const char* GetMethods(const char* type); }
extern "C" { REFLAPI const char* GetParams(const char* type, const char* method); }

#endif
/////////////////////////////////////////////////////////////////////
// reflection.cpp - Getting started with reflection mechanism      //
//                                                                 //
// Language:    Visual Studio 2005 C++                             //
// Platform:    Dell Dimension 9150, Windows XP Pro, SP 2          //
// Application: Prototype for CSE687 Project #4b - Reflection      //
// Author:      Jim Fawcett, CST 2-187, Syracuse University        //
//              (315) 443-3948, jfawcett@twcny.rr.com              //
/////////////////////////////////////////////////////////////////////

#include <iostream>
#include <sstream>
#include "reflection.h"

///////////////////////////////////////////////////////////
// Global functions with C types to make dynamic loading 
// simple
//
//----< return string with all types seperated by spaces >-----------

const char* GetTypes()
{
  reflector ref;
  static std::string temp = ref.show("Types",ref.getTypes());
  return temp.c_str();
}
//----< return string with all methods of given type >---------------

const char* GetMethods(const char* type)
{
  std::string msg = "Methods of ";
  msg += type;
  reflector ref;
  static std::string temp = ref.show(msg,ref.getMethods(type));
  return temp.c_str(); //reflector().getTypes();
}
//----< return string with all parameters of given type & method >---

const char* GetParams(const char* type, const char* method)
{
  std::string msg = "Parameters of ";
  msg += type;
  msg += "::";
  msg += method;
  reflector ref;
  static std::string temp = ref.show(msg,ref.getParams(type,method));
  return temp.c_str();
}
//
//----< here is where the type information is stored >---------------
//
// Analyzers will have to collect type information and add to this
// store.  Perhaps you can think of a better way.

reflector::reflector()
{
  Params ptemp1;
  ptemp1.push_back("void");
  
  Params ptemp2;
  ptemp2.push_back("const std::string&");
  
  Params ptemp3;
  ptemp3.push_back("const std::string&");
  ptemp3.push_back("const std::string&");
  
  Params ptemp4;
  ptemp4.push_back("const std::string&");
  ptemp4.push_back("const T&");
  
  Methods mtemp1;  
  mtemp1.insert(std::pair<std::string,Params>("reflector",ptemp1));
  mtemp1.insert(std::pair<std::string,Params>("~reflector",ptemp1));
  mtemp1.insert(std::pair<std::string,Params>("getTypes",ptemp1));
  mtemp1.insert(std::pair<std::string,Params>("getMethods",ptemp2));
  mtemp1.insert(std::pair<std::string,Params>("getParams",ptemp3));
  mtemp1.insert(std::pair<std::string,Params>("show",ptemp4));

  types["reflector"] = mtemp1;
}
//
//----< return a set of types >--------------------------------------

sets reflector::getTypes()
{
  sets temp;
  Types::iterator iter;
  for(iter=types.begin(); iter!=types.end(); ++iter)
    temp.insert(iter->first);
  return temp;
}
//----< return a multiset of methods - remember overloading >--------

msets reflector::getMethods(const std::string& type)
{
  msets temp;
  Methods::iterator iter;
  Methods& methods = types[type];
  for(iter=methods.begin(); iter!=methods.end(); ++iter)
    temp.insert(iter->first);
  return temp;
}
//----< return a list of parameters - remember order is important >--

Params reflector::getParams(
  const std::string& type, const std::string& method
)
{
  Params temp;
  Params::iterator iter;
  Methods methods = types[type];
  Methods::iterator miter = methods.find(method);
  Params& params = (*miter).second;
  for(iter=params.begin(); iter!=params.end(); ++iter)
    temp.push_back(*iter);
  return temp;
}
//----< generate a string encoded with type information >------------

template <typename T>
std::string reflector::show(const std::string& msg, const T& stuff)
{
  T::const_iterator iter;
  std::ostringstream out;
  out << "\n  " << msg;
  for(iter=stuff.begin(); iter!=stuff.end(); ++iter)
    out << "\n    " << (*iter).c_str();
  out << "\n";
  return out.str();
}
//
#ifdef TEST_REFLECTIONPROTO

//----< test as a console application, then turn into a library >----

void main()
{
  std::cout << "\n  Test Reflector";
  std::cout << "\n ================\n";

  reflector* pRef = new reflector;
  std::cout << pRef->show("types:",pRef->getTypes());
  std::cout << pRef->show("methods of reflector:",pRef->getMethods("reflector"));
  std::cout << pRef->show("parameters of show:", pRef->getParams("reflector","show"));

  delete pRef;
  std::cout << std::endl;
};

#endif
