#include <afxwin.h>
#include "resource.h"
#include "tracer.h"
#include <math.h>

#define PICTURE_HEIGHT 300
#define PICTURE_WIDTH 300

// Define a window class derived from CFrameWnd
class CTracerWindow : public CFrameWnd
{
public:

    // Constructor
	CTracerWindow();
	
	// Message and command handlers
	afx_msg void OnPaint();
	afx_msg void OnCommand();
	afx_msg void ViewFront();
	afx_msg void ViewLeft();
	afx_msg void ViewRight();
	afx_msg void ViewBack();
	afx_msg void ViewRotate();
	
	// A direct set pixel to use while building the image
	void SetPixel(int x, int y, COLORREF c);
	
	// You have to do one of these if you want to handle messages.
	DECLARE_MESSAGE_MAP()

};

// The window as a global
CTracerWindow *MainWindow;
CBitmap Bitmap;
CDC Picture;

// Some serious balls
Sphere Object1(0.0, 0.0, 0.0, 5.0, 1.0, 0.5, 0.1, 0.5);
Sphere Object2(4.0, 2.0, -4.0, 2.0, 0.3, 1.0, 1.0, 0.5);
Sphere Object3(-4.0, 2.0, -4.0, 2.0, 0.3, 1.0, 1.0, 0.5);
// And a floor
BoundedPlane Object4(0.0, 1.0, 0.0, 6.0, -100.0, 100.0,
    -10000, 10000, -1000.0, 1000.0, 0.5, 0.5, 0.7, 0.2);
// And a ceiling
BoundedPlane Object5(0.0, 1.0, 0.0, -12.0, -100.0, 100.0,
    -10000, 10000, -1000.0, 1000.0, 0.7, 0.5, 0.5, 0.0);
// And a couple of lights
LightSource Light1(10.0, 10.0, -10.0, 50000.0, 50000.0, 50000.0);
LightSource Light2(-5.0, 4.0, 10.0, 30000.0, 30000.0, 30000.0);
    


// A ray tracer object
class MyTracer : public RayTracer
{
public:

    // The pixel displayer
    virtual void SetPixel(int x, int y, int r, int g, int b)
    {
        // Set the pixel in the bitmap
        Picture.SetPixel(x, PICTURE_HEIGHT-y, RGB(r, g, b));
        
        // Uncomment this to draw as we contruct the picture, but it is real slow.
        MainWindow->SetPixel(x, PICTURE_HEIGHT-y, RGB(r, g, b));
    }
} Ray;

// The message map for a window declares which windows messages it handles
BEGIN_MESSAGE_MAP(CTracerWindow, CFrameWnd)
	ON_WM_PAINT()
	ON_COMMAND(ID_VIEW_FRONT, ViewFront)
	ON_COMMAND(ID_VIEW_LEFT, ViewLeft)
	ON_COMMAND(ID_VIEW_RIGHT, ViewRight)
	ON_COMMAND(ID_VIEW_BACK, ViewBack)
	ON_COMMAND(ID_VIEW_ROTATE, ViewRotate)
END_MESSAGE_MAP()

// This is the constructor for CTracerWindow.  It simply loads the attributes of the Window defined in
// the .RC file

CTracerWindow::CTracerWindow()
{
    CDC *dc;
    
    // Create the window with attributes from Ray.rc
    LoadFrame(IDR_MAINFRAME);
    
    // Create the device context and bitmap for our little drawing
    dc = GetDC();
    Picture.CreateCompatibleDC(dc);
    Bitmap.CreateCompatibleBitmap(dc, PICTURE_WIDTH, PICTURE_HEIGHT);
    Picture.SelectObject(&Bitmap);
    Picture.SelectStockObject(BLACK_BRUSH);
    Picture.SelectStockObject(BLACK_PEN);
    Picture.Rectangle(0, 0, PICTURE_WIDTH-1, PICTURE_HEIGHT-1);
    ReleaseDC(dc);
}


void CTracerWindow::SetPixel(int x, int y, COLORREF c)
{
    CDC *dc;
    CRect r;
    int xo, yo;
    
    // Get a dc
    dc = GetDC();
    
    // Get the bounds of the client rect
    GetClientRect(&r);
    
    // center the picture
    xo = (r.right - PICTURE_WIDTH) / 2;
    yo = (r.bottom - PICTURE_WIDTH) / 2;
    
    // and display the pixel
    dc->SetPixel(xo+x, yo+y, c);
    
    ReleaseDC(dc);
}

void CTracerWindow::OnPaint()
{
    PAINTSTRUCT ps;
    CDC *dc;
    CRect r;
    int x, y;
    
    GetClientRect(&r);
    x = (r.right - PICTURE_WIDTH) / 2;
    if (x < 0) x = 0;
    y = (r.bottom - PICTURE_WIDTH) / 2;
    
    dc = BeginPaint(&ps);
    dc->BitBlt(x, y, PICTURE_WIDTH, PICTURE_HEIGHT, &Picture, 0, 0, SRCCOPY);    
    EndPaint(&ps);
}


void CTracerWindow::ViewLeft()
{
    Vector View;
    
    // Set up view position to the left of the scene
    View[X] = -15.0;
    View[Y] = 0.0;
    View[Z] = 0.0;

    Ray.Trace(&Object1, &Light1, PICTURE_WIDTH, PICTURE_HEIGHT, View);
    InvalidateRect(NULL);
}

void CTracerWindow::ViewRight()
{
    Vector View;
    
    // Set up view position to the right of the scene
    View[X] = 15.0;
    View[Y] = 0.0;
    View[Z] = 0.0;

    Ray.Trace(&Object1, &Light1, PICTURE_WIDTH, PICTURE_HEIGHT, View);
    InvalidateRect(NULL);
}

void CTracerWindow::ViewFront()
{
    Vector View;
    
    // Set up view position in front of the scene
    View[X] = 0.0;
    View[Y] = 0.0;
    View[Z] = -15.0;

    Ray.Trace(&Object1, &Light1, PICTURE_WIDTH, PICTURE_HEIGHT, View);
    InvalidateRect(NULL);
}

void CTracerWindow::ViewBack()
{
    Vector View;
    
    // Set up view position behind the scene
    View[X] = 0.0;
    View[Y] = 0.0;
    View[Z] = 15.0;

    Ray.Trace(&Object1, &Light1, PICTURE_WIDTH, PICTURE_HEIGHT, View);
    InvalidateRect(NULL);
}

void CTracerWindow::ViewRotate()
{
    Vector View;
    double Angle, t;
    int i;
    
    // Set up view position in front of the scene
    View[X] = 0.0;
    View[Y] = 0.0;
    View[Z] = -15.0;

    Angle = 3.14159 / 8;
    for (i=0; i<16; i++)
    {
        t = View[X];
        View[X] = View[X] * cos(Angle) + View[Z] * sin(Angle);
        View[Z] = View[Z] * cos(Angle) - t * sin(Angle);
	    Ray.Trace(&Object1, &Light1, PICTURE_WIDTH, PICTURE_HEIGHT, View);
	    Invalidate(0); 
	    UpdateWindow();     
    }
}


// Define an application class derived from CWinApp
class CTracerApp : public CWinApp
{
public:
	virtual BOOL InitInstance();
};

// Construct the CHelloApp's m_pMainWnd data member
BOOL CTracerApp::InitInstance()
{
    // Initialize and show the tracer window
	MainWindow = new CTracerWindow();
	m_pMainWnd = MainWindow;
	m_pMainWnd->ShowWindow(m_nCmdShow);
	m_pMainWnd->UpdateWindow();

    // Link out objects together
    Object1.next = &Object2;
    Object2.next = &Object3;
    Object3.next = &Object4;
    Object4.next = &Object5;
    
    Light1.next = &Light2;
    
	return TRUE;
}

CTracerApp TracerApp;  // HelloApp's constructor initializes and runs the app
