/**
 *  Design Scripting Library
 *  Copyright (C) 2004 Stylianos Dritsas
 *
 *  This software is provided 'as-is', without any expressed or implied warranty.  
 *  In no event will the author be held liable for any damages arising from the 
 *  use of this software. 
 *
 *  Permission is granted to anyone to use this software for any purpose, 
 *  including commercial applications, and to alter it and redistribute it 
 *  freely, subject to the following restrictions:
 *
 *  1. The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *  2. Altered source versions must be plainly marked as such, and must not be
 *     misrepresented as being the original software.
 *  3. This notice may not be removed or altered from any source distribution.
 *
 *  Stylianos Dritsas dritsas@alum.mit.edu
 *
 */

/**
 *  BUILD INSTRUCTIONS:
 *  compile with gcc / mingw
 *  compile with: --add-stdcall-alias
 *  linker  with: libuuid.a, liboleaut32.a
 *
 *  NOTICE: 
 *  Very few error checking, use with care :)
 *  The DllUnregisterServer is totally unimplemented
 *  The dll main is all over, as well as the lock server
 *  and other boring to do methods
 *
 */
            
#include <windows.h>
#include <initguid.h>
#include <winreg.h>

#ifndef INITGUID
#define INITGUID
#endif

/**
 * Server GUID and hard coded dispatch interface info
 * to add methods:
 * 1. figure out the parameters and make another PARAMDATA entry 
 * 2. put an entry in the interface table and !double check! the 
 *    disptch id and vtable index
 * 3. increment the interface table entries
 * 4. put the methods in the the binary file class in correct order
 * 5. implement the method!
 *
 */
 
/**
 *  CLSID:32F5D805-FC94-4dde-8F25-960710BD6263
 */
DEFINE_GUID( 
  IID_DesignScripting, 
  0x32f5d805, 0xfc94, 0x4dde, 0x8f, 0x25, 0x96, 0x7, 0x10, 0xbd, 0x62, 0x63);

#define DLLEXPORT __declspec( dllexport )

/**
 *  Globals
 */
unsigned long global_references;
bool          global_lock; 
HINSTANCE     global_instance;

/** 
 *                                      Stupid param name    Param type
 */
static PARAMDATA param_array      =   { OLESTR( "ARRAY"   ), VT_BYREF | VT_VARIANT };
static PARAMDATA param_integer    =   { OLESTR( "INTEGER" ), VT_I4 };
static PARAMDATA param_real       =   { OLESTR( "REAL"    ), VT_R8 };
static PARAMDATA param_file   [2] = { { OLESTR( "STRING"  ), VT_BSTR }, 
                                      { OLESTR( "BOOLEAN" ), VT_BOOL } };
static PARAMDATA param_port   [7] = { { OLESTR( "INTEGER" ), VT_I4 }, 
                                      { OLESTR( "INTEGER" ), VT_I4 },
                                      { OLESTR( "INTEGER" ), VT_I4 },
                                      { OLESTR( "INTEGER" ), VT_I4 },
                                      { OLESTR( "INTEGER" ), VT_I4 },
                                      { OLESTR( "INTEGER" ), VT_I4 },
                                      { OLESTR( "INTEGER" ), VT_I4 } };
/**                                      
 *  Name of the Method       ParamList    DispId #VTBL CallStyle #Params  MethodCall ReturnType 
 */
static METHODDATA method_table[] = {
 { OLESTR( "ABOUT"       ), NULL,            100,  0, CC_CDECL, 0, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "OPENFILE"    ), param_file,     1001,  1, CC_CDECL, 2, DISPATCH_METHOD, VT_BOOL  },
 { OLESTR( "OPENSERIAL"  ), param_port,     1002,  2, CC_CDECL, 7, DISPATCH_METHOD, VT_BOOL  },
 { OLESTR( "CLOSE"       ), NULL,           1003,  3, CC_CDECL, 0, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "READBYTE"    ), NULL,           1004,  4, CC_CDECL, 0, DISPATCH_METHOD, VT_I4    },
 { OLESTR( "READWORD"    ), NULL,           1005,  5, CC_CDECL, 0, DISPATCH_METHOD, VT_I4    },
 { OLESTR( "READDWORD"   ), NULL,           1006,  6, CC_CDECL, 0, DISPATCH_METHOD, VT_I4    },
 { OLESTR( "READSHORT"   ), NULL,           1007,  7, CC_CDECL, 0, DISPATCH_METHOD, VT_I4    },
 { OLESTR( "READLONG"    ), NULL,           1008,  8, CC_CDECL, 0, DISPATCH_METHOD, VT_I4    },
 { OLESTR( "READFLOAT"   ), NULL,           1009,  9, CC_CDECL, 0, DISPATCH_METHOD, VT_R4    },
 { OLESTR( "READDOUBLE"  ), NULL,           1010, 10, CC_CDECL, 0, DISPATCH_METHOD, VT_R8    }, 
 { OLESTR( "READBYTES"   ), &param_array,   1011, 11, CC_CDECL, 1, DISPATCH_METHOD, VT_BOOL  },
 { OLESTR( "WRITEBYTE"   ), &param_integer, 1012, 12, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "WRITEWORD"   ), &param_integer, 1013, 13, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "WRITEDWORD"  ), &param_integer, 1014, 14, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "WRITESHORT"  ), &param_integer, 1015, 15, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "WRITELONG"   ), &param_integer, 1016, 16, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "WRITEFLOAT"  ), &param_real,    1017, 17, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "WRITEDOUBLE" ), &param_real,    1018, 18, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "WRITEBYTES"  ), &param_array,   1019, 19, CC_CDECL, 1, DISPATCH_METHOD, VT_BOOL  },
 { OLESTR( "LOCATION"    ), NULL,           1020, 20, CC_CDECL, 0, DISPATCH_METHOD, VT_I4    },
 { OLESTR( "MOVETO"      ), &param_integer, 1021, 21, CC_CDECL, 1, DISPATCH_METHOD, VT_EMPTY },
 { OLESTR( "LENGTH"      ), NULL,           1022, 22, CC_CDECL, 0, DISPATCH_METHOD, VT_I4    }
};

/**
 *  Interface information 
 */
INTERFACEDATA interface_table = {
  method_table,  // methods info
  23             // #methods
};

/**
 *  About message 
 */
#define ABOUT_MESSAGE "DesignScripting Library version 1.0\r\n\r\nCopyright (c) 2004 Stylianos Dritsas\r\nAll rights reserved."

class DesignScripting;

/**
 *  The method holder
 */
class BinaryFile {
  public:
    STDMETHOD_( void,   About       )( void          );
    STDMETHOD_( bool,   OpenFile    )( BSTR filename, 
                                       BOOL readonly );
    STDMETHOD_( bool,   OpenSerial  )( long portnumber,
                                       long buffersize,
                                       long baudrate,
                                       long parity,
                                       long databits,
                                       long stopbits,
                                       long timeout  );
    STDMETHOD_( void,   Close       )( void          );

    STDMETHOD_( long,   ReadByte    )( void          );
    STDMETHOD_( long,   ReadWord    )( void          );
    STDMETHOD_( long,   ReadDWord   )( void          );
    STDMETHOD_( long,   ReadShort   )( void          );
    STDMETHOD_( long,   ReadLong    )( void          );
    STDMETHOD_( float,  ReadFloat   )( void          );
    STDMETHOD_( double, ReadDouble  )( void          );
    STDMETHOD_( bool,   ReadBytes   )( LPVARIANT     );

    STDMETHOD_( void,   WriteByte   )( long integer  );
    STDMETHOD_( void,   WriteWord   )( long integer  );
    STDMETHOD_( void,   WriteDWord  )( long integer  );
    STDMETHOD_( void,   WriteShort  )( long integer  );
    STDMETHOD_( void,   WriteLong   )( long integer  );
    STDMETHOD_( void,   WriteFloat  )( double real   );
    STDMETHOD_( void,   WriteDouble )( double real   );
    STDMETHOD_( bool,   WriteBytes  )( LPVARIANT     );

    STDMETHOD_( long,   Location    )( void          );
    STDMETHOD_( void,   MoveTo      )( long integer  );
    STDMETHOD_( long,   Length      )( void          );

    BinaryFile( ) {
      this->designscripting = NULL;
      this->filehandle = INVALID_HANDLE_VALUE;
    }
    ~BinaryFile( );
  public:
    DesignScripting* designscripting;
    HANDLE           filehandle;
};

/**
 *  The unknown/dispatch handler
 */
class DesignScripting : public IUnknown {
  public:
    /**
     *  On the fly idispatch
     */
    static DesignScripting* Create( ) {
      ITypeInfo *typeinfo;
      IUnknown  *dispatch;
      DesignScripting* scripting = new DesignScripting( );
      scripting->AddRef( );
      CreateDispTypeInfo( 
        &interface_table, 
        LOCALE_SYSTEM_DEFAULT, 
        &typeinfo 
      );
      CreateStdDispatch( 
        scripting, 
        &( scripting->binaryfile ), 
        typeinfo, 
        &dispatch 
      );
      typeinfo->Release( );
      scripting->unknown = dispatch;
      return scripting;      
    }
    
    STDMETHOD (  QueryInterface )( REFIID riid, void **ppv );
    STDMETHOD_( unsigned long, AddRef  )( void );
    STDMETHOD_( unsigned long, Release )( void );
    
    DesignScripting( ) {
      this->binaryfile.designscripting = this;
      this->references = 0; 
    }
    BinaryFile binaryfile;
  private:
    IUnknown* unknown;
    unsigned long references;
};

/**
 * The class factory handler
 */
class DesignScriptingClassFactory : public IClassFactory {
  public:
    static IClassFactory* Create( ) {
      return new DesignScriptingClassFactory( );
    }
    STDMETHOD ( QueryInterface )( REFIID riid, void **ppv );
    STDMETHOD_( unsigned long, AddRef  )( void );
    STDMETHOD_( unsigned long, Release )( void );
    STDMETHOD ( CreateInstance )( IUnknown *outer, REFIID id, void **ppv );
    STDMETHOD ( LockServer     )( BOOL lock );
    DesignScriptingClassFactory( ) { 
      this->references = 1; 
    }
  private:
    unsigned long references;
};

DesignScripting* designscripting;

/*******************************************************************************
 *  Implementation
 */

/**
 *  Destructor
 */
BinaryFile::~BinaryFile( ) {
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    CloseHandle( this->filehandle );  
  }
}

/**
 *  About
 */
STDMETHODIMP_( void ) BinaryFile::About( ) {  
  MessageBox( 
    NULL, 
    ABOUT_MESSAGE, 
    "About DesignScripting...", 
    MB_OK | MB_ICONINFORMATION 
  );
}  

/**
 *  Open file stream
 */
STDMETHODIMP_( bool ) BinaryFile::OpenFile( BSTR filename, BOOL readonly ) {  
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    CloseHandle( this->filehandle );
    this->filehandle = INVALID_HANDLE_VALUE;
  }
  int   length = WideCharToMultiByte( CP_ACP, 0, filename, -1, NULL, 0, NULL, NULL );
  char* buffer = new char[length];
  WideCharToMultiByte( CP_ACP, 0, filename, -1, buffer, length, NULL, NULL );
  this->filehandle = CreateFile(
    buffer, 
    GENERIC_READ | GENERIC_WRITE, 
    FILE_SHARE_READ | FILE_SHARE_WRITE, 
    NULL, 
    ( readonly ) ? OPEN_EXISTING : OPEN_ALWAYS, 
    FILE_ATTRIBUTE_NORMAL, 
    NULL 
  );
  delete[] buffer;
  return ( this->filehandle != INVALID_HANDLE_VALUE );  
}

/**
 *  Open COM stream
 */
STDMETHODIMP_( bool ) BinaryFile::OpenSerial( 
  long portnumber,
  long buffersize,
  long baudrate,
  long parity,
  long databits,
  long stopbits,
  long timeout ) { 
     
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    CloseHandle( this->filehandle );
    this->filehandle = INVALID_HANDLE_VALUE;
  }
  
  char* DeviceName = new char[256];
  wsprintf( DeviceName, "COM%d\0", portnumber );
  this->filehandle = CreateFile( DeviceName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL );
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    delete[] DeviceName;
    return false;
  } 
  delete[] DeviceName;
  
  if( SetupComm( this->filehandle, buffersize, buffersize ) == 0 ) {
    CloseHandle( this->filehandle );
    this->filehandle = INVALID_HANDLE_VALUE;
    return false;
  }
 
  DCB state;
  if( GetCommState( this->filehandle, &state ) == 0 ) {
    CloseHandle( this->filehandle );
    this->filehandle = INVALID_HANDLE_VALUE;
    return false;
  }

  char* config = new char[256];
  wsprintf( config, "baud=%d parity=%s data=%d stop=%d\0", baudrate, ( parity != 0 ) ? "y" : "n", databits, stopbits );
  if( BuildCommDCB( config, &state ) == 0 ) {
    delete[] config;
    CloseHandle( this->filehandle );
    this->filehandle = INVALID_HANDLE_VALUE;
    return false;
  }
  delete[] config;

  if( SetCommState( this->filehandle, &state ) == 0 ) {
    CloseHandle( this->filehandle );
    this->filehandle = INVALID_HANDLE_VALUE;
    return false;
  }

  COMMTIMEOUTS timeouts;
  timeouts.ReadIntervalTimeout         = 0;
  timeouts.ReadTotalTimeoutMultiplier  = 0;
  timeouts.ReadTotalTimeoutConstant    = timeout;
  timeouts.WriteTotalTimeoutMultiplier = 0;
  timeouts.WriteTotalTimeoutConstant   = timeout;
  if( SetCommTimeouts( this->filehandle, &timeouts ) == 0 ) {
    CloseHandle( this->filehandle );
    this->filehandle = INVALID_HANDLE_VALUE;
    return false;
  }

  return true;
}  

/**
 *  Write bytes to stream
 */
STDMETHODIMP_( bool ) BinaryFile::WriteBytes( LPVARIANT array ) {  
  BYTE* bytes;
  DWORD count, size;

  if( this->filehandle == INVALID_HANDLE_VALUE ) {
    return false;
  }

  if( array->vt != ( VT_ARRAY | VT_VARIANT ) ) {
    return false;
  }

  if( array->parray == NULL ) {
    return false;
  }

  if( array->parray->cDims != 1 ) {
    return false;
  }

  long lbound, ubound;

  if( FAILED( SafeArrayGetLBound( array->parray, 1, &lbound )) ||
      FAILED( SafeArrayGetUBound( array->parray, 1, &ubound ) ) ) {
    return false;
  }

  long item = 0;
  count = ubound - lbound + 1;
  bytes = (BYTE *)malloc( count );

  if( bytes == NULL ) {
    return false;
  }

  VARIANT element;

  for( long index = lbound; index <= ubound; index++ ) {
    if( FAILED( SafeArrayGetElement( array->parray, &index, &element ) ) ) {
      free( bytes );
      return false;
    } else {
      switch( element.vt ) {
        case VT_UI1: bytes[item++] = (BYTE)element.bVal; break;
        case VT_I2:  bytes[item++] = (BYTE)element.iVal; break; 
        case VT_I4:  bytes[item++] = (BYTE)element.lVal; break; 
        default:     bytes[item++] = 0;
      }
    }
  }

  if( WriteFile( this->filehandle, bytes, count, &size, NULL ) ) {
    free( bytes );
    return true;
  } else {
    free( bytes );
    return false;  
  }
}  

/**
 *  Read bytes from stream
 */
STDMETHODIMP_( bool ) BinaryFile::ReadBytes( LPVARIANT array ) {  
  BYTE* bytes;
  DWORD count, size;

  if( this->filehandle == INVALID_HANDLE_VALUE ) {
    return false;
  }

  if( array->vt != ( VT_ARRAY | VT_VARIANT ) ) {
    return false;
  }

  if( array->parray == NULL ) {
    return false;
  }

  if( array->parray->cDims != 1 ) {
    return false;
  }

  long lbound, ubound;

  if( FAILED( SafeArrayGetLBound( array->parray, 1, &lbound )) ||
      FAILED( SafeArrayGetUBound( array->parray, 1, &ubound ) ) ) {
    return false;
  }

  long item = 0;
  count = ubound - lbound + 1;
  bytes = (BYTE *)malloc( count );

  if( bytes == NULL ) {
    return false;
  }

  if( !ReadFile( this->filehandle,  bytes, count, &size, NULL ) ) {
    free( bytes );
    return false;
  }

  VARIANT element;

  for( long index = lbound; index <= ubound; index++ ) {
    if( FAILED( SafeArrayGetElement( array->parray, &index, &element ) ) ) {
      free( bytes );
      return false;
    } else {
      switch( element.vt ) {
        case VT_UI1: element.bVal = bytes[item++]; break;
        case VT_I2:  element.iVal = bytes[item++]; break;
        case VT_I4:  element.lVal = bytes[item++]; break;
      }
      if( FAILED( SafeArrayPutElement( array->parray, &index, &element ) ) ) {
        free( bytes );
        return false;
      }
    }
  }

  free( bytes );
  return true;
}  

/**
 *  Close stream
 */
STDMETHODIMP_( void ) BinaryFile::Close( ) {  
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    CloseHandle( this->filehandle );  
  }  
}  

/**
 *  Read a byte
 */
STDMETHODIMP_( long ) BinaryFile::ReadByte( ) {  
  DWORD bytes;
  BYTE  buffer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    ReadFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
    return buffer;
  } else {
    return -1;  
  }
}

/**
 *  Read a word
 */
STDMETHODIMP_( long ) BinaryFile::ReadWord( ) {  
  DWORD bytes;
  WORD  buffer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    ReadFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
    return buffer;
  } else {
    return -1;  
  }
}

/**
 *  Read a double word
 */
STDMETHODIMP_( long ) BinaryFile::ReadDWord( ) {  
  DWORD bytes;
  DWORD buffer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    ReadFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
    return buffer;
  } else {
    return -1;  
  }
}

/**
 *  Read a short int
 */
STDMETHODIMP_( long ) BinaryFile::ReadShort( ) {  
  DWORD bytes;
  short buffer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    ReadFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
    return buffer;
  } else {
    return -1;  
  }
}

/**
 *  Read a long int
 */
STDMETHODIMP_( long ) BinaryFile::ReadLong( ) {  
  DWORD bytes;
  long  buffer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    ReadFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
    return buffer;
  } else {
    return -1;  
  }
}

/**
 *  Read a float
 */
STDMETHODIMP_( float ) BinaryFile::ReadFloat( ) {  
  DWORD bytes;
  float buffer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    ReadFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
    return buffer;
  } else {
    return -1;  
  }
}

/**
 *  Read a double
 */
STDMETHODIMP_( double ) BinaryFile::ReadDouble( ) {  
  DWORD  bytes;
  double buffer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    ReadFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
    return buffer;
  } else {
    return -1;  
  }
}

/**
 *  Write a byte
 */
STDMETHODIMP_( void ) BinaryFile::WriteByte( long integer ) {  
  DWORD bytes;
  BYTE  buffer = (BYTE)integer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    WriteFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
  } 
}

/**
 *  Write a word
 */
STDMETHODIMP_( void ) BinaryFile::WriteWord( long integer ) {  
  DWORD bytes;
  WORD  buffer = (WORD)integer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    WriteFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
  } 
}

/**
 *  Write a double word
 */
STDMETHODIMP_( void ) BinaryFile::WriteDWord( long integer ) {  
  DWORD bytes;
  DWORD buffer = (DWORD)integer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    WriteFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
  } 
}

/**
 *  Write a short int
 */
STDMETHODIMP_( void ) BinaryFile::WriteShort( long integer ) {  
  DWORD bytes;
  short buffer = (WORD)integer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    WriteFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
  } 
}

/**
 *  Write a long int
 */
STDMETHODIMP_( void ) BinaryFile::WriteLong( long integer ) {  
  DWORD bytes;
  long  buffer = (DWORD)integer;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    WriteFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
  } 
}

/**
 *  Write a float
 */
STDMETHODIMP_( void ) BinaryFile::WriteFloat( double real ) {  
  DWORD bytes;
  float buffer = (float)real;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    WriteFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
  } 
}

/**
 *  Write a double
 */
STDMETHODIMP_( void ) BinaryFile::WriteDouble( double real ) {  
  DWORD  bytes;
  double buffer = real;
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    WriteFile( this->filehandle, &buffer, sizeof( buffer ), &bytes, NULL );
  } 
}

/**
 *  Stream location
 */
STDMETHODIMP_( long ) BinaryFile::Location( ) {  
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    return SetFilePointer( this->filehandle, 0, NULL, FILE_CURRENT );  
  } else {
    return -1;
  }
}

/**
 *  Stream seek
 */
STDMETHODIMP_( void ) BinaryFile::MoveTo( long integer ) {  
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    SetFilePointer( this->filehandle, integer, NULL, FILE_BEGIN ); 
  } 
}

/**
 *  Stream length
 */
STDMETHODIMP_( long ) BinaryFile::Length( ) {  
  if( this->filehandle != INVALID_HANDLE_VALUE ) {
    long save   = SetFilePointer( this->filehandle, 0, NULL, FILE_CURRENT );
    long length = SetFilePointer( this->filehandle, 0, NULL, FILE_END );
    SetFilePointer( this->filehandle, save, NULL, FILE_BEGIN );
    return length;  
  } else {
    return -1;
  }
}

/**
 *  IUknown stuff
 */
STDMETHODIMP DesignScripting::QueryInterface( REFIID id, void **ppv ) {
  if( IsEqualIID( id, IID_IUnknown ) ) {
    *ppv = this;
  } else if( IsEqualIID( id, IID_IDispatch ) ) {
    return this->unknown->QueryInterface( id, ppv );
  } else {
    *ppv = NULL;
    return ResultFromScode( E_NOINTERFACE );
  }
  this->AddRef( );
  return NOERROR;
}

/**
 *  IUknown stuff
 */
STDMETHODIMP_(unsigned long) DesignScripting::AddRef( ) {
  return ++this->references;
}

/**
 *  IUknown stuff
 */
STDMETHODIMP_(unsigned long) DesignScripting::Release( ) {
 if( ( --this->references ) == 0 ) {
   if( this->unknown != NULL ) { 
      this->unknown->Release( );
    }  
    delete this;
    return 0;
  } else {
    return this->references;
  }  
}

/**
 *  IUknown stuff
 */
STDMETHODIMP DesignScriptingClassFactory::QueryInterface( REFIID id, void **ppv ) {
  if( IsEqualIID( id, IID_IUnknown ) || 
      IsEqualIID( id, IID_IClassFactory ) ) {
    this->AddRef( );
    *ppv = this;
    return NOERROR;
  } else {
    *ppv = NULL;
     return ResultFromScode( E_NOINTERFACE );
  }  
}

/**
 *  IUknown stuff
 */
STDMETHODIMP_(unsigned long) DesignScriptingClassFactory::AddRef( ) {
  return this->references++;
}

/**
 *  IUknown stuff
 */
STDMETHODIMP_(unsigned long) DesignScriptingClassFactory::Release( ) {
  if( ( --this->references ) == 0 ) {
    delete this;
    return 0;
  } else {
    return this->references;
  }    
}

/**
 *  IClassFactory stuff
 */
STDMETHODIMP DesignScriptingClassFactory::CreateInstance( IUnknown *outer, REFIID id, void **ppv ) {
  if( outer != NULL ) {
    return ResultFromScode( CLASS_E_NOAGGREGATION );
  } else {
    DesignScripting* designscripting = DesignScripting::Create( );
    return designscripting->QueryInterface( id, ppv );
  }  
}

/**
 *  IClassFactory stuff
 */
STDMETHODIMP DesignScriptingClassFactory::LockServer( BOOL lock ) {
  return( NOERROR );
}

/**
 *  Library stuff
 */
STDAPI DLLEXPORT DllGetClassObject( const CLSID& rclsid, const IID& riid, void ** ppv ) {  
  if( !IsEqualGUID( rclsid, IID_DesignScripting ) ) { 
    return( E_FAIL ); 
  } 
  
  if( ( !IsEqualGUID( riid, IID_IUnknown ) ) && 
      ( !IsEqualGUID( riid, IID_IClassFactory ) ) ) { 
    return( E_NOINTERFACE ); 
  } 
  
  *ppv = new DesignScriptingClassFactory( ); 

  if( *ppv == NULL ) { 
    return( E_OUTOFMEMORY ); 
  } 
  
  ( (IUnknown*)*ppv )->AddRef( ); 
  
  return( S_OK ); 
}

/**
 *  Library stuff
 */
STDAPI DLLEXPORT DllCanUnloadNow( ) { 
  return( S_OK ); 
} 

/**
 *  Library stuff
 */
STDAPI DLLEXPORT DllRegisterServer( ) {
  char STR_DESIGNSCRIPTING[] = "DesignScripting\0";
  char STR_CLASSID[]         = "{32F5D805-FC94-4DDE-8F25-960710BD6263}\0";
  char STR_THREADINGMODEL[]  = "ThreadingModel\0";
  char STR_APPARTMENT[]      = "Apartment\0";
  char STR_FOLDER[512];

  HKEY keya, keyb, keyc;

  if( RegCreateKey( 
        HKEY_CLASSES_ROOT, 
        TEXT("DesignScripting"), 
        &keya ) != ERROR_SUCCESS ) {
    return E_UNEXPECTED;
  }

  if( RegSetValue( 
        keya, 
        NULL, 
        REG_SZ, 
        STR_DESIGNSCRIPTING, 
        sizeof( STR_DESIGNSCRIPTING ) ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    return E_UNEXPECTED;
  }

  if( RegCreateKey( 
        keya, 
        TEXT("CLSID"), 
        &keyb ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    return E_UNEXPECTED;
  }

  if( RegSetValue( 
        keyb, 
        NULL, 
        REG_SZ, 
        STR_CLASSID, 
        sizeof( STR_CLASSID ) ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    return E_UNEXPECTED;
  }

  RegCloseKey( keya ); 
  RegCloseKey( keyb ); 

  if( RegCreateKey( 
        HKEY_CLASSES_ROOT, 
        TEXT("CLSID"), 
        &keya ) != ERROR_SUCCESS ) {
    return E_UNEXPECTED;
  }

  if( RegCreateKey( 
        keya, 
        STR_CLASSID, 
        &keyb ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    return E_UNEXPECTED;
  }

  if( RegSetValue( 
        keyb, 
        NULL, 
        REG_SZ, 
        STR_DESIGNSCRIPTING, 
        sizeof( STR_DESIGNSCRIPTING ) ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    return E_UNEXPECTED;
  }

  if( RegCreateKey( 
        keyb, 
        TEXT("InProcServer32"), 
        &keyc ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    return E_UNEXPECTED;
  }
   
  if( GetModuleFileName(
    global_instance, 
    STR_FOLDER, 
    sizeof( STR_FOLDER ) / sizeof( TCHAR ) ) == 0 ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    RegCloseKey( keyc ); 
    return E_UNEXPECTED;
  }

  if( RegSetValue( 
        keyc, 
        NULL, 
        REG_SZ, 
        STR_FOLDER, 
        lstrlen( STR_FOLDER ) ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    RegCloseKey( keyc ); 
    return E_UNEXPECTED;
  }

  if( RegSetValueEx(
        keyc, 
        STR_THREADINGMODEL, 
        0, 
        REG_SZ, 
        (BYTE*)STR_APPARTMENT, 
        sizeof( STR_APPARTMENT ) ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    RegCloseKey( keyc ); 
    return E_UNEXPECTED;
  }

  RegCloseKey( keyc );

  if( RegCreateKey( 
        keyb, 
        TEXT("ProgID"), 
        &keyc ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    return E_UNEXPECTED;
  }

  if( RegSetValue( 
        keyc, 
        NULL, 
        REG_SZ, 
        STR_DESIGNSCRIPTING, 
        sizeof( STR_DESIGNSCRIPTING ) ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    RegCloseKey( keyc ); 
    return E_UNEXPECTED;
  }

  RegCloseKey( keyc );

  if( RegCreateKey( 
        keyb, 
        TEXT("VersionIndependentProgID"), 
        &keyc ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    return E_UNEXPECTED;
  }

  if( RegSetValue( 
        keyc, 
        NULL, 
        REG_SZ, 
        STR_DESIGNSCRIPTING, 
        sizeof( STR_DESIGNSCRIPTING ) ) != ERROR_SUCCESS ) {
    RegCloseKey( keya ); 
    RegCloseKey( keyb ); 
    RegCloseKey( keyc ); 
    return E_UNEXPECTED;
  }

  RegCloseKey( keya );
  RegCloseKey( keyb );
  RegCloseKey( keyc );

  return( S_OK ); 
}

/**
 *  Library stuff
 */
STDAPI DLLEXPORT DllUnregisterServer( ) {
  return( E_UNEXPECTED ); 
}

/**
 *  Old time library stuff
 */
extern "C" {
  BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved ) {
    global_references = 0;
    global_lock       = false;
    global_instance   = hInst;
    return TRUE;
  }
}