/**************************************************************************/
/* The next few lines are standard "boiler plate" code all UDF's have     */
/**************************************************************************/

#include <windows.h>
#include <dde.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <malloc.h>

/* boiler plate declarations for functions in zclient we will call */
typedef struct
	{
   double x, y, z, l, m, n, opd, intensity;
	double Exr, Exi, Eyr, Eyi, Ezr, Ezi;
	int wave, error, vigcode, want_opd;
	}DDERAYDATA;

void WaitForData(HWND hwnd);
char *GetString(char *szBuffer, int n, char *szSubString);
void remove_quotes(char *s);
int PostRequestMessage(char *szItem, char *szDataOut);
int PostArrayTraceMessage(char *szBuffer, DDERAYDATA *RD);
void CenterWindow(HWND hwnd);
void UserFunction(char *szCommandLine);
void MakeEmptyWindow(int text, char *szAppName, char *szOptions);
void Get_2_5_10(double cmax, double *cscale);

/* boiler plate global variables used by the client code */
extern HINSTANCE globalhInstance;
extern HWND hwndClient;


/**************************************************/
/* Below is the code specific to this UDF program */
/**************************************************/

// NSCTraceDemo sample program
// Written by Kenneth Moore December 2000
// Version 1.0

// This sample program illustrates using ZCLIENT calls to trace 1 NSC ray with optional splitting and scattering
// with the GetTRaceArray family of DDE calls. It is really easy to do!
// Most of this code is just for defining the rays to trace and printing the results.


/* okay, now the actual code that creates the UDF data for display in a ZEMAX window */
/* this function MUST be called UserFunction and take the command line argument      */

void UserFunction(char *szCommandLine)
{
char szModuleName[300], szOutputFile[300];
FILE *output;
int k, show_settings, nMaxSegments, nNumSegments;
int levl, parn, hito, insi;
static char szBuffer[5000], szSub[256], szAppName[] = "NSC Trace Demo";
DDERAYDATA *RD;


/* extract the command line arguments */
show_settings = atoi(GetString(szCommandLine, 1, szSub));
/* this tells us where to put the output data */
GetString(szCommandLine, 2, szOutputFile);
remove_quotes(szOutputFile);

//MessageBox(hwndClient, szCommandLine, "szCommandLine:", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
//MessageBox(hwndClient, szOutputFile, "Output file:", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);

/* first, update the lens so we have the latest data; and then test to make sure the system is valid */
PostRequestMessage("GetUpdate", szBuffer);

if (atoi(GetString(szBuffer, 0, szSub)))
	{
   /* the current lens cannot be traced! */
   /* some features may be able to create data without tracing rays; some can't */
   /* If data cannot be created return "empty" data displays */
   sprintf(szBuffer,"???");
   MakeEmptyWindow(1, szAppName, szBuffer);
   return;
   }

if (show_settings) MessageBox (hwndClient, "This window has no options.", "ZEMAX Client Window", MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);

/* make RD big enough to hold all the ray segments. */
PostRequestMessage("GetNSCSettings", szBuffer);
//MessageBox(hwndClient, szBuffer, "GetNSCSettings:", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);

/*
szBuffer will now hold this data:
MaxIntercepts
MaxSegments
MaxNesting
MinAbsoluteI
MinRelativeI
GlueDistance

The most important one is the number of segments... this is how many branches any
one ray can create. We need to make RD at least that big, or ZEMAX may ovewrwrite the buffer!!
*/

nMaxSegments = atoi(GetString(szBuffer, 1, szSub));

RD = (DDERAYDATA *) malloc(sizeof(DDERAYDATA) * (nMaxSegments+2));
if (!RD)
	{
	/* not enough memory!! */
	sprintf(szBuffer,"Not enough memory for %i segments!", nMaxSegments);
	MessageBox(hwndClient, szBuffer, "No Memory!", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
   sprintf(szBuffer,"???");
   MakeEmptyWindow(1, szAppName, szBuffer);
   return;
	}

/* zero the array */
ZeroMemory(&RD[0], sizeof(DDERAYDATA) * (nMaxSegments+2));

/* Fill RD with data to trace some rays */
/* note NSC rays use these fields somewhat differently than sequential rays!! */
/* only RD[0] is used... only one ray at a time may be traced! */
RD[0].x = 0.0;
RD[0].y = 0.0;
RD[0].z = 0.0;
RD[0].l = 0.0;
RD[0].m = 0.0;
RD[0].n = 1.0;
RD[0].Exr = 0.0;
RD[0].Exi = 0.0;
RD[0].Eyr = 1.0;
RD[0].Eyi = 0.0;
RD[0].Ezr = 0.0;
RD[0].Ezi = 0.0;
RD[0].opd = 5.0 + nMaxSegments; /* mode 5, NSC rays!! */
RD[0].intensity = 1.0;
RD[0].wave = 0; /* ZEMAX will choose wavelength according to weights */
RD[0].error = 1; /* this is the surface number; this sample assumes the NSC surface is 1 */
RD[0].vigcode = 3; /* this controls polarization, split, and scatter; see below */
RD[0].want_opd =  0; /* this is the inside of flag; zero if the source is not inside anything */

/*
The vigcode is used to set if rays are polarized, split, and scatter. Use 0
for no polarization, 1 for polarization; plus 2 for split or plus 0 for no
splitting, plus 4 for scattering or plus 0 for no scattering.

The resulting integer is between 0 and 7. Note if splitting you must set polarization on.

The sample "3" value means polarization and splitting, but no scattering.

*/

/* Now go get the data */
PostArrayTraceMessage(szBuffer, RD);
/* Okay, we got the data! There, wasn't that easy! */

/* open a file for output */
if ((output = fopen(szOutputFile, "wt")) == NULL)
	{
   /* can't open the file!! */
   return;
   }

/* this windows function gives us the name of our own executable; we pass this back to ZEMAX */
GetModuleFileName(globalhInstance, szModuleName, 255);

/* ok, make a text listing */
fputs("Listing of NSC Trace data\n",output);

fputs("seg# Prnt Levl   In  Hit               X               Y               Z Intensity\n", output);

/* the total number of segments is stored in the "parent" slot in segment 0 */
nNumSegments = RD[0].want_opd;
RD[0].want_opd = 0;

//sprintf(szBuffer,"There are %i ray segments.", nNumSegments);
//MessageBox(hwndClient, szBuffer, "# segements:", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);

for (k = 0; k <= nNumSegments; k++)
	{
	levl = RD[k].wave;
	parn = RD[k].want_opd;
	hito = RD[k].error;
	insi = RD[k].vigcode;
   sprintf(szBuffer, "%4i %4i %4i %4i %4i %15.6E %15.6E %15.6E %7.4f\n", k, parn, levl, insi, hito, RD[k].x, RD[k].y, RD[k].z, RD[k].intensity);
	fputs(szBuffer, output);
   }
/* close the file! */
fclose(output);

/* free the RAM! */
free(RD);

/* create a text window. Note we pass back the filename and module name. */

//MessageBox(hwndClient, szOutputFile, "Output file:", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);

sprintf(szBuffer,"MakeTextWindow,\"%s\",\"%s\",\"%s\"", szOutputFile, szModuleName, szAppName);

//MessageBox(hwndClient, szBuffer, "szBuffer:", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);

PostRequestMessage(szBuffer, szBuffer);
}



