#include <windows.h>
#include <math.h>
#include "windxf.h"
#include <stdlib.h>
#include <stdio.h>

extern HWND GBL_hwnd;
extern HDC mfhdc;
extern HDC GBL_hdc;

typedef struct {
   int   number;
   char  marker[80];
   } DXF_STATE;

#define  NUM_STATES  30

DXF_STATE statetab[] =  { 0,"SECTION",  /* Section Begin      1 */
                          2,"HEADER",   /* Header             2 */
                          0,"ENDSEC",   /* End Section        3 */
                          2,"TABLES",   /* Tables section     4 */
                          0,"TABLE",    /* Table Begin        5 */
                          2,"VPORT",    /* Viewport           6 */
                          70,"#",       /* 70 with a count    7 */
                          0,"ENDTAB",   /* End of table       8 */
                          9,"$EXTMIN",  /* Min Extents        9 */
                          9,"$EXTMAX",  /* Max Extents        10 */
                          10,"#",       /* X coord            11 */
                          20,"#",       /* Y coord            12 */
                          2,"ENTITIES", /* Entities           13 */
                          11,"#",       /* X2 coord           14 */
                          21,"#",       /* Y2 coord           15 */
                          0,"LINE",     /* Line Entity        16 */
                          0,"ATTDEF",   /* Attribute Def      17 */
                          8, "#",       /* Layer Name         18 */
                          40,"#",       /* Height             19 */
                          1, "#",       /* Primary Text       20 */
                          2, "#",       /* Name               21 */
                          3, "#",       /* Other Text         22 */
                          50,"#",       /* Angle              23 */
                          51,"#",       /* Angle              24 */
                          0, "ARC",     /* Arc                25 */
                          0, "POLYLINE",/* Polyline           26 */
                          0, "VERTEX",  /* Vertex             27 */
                          0, "SEQEND",  /* End Sequence       28 */
                          66, "#",      /* Entities Follow    29 */
                          41, "#",      /* End Width          30 */
                          };

typedef enum {
   NUMBER,
   MARKER,
   } READ_STATE;

READ_STATE  curstate = NUMBER;


char  layer[256], heightstr[256], textstr[256];
double wx1, wy1, wx2, wy2;
int logextx, logexty;

int 
ReadDXF(dxfname)
char * dxfname;
{
int fd;
char  line1[256], line2[256];
int st;
double x, y;
int color;
char msg[256];
int xi,yi;
int lx, ly;

int   section_active  = 0;
int   header_active   = 0;
int   entities_active = 0;
int   tables_active   = 0;
int   table_start     = 0;
int   polyline_count  = 0;

double height, radius, st_ang, end_ang;

MSG msg1;

fd = _lopen(dxfname, OF_READ);


while (1) 
  {
   PeekMessage(&msg1, GBL_hwnd,(WORD)0,(WORD)0,PM_NOREMOVE);

   if (getline(line1, 256, fd) == NULL)
      break;

   if (getline(line2, 256, fd) == NULL)
      break;

   st = dxfstate(line1, line2);
   switch(st)
     {
      case 1: /* Section */
           section_active = 1;
           break;
      case 2: /* Header */
           header_active = 1;
           break;
      case 3: /* End Section */
           section_active = 0;
           /* Whatever section was active, shut it down */
           if (header_active)
             header_active = 0;
           else
           if (entities_active)
             entities_active = 0;
           else
           if (tables_active)
             tables_active = 0;
           break;

      case 4: /* Tables Active */
           tables_active = 1;
           break;
      case 5: /* Table */
           table_start = 1;
           break;
      case 8: /* End table */
           table_start = 0;
           break;
      case 9: /* Min Extents */
           get_followxyfloat(fd, &wx1, &wy1);
           break;
      case 10: /* Max Extents */
           get_followxyfloat(fd, &wx2, &wy2);
           color = 7;

           /* Now we're ready to set our world coords.
              Adjust the smaller of the axis to form
              a square world coordinate system */

           if ( (wy2 - wy1) > (wx2 - wx1) )
             {
              wx2 += (((wy2-wy1)/(wx2-wx1))*(wx2-wx1))/2.0;
              wx1 -= (((wy2-wy1)/(wx2-wx1))*(wx2-wx1))/2.0;
             }
           else
             {
              wy2 += (((wx2-wx1)/(wy2-wy1))*(wy2-wy1))/2.0;
              wy1 -= (((wx2-wx1)/(wy2-wy1))*(wy2-wy1))/2.0;
             }
           break;

      case 13: /* Entities */
           entities_active = 1;
           break;

      case 16: /* Line Entities */
           if (entities_active)
           {
             get_followxyfloat(fd, &x, &y);
             MapUsertoLog(x,y,&lx,&ly);
             MoveTo(GBL_hdc, lx, ly);
             MoveTo(mfhdc, lx, ly);
             get_followxyfloat(fd, &x, &y);
             MapUsertoLog(x,y,&lx,&ly);
             LineTo(GBL_hdc, lx, ly);
             LineTo(mfhdc, lx, ly);
           }
           break;

      case 17: /* Attribute Def */
         get_followline(fd, 18, layer);
         get_followxyfloat(fd, &x, &y);
         get_followline(fd, 19, heightstr);
         height = atof(heightstr);
         get_followline(fd, 20, textstr);
         break;

      case 25: /* Arc */
         get_followarc(fd, &x,&y,&radius,&st_ang,&end_ang);
         /* MS Windows doesn't support an arc the way DXF does,
            so we'll leave this one for a future version */
         /* movabs(&x,&y);
         arc(&radius,&end_ang,&st_ang); */
         break;

      case 26: /* Polyline */
        if (entities_active)
          {
            get_followline(fd, 18, textstr);
            get_followline(fd, 29, textstr);
            /* Polyline is justing setting us up to read
            vertices */
            polyline_count = 0;
          }
      break;
      case 27: /* Vertex */
      if (entities_active)
       {
         get_followxyfloat(fd, &x, &y);
         MapUsertoLog(x,y,&lx,&ly);
         if (polyline_count==0)
           {
             /* If this is the first vertex, move to it */
             MoveTo(GBL_hdc, lx, ly); 
             MoveTo(mfhdc, lx, ly); 
           }
         else
           {
             /* Line to subsequent vertices */
             LineTo(GBL_hdc, lx, ly); 
             LineTo(mfhdc, lx, ly); 
           }
         polyline_count++;
       }
       break;

    case 28: /* Sequence end */
      if (entities_active)
         {
           if (polyline_count > 0)
              polyline_count = 0;
         }
      break;
       } /* end of switch */
   } /* end of while(1) */
_lclose(fd);
return 1;
}

int dxfstate(line1, line2)
char * line1;
char * line2;
{
int ctr;
int num;
int l;

num = atoi(line1);
l = lstrlen(line2);

for (ctr=0; ctr < NUM_STATES; ctr++)
  {
   if ( (statetab[ctr].number == num) &&
        (!lstrcmp(statetab[ctr].marker,line2)))
        return ctr+1;
   if ( (statetab[ctr].number == num) &&
        (!lstrcmp(statetab[ctr].marker,"#")))
        return ctr+1;
  }

return -1;
}

int
get_followxyfloat(fd, x, y)
int fd;
double * x;
double * y;
{
long pos;
int  ctr, st2;
char line1[256], line2[256];
double p1, p2;

char msg[256];


 pos = _llseek(fd,0L,1);
 ctr = 0;
 while (ctr < 2)
   {
     getline(line1, 256, fd);
     getline(line2, 256, fd);
     st2 = dxfstate(line1, line2);
     if ( (st2 == -1)  || (st2 == 18) )
       continue;
     if ( (st2 == 11) || (st2 == 14) )
      {
       p1 = atof(line2);  
       *x = p1;
       ctr++;
       continue;
      }
     if ( (st2 == 12) || (st2 == 15) )
      {
       p2 = atof(line2); 
       *y = p2;
       ctr++;
       continue;
      }
     _llseek(fd, pos, 0);
   }

return 1;
}

int
get_followline(fd, st, line)
int fd;
int st;
char * line;
{
long pos;
int  st2;
char line1[256], line2[256];

 pos = _llseek(fd,0L,1);
 while (1)
   {
     getline(line1, 256, fd);
     getline(line2, 256, fd);
     st2 = dxfstate(line1, line2);
     if (st2 == -1)
       continue;
     if (st2 == st)
      {
       lstrcpy(line, line2);
       break;
      }
     _llseek(fd, pos, 0);
   }
return 1;
}

int
get_followarc(fd, x, y, radius, st_ang, end_ang)
int fd;
double * x;
double * y;
double * radius;
double * st_ang;
double * end_ang;
{
long pos;
int  ctr, st2;
char line1[256], line2[256];

 pos = _llseek(fd,0L,1);
 ctr = 0;
 while (ctr < 5)
   {
     getline(line1, 256, fd);
     getline(line2, 256, fd);
     st2 = dxfstate(line1, line2);
     if ( (st2 == -1)  || (st2 == 18) )
       continue;
     if ( (st2 == 11) || (st2 == 14) )
      {
       *x = atof(line2);
       ctr++;
       continue;
      }
     if ( (st2 == 12) || (st2 == 15) )
      {
       *y = atof(line2);
       ctr++;
       continue;
      }
     if ( st2 == 19 )
      {
       *radius = atof(line2);
       ctr++;
       continue;
      }
     if ( st2 == 23 )
      {
       *st_ang = atof(line2);
       ctr++;
       continue;
      }
     if ( st2 == 24 )
      {
       *end_ang = atof(line2);
       ctr++;
       continue;
      }
     _llseek(fd, pos, 0);
   }
return 1;
}

/* Windows doesn't support fgets, so we have to write
   our own binary, unbuffered version.  This program could
   be greatly enhanced by implementing a look ahead buffer 
   here */

int getline(line, lim, fd) 
char * line;
int lim;
int fd;
{
int ctr;
int nread;
long backup;
long newpos;

nread = _lread(fd, line, lim-1);

if (nread < lim-1)
 {
  return 0;
 }

for (ctr=0; ctr < lim-1; ctr++)
  if ( (line[ctr] == (char)13) &&
       (line[ctr+1] == (char)10) )
       {
        line[ctr] = (char)NULL;
        backup = ctr - lim + 3;
        newpos = _llseek(fd, (long)backup, 1);
        return 1;
       }
line[lim-1] = '\0';

/* Should never get here... */
MessageBox(GBL_hwnd, "Didn't hit loop", (LPSTR) NULL, MB_OK);
}

/* maps user coordinates to logical device coordinates */
int
MapUsertoLog(x,y,lx,ly)
double  x;
double  y;
int * lx;
int * ly;
{
*lx = (int) (((x-wx1)/(wx2-wx1)) * (double)1000);
*ly = (int) (((y-wy1)/(wy2-wy1)) * (double)1000);
*ly = 1000 - *ly;
}
