#include "stdafx.h"

#include <vector>

#include "Keys.h"
#include "Tile.h"
#include "Camera.h"
#include "EngineIO.h"
#include "Engine.h"
#include "Canvas3D.h"
#include "MaterialManager.h"

#include "Canvas3D.h"

// MFC - Assists in finding memory leaks:
#ifdef MFC_DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

Canvas3D* Canvas3D::_instance = 0;

/****************************************************************************/
/** \brief Liefert die Singleton-Instanz
    \return Pointer auf die Instanz
*****************************************************************************/
Canvas3D* Canvas3D::getInstance()
{
 if (_instance == 0) 
   _instance = new Canvas3D();
 return _instance;
}

/****************************************************************************/
/** \brief Konstruktor
*****************************************************************************/
Canvas3D::Canvas3D()
{
#ifdef WIN32
m_hdc = 0;
#endif

m_widthCanvas  = 0;
m_heightCanvas = 0;
m_height = 0;
m_width  = 0;
m_startX = 0;
m_startY = 0;

m_pDib = 0;
m_pDiData = 0;

m_engine = Engine::getInstance();
}

/****************************************************************************/
/** \brief initialisiert die Canvas - Laedt die binare Mapdatei
    \param datFile Pfad-Dateiname+Extension der (binaeren) Mapdatei
    \return true-->OK, false-->
*****************************************************************************/
bool Canvas3D::init(char* datFile)
{
//m_zephyr->saveAsMapfile(datFile);
if (m_engine->loadMapfile(datFile) == false)
   return (false);

m_engine->calcAnimation();
m_engine->calcLightMapGrid(); 
m_engine->calcLightMap(); 

return (true);
}

/****************************************************************************/
/** \brief Destruktor
*****************************************************************************/
Canvas3D::~Canvas3D()
{
delete (m_engine);

 if (m_pDib != 0)
   free(m_pDib);

// if (m_pDiData != 0)
//   free(m_pDiData);

_instance = 0; // Singleton !!!
}

/****************************************************************************/
/** \brief Initialisiert die Canvas und den Renderer
* @param widthCanvas Breite der Canvas
* @param heightCanvas Hoehe der Canvas
* @param width Breite 
* @param height Hoehe
*****************************************************************************/
void Canvas3D::initialize(int widthCanvas , int heightCanvas, int width, int height)
{
  m_widthCanvas  = widthCanvas;
  m_heightCanvas = heightCanvas;
  m_width  = width;
  m_height = height;
  
  m_startX = (m_widthCanvas  - m_width)  / 2;
  m_startY = (m_heightCanvas - m_height) / 2;
     
  createImage();
  
  m_engine->initRenderer(m_width, m_height, m_pDiData); 
}

/****************************************************************************/
/** \brief Rendert ein Frame in das Image
*****************************************************************************/
#ifdef WIN32
void Canvas3D::renderFrame(void)
{
 m_engine->renderFrame();
}
#else  // MAC bzw. OpenGL
void Canvas3D::renderFrame(void)
{
 m_engine->renderFrame();
 flipImage(); // fuer OpenGL!
}
#endif

/****************************************************************************/
/** \brief Zentrale Methode zum Rendern der Map
*****************************************************************************/
#ifdef WIN32
void Canvas3D::paint(HDC hdc)
{
 m_hdc = hdc;
 paintBltBit(hdc);
 paintCanvas(hdc);
}

/****************************************************************************/
/** \brief Zeichnet einen Rahmen in den Graphikkontext
    \param hdc Graphic-Devicecontext
*****************************************************************************/
void Canvas3D::paintCanvas(HDC hdc)
  {
  int ymin = m_startY;
  int ymax = m_startY + m_height;

  int xmin = m_startX;
  int xmax = m_startX + m_width;

  MoveToEx(hdc, xmin, ymin, NULL);
  LineTo(hdc,xmax, ymin); 
  LineTo(hdc,xmax, ymax);
  LineTo(hdc,xmin, ymax);
  LineTo(hdc,xmin, ymin);
 }

/****************************************************************************/
/** \brief Zeichnet das Image (Bitmap) in das Windowsfenster
    \param hdc Graphic-Devicecontext
*****************************************************************************/
void Canvas3D::paintBltBit (HDC hdc)
{
   int xoffset = m_startX; //0;
   int yoffset = m_startY; //0;

   int m_cxWinSize = m_width;
   int m_cyWinSize = m_height;

   HDC memDC = CreateCompatibleDC(hdc);
   HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, m_width, m_height);
   HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);

   SetDIBitsToDevice (memDC, 0, 0, m_cxWinSize, m_cyWinSize, 0, 0,
                0, m_cyWinSize, m_pDiData, (BITMAPINFO *) m_pDib, DIB_RGB_COLORS);
 
   BitBlt(hdc, xoffset, yoffset, m_width, m_height, memDC, 0, 0, SRCCOPY);

   SelectObject(memDC, hOldBmp);
   DeleteObject(hMemBmp);
   DeleteDC(memDC); 
}

/****************************************************************************/
/** \brief Erstellt das Image (WIN32-->DIB Image)
    \return true-->OK, false-->Fehler
*****************************************************************************/
bool Canvas3D::createImage()
{
   BITMAPINFOHEADER *pbmih;
   WORD wDIRowBytes;

   wDIRowBytes = (WORD) ((3 * m_width + 3L) >> 2) << 2;

   if (m_pDib != 0)
      free(m_pDib);

   m_pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) + wDIRowBytes * m_height);
   if (m_pDib == 0)
      {
      return (false);
      }

   memset (m_pDib, 0, sizeof(BITMAPINFOHEADER) + wDIRowBytes * m_height);

   pbmih = (BITMAPINFOHEADER *) m_pDib;
   pbmih->biSize     = sizeof(BITMAPINFOHEADER);
   pbmih->biWidth    = m_width;
   pbmih->biHeight   = -((long) m_height);
   pbmih->biPlanes   =  1;
   pbmih->biBitCount = 24;
   pbmih->biCompression = 0;

   m_pDiData = m_pDib + sizeof(BITMAPINFOHEADER); // Pointer auf Datenbereich

   InitBitmap (m_pDiData, m_width, m_height);

   return (true);
}
#else
/****************************************************************************/
/** \brief Zentrale Methode zum Rendern der Map
*****************************************************************************/
void Canvas3D::paint(void)
{
}

/****************************************************************************/
/** \brief Erstellt das Image (hier fuer Mac/OpenGL als einfachen 
           Speicherbereich (BYTE-Array)
    \return true-->OK, false-->Fehler
*****************************************************************************/
bool Canvas3D::createImage()
{ //                                          3
   m_pDiData = (BYTE*) malloc (sizeof(BYTE) * 4 * m_width * m_height);
   if (m_pDiData == 0)
      return (false);
  
   InitBitmap (m_pDiData, m_width, m_height);

   return (true);
}
#endif

/****************************************************************************/
/** \brief Windows-Bitmap --> OpenGL Image:
* 1. Ursprung von oben links nach unten links
* 2. BGR --> RGB
*****************************************************************************/
void Canvas3D::flipImage(void)
{
BYTE b0,b1,b2;
int h2 = m_height / 2;
int ho, hu;
for (ho=0, hu=m_height-1; ho < h2; ho++, hu--)
   {
   for (int w=0; w < m_width; w++)
      {
      int io = (ho * m_width + w) * 3;
      int iu = (hu * m_width + w) * 3;
      
      b0 = m_pDiData[io+0];
      b1 = m_pDiData[io+1];
      b2 = m_pDiData[io+2];
      
      m_pDiData[io+0] = m_pDiData[iu+2];
      m_pDiData[io+1] = m_pDiData[iu+1];
      m_pDiData[io+2] = m_pDiData[iu+0];

      m_pDiData[iu+0] = b2;
      m_pDiData[iu+1] = b1;
      m_pDiData[iu+2] = b0;
      }
   }
}

/****************************************************************************/
/** \brief Eingabe und Konvertierung eines keycodes
    \param pressed true-->Taste wurde gedrueckt, false->Taste wurde losgelassen
    \param key Keycode (Anhaengig vom Betriebssystem)
*****************************************************************************/
bool Canvas3D::inputKey(bool pressed, int key)
{
int c4key = C4_KEY_UNKNOWN;

switch (key)
   {
#ifdef WIN32  // Windows
   case VK_UP:     c4key = C4_KEY_UP;    break;
   case VK_DOWN:   c4key = C4_KEY_DOWN;  break;
   case VK_LEFT:   c4key = C4_KEY_LEFT;  break;
   case VK_RIGHT:  c4key = C4_KEY_RIGHT; break;

   case VK_NEXT:   c4key = C4_KEY_NEXT;  break;
   case VK_PRIOR:  c4key = C4_KEY_PRIOR; break;

   case VK_DELETE: c4key = C4_KEY_DEL;   break;
   case VK_END:    c4key = C4_KEY_END;   break;

   case VK_F1:     c4key = C4_KEY_F1;    break;
   case VK_F2:     c4key = C4_KEY_F2;    break;
   case VK_F3:     c4key = C4_KEY_F3;    break;
   case VK_F4:     c4key = C4_KEY_F4;    break;
   case VK_F5:     c4key = C4_KEY_F5;    break;
   case VK_F6:     c4key = C4_KEY_F6;    break;
   case VK_F7:     c4key = C4_KEY_F7;    break;
   case VK_F8:     c4key = C4_KEY_F8;    break;
   case VK_F9:     c4key = C4_KEY_F9;    break;
   case VK_F10:    c4key = C4_KEY_F10;   break;
   case VK_F11:    c4key = C4_KEY_F11;   break;
   case VK_F12:    c4key = C4_KEY_F12;   break;
#else // Mac bzw. OpenGL, GLUT
   case 'i':       c4key = C4_KEY_F1;    break;
   case 'w':       c4key = C4_KEY_UP;    break;
   case 'a':       c4key = C4_KEY_LEFT;  break;
   case 's':       c4key = C4_KEY_DOWN;  break;
   case 'd':       c4key = C4_KEY_RIGHT; break;

   case 'y':       c4key = C4_KEY_NEXT;  break;
   case 'x':       c4key = C4_KEY_PRIOR; break;
#endif
   }
return (m_engine->inputKey(pressed, c4key));
}

/****************************************************************************/
/** \brief Getter fuer die Framerate
    \return frames per second
*****************************************************************************/
double Canvas3D::getFrameRate(void)
{
return (m_engine->getFrameRate());
}

/****************************************************************************/
/** \brief Getter fuer die Anzahl der gerenderten Tiles im aktuellen Frame
    \return tiles per frame
*****************************************************************************/
int Canvas3D::getTilesPerFrame(void)
{
return (m_engine->getTilesPerFrame());
}

/****************************************************************************/
/** \brief Getter fuer die Anzahl der gerenderten Partikel im aktuellen Frame
    \return partikel per second
*****************************************************************************/
int Canvas3D::getPartikelsPerFrame(void)
{
return (m_engine->getPartikelsPerFrame());
}

/****************************************************************************/
/** \brief Initialisiert die Bitmap
    \param pDiData Pointer auf Bitmap
    \param cxWinSize Breite der Bitmap
    \param cyWinSize Hoehe  der Bitmap
*****************************************************************************/
#ifdef WIN32
void Canvas3D::InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
{
   BYTE *dst;
   int x, y, col;

   dst = pDiData;
   for (y = 0; y < cyWinSize; y++)
      {
      col = 0;
      for (x = 0; x < cxWinSize; x++)
         {
         *dst++ = 255;
         *dst++ = 255;
         *dst++ = 255;
         col += 3;
         }

      while ((col % 4) != 0)
         {
         dst++;
         col++;
         }
      }
}
#else // Fuer Mac / OpenGL
void Canvas3D::InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
{
}
#endif
