/*****************************************************************************
*   Module to load/save EEDraw files.					     *
*									     *
* Written by:  Gershon Elber			IBM PC Ver 1.0,	Oct. 1989    *
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <dos.h>
#include <alloc.h>
#include "Program.h"
#include "Director.h"
#include "EELibs.h"
#include "EEModify.h"
#include "EERedraw.h"
#include "EELayer.h"
#include "EELoad.h"

#define MAIN_HEAD_STRING "EEDraw EEDRAW PROGRAM Version 1.0"
#define EEDRAW_FILE_STAMP "EEDraw"
#define NULL_STRING "_NONAME_"
#define FILE_EXTENSION ".EED"
#define NETLIST_FILE_EXTENSION ".NET"

static IntrCursorShapeStruct Cursor;
static int GlblNumOfPolylines = 0;

static void GetPolylineNetList(DrawPolylineStruct *PolylineStruct,
			       DrawGenericStruct *Phead,
			       NetListStruct **NetListHead,
			       BooleanType *IsABus);
static void GetOnePolylineNetList(DrawPolylineStruct *PolylineStruct,
				  DrawGenericStruct *Phead,
				  NetListStruct **NetListHead,
				  BooleanType *IsABus);
static int StringComp(const void *Str1, const void *Str2);
static BooleanType ConnectionExistsAtXY(DrawGenericStruct *Phead, int x, int y);

/*****************************************************************************
* Routine to clear EEDraw drawing list of a window.			     *
*****************************************************************************/
BooleanType ClearDrawList(EEWindowStruct *Window)
{
    DrawGenericStruct *DrawStruct,
        *EEDrawList = Window -> EEDrawList;

    Cursor.CursorType = INTR_CURSOR_ARROW;
    if (!IntrQueryYesNo("Clear old data?", EEPopUpFrameColor,
                	EEPopUpBackColor, EEPopUpForeColor, EEPopUpXorColor,
			EEWindowsFrameWidth, &Cursor,
                        INTR_WNDW_PLACE_CENTER)) return FALSE;

    while (EEDrawList != NULL) {
        DrawStruct = EEDrawList;
	EEDrawList = EEDrawList -> Pnext;
	FreeStruct(DrawStruct);
    }

    Window -> EEDrawList = NULL;

    return TRUE;
}

/*****************************************************************************
* Routine to close the given window. Screen is NOT refreshed.		     *
*****************************************************************************/
void CloseEEDFile(EEWindowStruct *Window)
{
    DrawGenericStruct *EEDrawList, *DrawStruct;

    if (Window == NULL) return;

    Cursor.CursorType = INTR_CURSOR_ARROW;

    IntrWndwDelete(Window -> IntrLibWindowID, FALSE);

    EEDrawList = Window -> EEDrawList;
    while (EEDrawList != NULL) {
	DrawStruct = EEDrawList;
	EEDrawList = EEDrawList -> Pnext;
	FreeStruct(DrawStruct);
    }

    if (Window -> FileName != NULL);
        MyFree(Window -> FileName);
}

/*****************************************************************************
* Routine to load an EEDraw file.					     *
* IF Window -> FileName has not name (length 0) a file named is queried.     *
* Returns TRUE if file has been loaded (at list partially.)		     *
*****************************************************************************/
BooleanType LoadEEFile(EEWindowStruct *Window)
{
    char Line[LINE_LEN], Name1[LINE_LEN_SHORT], Name2[LINE_LEN_SHORT],
			     Char1[LINE_LEN_SHORT], Char2[LINE_LEN_SHORT];
    FileNameType *FileNames;
    int i, NumOfFiles,
	LineCount = 1;
    int Main = 0, Sub = 0;	/* Program Version Information */
    int headsize;
    BooleanType
	Failed = FALSE;
    DrawGenericStruct *Phead, *Pnext;
    DrawLibItemStruct *LibItemStruct;
    DrawConnectionStruct *ConnectionStruct;
    DrawPolylineStruct *PolylineStruct;
    DrawTextStruct *TextStruct;
    EEWindowStruct *TmpWindow;
    FILE *f;

    Cursor.CursorType = INTR_CURSOR_ARROW;
    sprintf(Line, "*%s", FILE_EXTENSION);
    if (strlen(Window -> FileName) == 0) {
	if ((FileNames = GetFileNamesDir(Line, ".", &NumOfFiles)) != NULL) {
	    qsort(FileNames, NumOfFiles, sizeof(FileNameType),
		  (int (*)(const void *, const void *)) strcmp);
	    if ((i = IntrQueryList("EED files", (char **) FileNames,
		sizeof(FileNameType), NumOfFiles, EEListNumDisplayed,
		EEPopUpFrameColor, EEPopUpBackColor, EEPopUpForeColor,
		EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                INTR_WNDW_PLACE_CENTER)) < 0) {
		MyFree((VoidPtr) FileNames);
		return FALSE;
	    }
	    strcpy(Window -> FileName, FileNames[i]);
	    MyFree((VoidPtr) FileNames);
        }
        else {
            IntrQueryContinue("No EEDraw files found.", EEPopUpFrameColor,
		         EEPopUpBackColor, EEPopUpForeColor, EEPopUpXorColor,
			 EEWindowsFrameWidth, &Cursor,
                         INTR_WNDW_PLACE_CENTER);
	    return FALSE;
	}
    }

    if ((f = fopen(Window -> FileName, "rt")) == NULL) {
	sprintf(Line, "Failed to open \"%s\"", Window -> FileName);
	IntrQueryContinue(Line, EEPopUpFrameColor, EEPopUpBackColor,
			  EEPopUpForeColor, EEPopUpXorColor,
			  EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);

	return FALSE;
    }

    if (fgets(Line, LINE_LEN - 1, f) == NULL ||
	strncmp(Line, EEDRAW_FILE_STAMP, sizeof(EEDRAW_FILE_STAMP) - 1) != 0) {
	sprintf(Line, "\"%s\" is NOT EEDraw file", Window -> FileName);
	IntrQueryContinue(Line, EEPopUpFrameColor, EEPopUpBackColor,
        		  EEPopUpForeColor, EEPopUpXorColor,
			  EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	fclose(f);
	return FALSE;
    }
    sscanf(Line,"EEDraw EEDRAW PROGRAM Version %d.%d",&Main,&Sub);
    headsize = strlen(Line) -1 ;
    if(((Main <= 2) & (Sub <= 3)) || (headsize != strlen(MAIN_HEAD_STRING))){
	sprintf(Line,"Old version database, %d.%d Converting........  ",
		Main,Sub);
	IntrDrawMessage(Line, EEPopUpForeColor, EEPopUpBackColor);
		/* Old Version of the program generated the data file */
		/* send the data file through the convertor */
		fclose(f);	/* close the file */
		sprintf(Line,"conv %s",Window -> FileName);
		system(Line); 
    		if ((f = fopen(Window -> FileName, "rt")) == NULL) {
			sprintf(Line, "Failed to open \"%s\"", 
					Window -> FileName);
			IntrQueryContinue(Line, 
					EEPopUpFrameColor, EEPopUpBackColor,
			  		EEPopUpForeColor, EEPopUpXorColor,
			  		EEWindowsFrameWidth, &Cursor,
                          		INTR_WNDW_PLACE_CENTER);
		
			IntrEraseMessage();
			return FALSE;
    		}
		
    		if (fgets(Line, LINE_LEN - 1, f) == NULL ||
			strncmp(Line, EEDRAW_FILE_STAMP,
					sizeof(EEDRAW_FILE_STAMP) - 1) != 0) {
			sprintf(Line, "\"%s\" is NOT EEDraw file",
					Window -> FileName);
			IntrQueryContinue(Line,
					EEPopUpFrameColor, EEPopUpBackColor,
        		  		EEPopUpForeColor, EEPopUpXorColor,
			  		EEWindowsFrameWidth, &Cursor,
                          		INTR_WNDW_PLACE_CENTER);
			fclose(f);
			IntrEraseMessage();
			return FALSE;
    		}
		IntrEraseMessage();
    }

    if (fgets(Line, LINE_LEN - 1, f) == NULL ||
	strncmp(Line, "LIBS:", 5) != 0) {
	sprintf(Line, "\"%s\" is NOT EEDraw file", Window -> FileName);
	IntrQueryContinue(Line, EEPopUpFrameColor, EEPopUpBackColor,
        		  EEPopUpForeColor, EEPopUpXorColor,
			  EEWindowsFrameWidth, &Cursor,
			  INTR_WNDW_PLACE_CENTER);
	fclose(f);
	return FALSE;
    }
    else
	LoadLibraries(&Line[5]);
	TmpWindow = EEActiveWindow;
	EEActiveWindow = Window;
	LoadLayers(f);
	LineCount = ReturnLayerNumber() +2 + LineCount;
	EEActiveWindow = TmpWindow;

    while (!feof(f) &&
	   fgets(Line, LINE_LEN - 1, f) != NULL) {
	LineCount++;

	switch(Line[0]) {
	    case 'L':				      /* Its a library item. */
		LibItemStruct = (DrawLibItemStruct *)
					 MyMalloc(sizeof(DrawLibItemStruct));
		LibItemStruct -> StructType = DRAW_LIB_ITEM_STRUCT_TYPE;
		if (sscanf(&Line[1], "%s %s %s %s %d %d %d %d",
			   Name1, Name2,
			   Char1, Char2,
			   &LibItemStruct -> ChipNameX,
			   &LibItemStruct -> ChipNameY,
			   &LibItemStruct -> PartNameX,
			   &LibItemStruct -> PartNameY) != 8 ||
		    (Char1[0] != 'V' && Char1[0] != 'H') ||
		    (Char2[0] != 'V' && Char2[0] != 'H')) {
		    sprintf(Line,
			"EEDraw file lib item struct error at line %d, aborted",
								LineCount);
		    Failed = TRUE;
		    break;
		}
		LibItemStruct -> ChipNameOrient =
		    Char1[0] == 'V' ? TEXT_ORIENT_VERT : TEXT_ORIENT_HORIZ;
		LibItemStruct -> PartNameOrient =
		    Char2[0] == 'V' ? TEXT_ORIENT_VERT : TEXT_ORIENT_HORIZ;

		if (strcmp(Name1, NULL_STRING) != 0) {
		    for (i = 0; i < strlen(Name1); i++)
			if (Name1[i] == '~') Name1[i] = ' ';
		    LibItemStruct -> ChipName = strdup(Name1);
		}
		else {
		    LibItemStruct -> ChipName = NULL;
		    LibItemStruct -> ChipNameOrient = TEXT_ORIENT_NON;
		}

		if (strcmp(Name2, NULL_STRING) != 0) {
		    for (i = 0; i < strlen(Name2); i++)
			if (Name2[i] == '~') Name2[i] = ' ';
		    LibItemStruct -> PartName = strdup(Name2);
		}
		else {
		    LibItemStruct -> PartName = NULL;
		    LibItemStruct -> PartNameOrient = TEXT_ORIENT_NON;
		}

		LineCount++;
		if (!Failed &&
		    (fgets(Line, LINE_LEN - 1, f) == NULL ||
		     sscanf(Line, "%d %d %d %d %d %d %d",
			    &LibItemStruct -> Multi,
			    &LibItemStruct -> PosX,
			    &LibItemStruct -> PosY,
			    &LibItemStruct -> BBoxMinX,
			    &LibItemStruct -> BBoxMinY,
			    &LibItemStruct -> BBoxMaxX,
			    &LibItemStruct -> BBoxMaxY) != 7)) {
		    sprintf(Line,
			"EEDraw file lib item struct error at line %d, aborted",
								LineCount);
		    Failed = TRUE;
		    break;
		}

		LineCount++;
		if (!Failed &&
		    (fgets(Line, LINE_LEN - 1, f) == NULL ||
		     sscanf(Line, "%d %d %d %d",
			    &LibItemStruct -> Transform[0][0],
			    &LibItemStruct -> Transform[0][1],
			    &LibItemStruct -> Transform[1][0],
			    &LibItemStruct -> Transform[1][1]) != 4)) {
		    sprintf(Line,
			"EEDraw file lib item struct error at line %d, aborted",
								LineCount);
		    Failed = TRUE;
		    break;
		}

		if (!Failed) {
		    LibItemStruct -> Pnext = Window -> EEDrawList;
		    Window -> EEDrawList = (DrawGenericStruct *)
					                        LibItemStruct;
		}
		break;
	    case 'P':				     /* Its a polyline item. */
		PolylineStruct = (DrawPolylineStruct *)
					MyMalloc(sizeof(DrawPolylineStruct));
		PolylineStruct -> StructType = DRAW_POLYLINE_STRUCT_TYPE;
		if (sscanf(&Line[1], "%s %d %d",
			   Name1, &PolylineStruct -> Layer,
			   &PolylineStruct -> NumOfPoints) != 3 ||
		    (Name1[0] != 'B' && Name1[0] != 'L')) {
		    sprintf(Line,
			"EEDraw file polyline struct error at line %d, aborted",
								LineCount);
		    Failed = TRUE;
		    break;
		}
		PolylineStruct -> Width =
		    Name1[0] == 'B' ? GR_THICK_WIDTH : GR_NORM_WIDTH;
		PolylineStruct -> Points = (int *) MyMalloc(sizeof(int) * 2 *
						PolylineStruct -> NumOfPoints);
		for (i = 0; i < PolylineStruct -> NumOfPoints; i++) {
		    LineCount++;
		    if (fgets(Line, LINE_LEN - 1, f) == NULL ||
			sscanf(Line, "%d %d", &PolylineStruct -> Points[i*2],
					      &PolylineStruct -> Points[i*2+1])
								!= 2) {
			sprintf(Line,
			 "EEDraw file polyline struct error at line %d, aborted",
								LineCount);
			Failed = TRUE;
			MyFree((VoidPtr) PolylineStruct -> Points);
			break;
		    }
		}

		if (!Failed) {
		    PolylineStruct -> Pnext = Window -> EEDrawList;
		    Window -> EEDrawList = (DrawGenericStruct *)
							       PolylineStruct;
		}
		break;
	    case 'C':				   /* Its a connection item. */
		ConnectionStruct = (DrawConnectionStruct *)
				      MyMalloc(sizeof(DrawConnectionStruct));
		ConnectionStruct -> StructType = DRAW_CONNECTION_STRUCT_TYPE;
		if (sscanf(&Line[1], "%d %d %d",
			   &ConnectionStruct -> Layer,
			   &ConnectionStruct -> PosX,
			   &ConnectionStruct -> PosY) != 3) {
		    sprintf(Line,
		      "EEDraw file connection struct error at line %d, aborted",
								LineCount);
		    Failed = TRUE;
		}
		else {
		    ConnectionStruct -> Pnext = Window -> EEDrawList;
		    Window -> EEDrawList = (DrawGenericStruct *)
							     ConnectionStruct;
		}
		break;
	    case 'T':				   	  /* Its a text item. */
		TextStruct = (DrawTextStruct *)
					    MyMalloc(sizeof(DrawTextStruct));
		TextStruct -> StructType = DRAW_TEXT_STRUCT_TYPE;
		    TextStruct -> Scale = 1;
		if ((((i = sscanf(&Line[1], "%d %d %d %d %d",
				  &TextStruct -> Layer,
				  &TextStruct -> PosX,
				  &TextStruct -> PosY,
				  &TextStruct -> Orient,
				  &TextStruct -> Scale)) != 4) && i != 5) ||
		    fgets(Line, LINE_LEN - 1, f) == NULL) {
		    sprintf(Line,
		      "EEDraw file text struct error at line %d, aborted",
								LineCount);
		    Failed = TRUE;
		}
		else {

		    TextStruct -> Text = strdup(strtok(Line, "\n\r"));
		    TextStruct -> Pnext = Window -> EEDrawList;
		    Window -> EEDrawList = (DrawGenericStruct *) TextStruct;
		}
		if (i == 3) TextStruct -> Scale = 1;  /* No scale specified. */
		break;
	    default:
		Failed = FALSE;
		sprintf(Line, "EEDraw file undef structdef at line %d, aborted",
								LineCount);
		break;
	}

	if (Failed) {
	    IntrQueryContinue(Line, EEPopUpFrameColor, EEPopUpBackColor,
            		      EEPopUpForeColor, EEPopUpXorColor,
			      EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    break;
	}
    }

    /* EEDrawList was constructed in reverse order - reverse it back: */
    Phead = NULL;
    while (Window -> EEDrawList) {
	Pnext = Window -> EEDrawList;
	Window -> EEDrawList = Window -> EEDrawList -> Pnext;
	Pnext -> Pnext = Phead;
	Phead = Pnext;
    }
    Window -> EEDrawList = Phead;

    fclose(f);

    return TRUE;   /* Although it may be that file is only partially loaded. */
}

/*****************************************************************************
* Routine to save an EEDraw file.					     *
* FileSave controls how the file is to be saved - under what name.	     *
* Returns TRUE if the file has been saved.				     *
*****************************************************************************/
BooleanType SaveEEFile(FileSaveType FileSave, EEWindowStruct *Window)
{
    char *p, Line[LINE_LEN], Name[LINE_LEN],
	Name1[LINE_LEN_SHORT], Name2[LINE_LEN_SHORT], **LibNames;
    FileNameType *FileNames;
    int i, NumOfFiles;
    BooleanType
	Failed = FALSE;
    DrawGenericStruct
	*Phead = Window -> EEDrawList;
    DrawLibItemStruct *LibItemStruct;
    DrawConnectionStruct *ConnectionStruct;
    DrawPolylineStruct *PolylineStruct;
    DrawTextStruct *TextStruct;
    FILE *f;

    /* If no name exists in the window yet - save as new. */
    if (strlen(Window -> FileName) == 0) FileSave = FILE_SAVE_NEW;

    Cursor.CursorType = INTR_CURSOR_ARROW;
    switch (FileSave) {
	case FILE_SAVE_AS:
	    sprintf(Line, "Save as \"%s\"?", Window -> FileName);
	    if (!IntrQueryYesNo(Line, EEPopUpFrameColor, EEPopUpBackColor,
            			EEPopUpForeColor, EEPopUpXorColor,
				EEWindowsFrameWidth, &Cursor,
                                INTR_WNDW_PLACE_CENTER)) return FALSE;
	    strcpy(Line, Window -> FileName);

	    strcpy(Name, Line);	     /* Rename the old file to a '.bak' one. */
	    if ((p = strrchr(Name, '.')) != NULL && strlen(p) <= 4) p[0] = 0;
	    strcat(Name, ".bak");
	    unlink(Name);
	    rename(Line, Name);
	    break;
	case FILE_SAVE_NEW:
	    strcpy(Line, FILE_EXTENSION);
	    IntrQueryLine("EEDraw file to save to:", Line, LINE_LEN - 1,
			  EEPopUpFrameColor, EEPopUpBackColor,
			  EEPopUpForeColor, EEWindowsFrameWidth,
                          INTR_WNDW_PLACE_CENTER);
	    /* And make sure it has the right extension type. */
	    if ((p = strrchr(Line, '.')) != NULL && strlen(p) <= 4) p[0] = 0;
	    strcat(Line, FILE_EXTENSION);
	    break;
	case FILE_SAVE_OLD:
	    sprintf(Line, "*%s", FILE_EXTENSION);
	    if ((FileNames = GetFileNamesDir(Line, ".", &NumOfFiles)) != NULL) {
		qsort(FileNames, NumOfFiles, sizeof(FileNameType),
		      (int (*)(const void *, const void *)) strcmp);
		if ((i = IntrQueryList("EED files", (char **) FileNames,
			 sizeof(FileNameType), NumOfFiles, EEListNumDisplayed,
			 EEPopUpFrameColor, EEPopUpBackColor, EEPopUpForeColor,
			 EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
			 INTR_WNDW_PLACE_CENTER)) < 0) {
		    MyFree((VoidPtr) FileNames);
		    return FALSE;
		}
		strcpy(Line, FileNames[i]);
		MyFree((VoidPtr) FileNames);
	    }
	    else {
		IntrQueryContinue("No EEDraw files found.",
                		  EEPopUpFrameColor, EEPopUpBackColor,
				  EEPopUpForeColor, EEPopUpXorColor,
				  EEWindowsFrameWidth, &Cursor,
                                  INTR_WNDW_PLACE_CENTER);
		return FALSE;
	    }

	    sprintf(Name, "Save as \"%s\"?", Line);
	    if (!IntrQueryYesNo(Name, EEPopUpFrameColor, EEPopUpBackColor,
            			EEPopUpForeColor, EEPopUpXorColor,
				EEWindowsFrameWidth, &Cursor,
                                INTR_WNDW_PLACE_CENTER)) return FALSE;

	    strcpy(Name, Line);	     /* Rename the old file to a '.bak' one. */
	    if ((p = strrchr(Name, '.')) != NULL && strlen(p) <= 4) p[0] = 0;
	    strcat(Name, ".bak");
	    unlink(Name);
	    rename(Line, Name);
	    break;
    }

    if ((f = fopen(Line, "wt")) == NULL) {
	IntrQueryContinue("Failed to open file",
			  EEPopUpFrameColor, EEPopUpBackColor,
			  EEPopUpForeColor, EEPopUpXorColor,
			  EEWindowsFrameWidth, &Cursor,
			  INTR_WNDW_PLACE_CENTER);
	return FALSE;
    }

    LibNames = GetLibNames();
    for (i = 0, Name[0] = 0; LibNames[i] != NULL; i++) {
	if (i > 0) strcat(Name, ",");
	strcat(Name, LibNames[i]);
    }
    MyFree((VoidPtr) LibNames);

    if (fprintf(f, "%s EEDRAW PROGRAM %s\n",
		EEDRAW_FILE_STAMP, EEDRAW_VERSION) == EOF ||
	fprintf(f, "LIBS:%s\n", Name) == EOF) {
	IntrQueryContinue("File write operation failed.",
			  EEPopUpFrameColor, EEPopUpBackColor,
			  EEPopUpForeColor, EEPopUpXorColor,
			  EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	fclose(f);
	return FALSE;
    }

	SaveLayers(f);

    while (Phead) {
	switch(Phead -> StructType) {
	    case DRAW_LIB_ITEM_STRUCT_TYPE:	      /* Its a library item. */
		LibItemStruct = (DrawLibItemStruct *) Phead;

		if (LibItemStruct -> PartName != NULL) {
		    strcpy(Name1, LibItemStruct -> PartName);
		    for (i = 0; i < strlen(Name1); i++)
			if (Name1[i] <= ' ') Name1[i] = '~';
		}
		else
		    strcpy(Name1, NULL_STRING);
		if (LibItemStruct -> ChipName != NULL) {
		    strcpy(Name2, LibItemStruct -> ChipName);
		    for (i = 0; i < strlen(Name2); i++)
			if (Name2[i] <= ' ') Name2[i] = '~';
		}
		else
		    strcpy(Name2, NULL_STRING);

		if (fprintf(f, "L %-10s %-10s %c %c %-3d %-3d %-3d %-3d\n",
		    Name2, Name1,
		    LibItemStruct -> ChipNameOrient == TEXT_ORIENT_VERT ? 'V' : 'H',
		    LibItemStruct -> PartNameOrient == TEXT_ORIENT_VERT ? 'V' : 'H',
		    LibItemStruct -> ChipNameX,
		    LibItemStruct -> ChipNameY,
		    LibItemStruct -> PartNameX,
		    LibItemStruct -> PartNameY) == EOF) {
		    Failed = TRUE;
		    break;
		}

		if (!Failed &&
		    fprintf(f, "\t%-4d %-4d %-4d %-4d %-4d %-4d %-4d\n",
			    LibItemStruct -> Multi,
			    LibItemStruct -> PosX,
			    LibItemStruct -> PosY,
			    LibItemStruct -> BBoxMinX,
			    LibItemStruct -> BBoxMinY,
			    LibItemStruct -> BBoxMaxX,
			    LibItemStruct -> BBoxMaxY) == EOF) {
		    Failed = TRUE;
		    break;
		}

		if (!Failed &&
		    fprintf(f, "\t%-4d %-4d %-4d %-4d\n",
			    LibItemStruct -> Transform[0][0],
			    LibItemStruct -> Transform[0][1],
			    LibItemStruct -> Transform[1][0],
			    LibItemStruct -> Transform[1][1]) == EOF) {
		    Failed = TRUE;
		    break;
		}
		break;
	    case DRAW_POLYLINE_STRUCT_TYPE:	     /* Its a polyline item. */
		PolylineStruct = (DrawPolylineStruct *) Phead;
		if (fprintf(f, "P %c %2d %d\n",
			PolylineStruct -> Width == GR_NORM_WIDTH ? 'L' : 'B',
			PolylineStruct -> Layer,
			PolylineStruct -> NumOfPoints) == EOF) {
		    Failed = TRUE;
		    break;
		}
		for (i = 0; i < PolylineStruct -> NumOfPoints; i++) {
		    if (fprintf(f, "\t%-4d %-4d\n",
			PolylineStruct -> Points[i*2],
			PolylineStruct -> Points[i*2+1]) == EOF) {
			Failed = TRUE;
			break;
		    }
		}
		break;
	    case DRAW_CONNECTION_STRUCT_TYPE:	   /* Its a connection item. */
		ConnectionStruct = (DrawConnectionStruct *) Phead;
		if (fprintf(f, "C %2d %-4d %-4d\n",
			ConnectionStruct -> Layer,
			ConnectionStruct -> PosX,
			ConnectionStruct -> PosY) == EOF) {
		    Failed = TRUE;
		}
		break;
	    case DRAW_TEXT_STRUCT_TYPE:			 /* Its a text item. */
		TextStruct = (DrawTextStruct *) Phead;
		if (TextStruct -> Scale > 1) {
		    if (fprintf(f, "T %2d %-4d %-4d %-4d %-4d\n%s\n",
				TextStruct -> Layer,
				TextStruct -> PosX,
				TextStruct -> PosY,
				TextStruct -> Orient,
				TextStruct -> Scale,
				TextStruct -> Text) == EOF)
			Failed = TRUE;
		}
		else {
		    if (fprintf(f, "T %2d %-4d %-4d %-4d\n%s\n",
				TextStruct -> Layer,
				TextStruct -> PosX,
				TextStruct -> PosY,
				TextStruct -> Orient,
				TextStruct -> Text) == EOF)
			Failed = TRUE;
		}
		break;
	}

	if (Failed) {
	    IntrQueryContinue("File write operation failed.",
			      EEPopUpFrameColor, EEPopUpBackColor,
			      EEPopUpForeColor, EEPopUpXorColor,
			      EEWindowsFrameWidth, &Cursor,
			      INTR_WNDW_PLACE_CENTER);
	    break;
	}

	Phead = Phead -> Pnext;
    }
    fclose(f);

    if (FileSave == FILE_SAVE_NEW)
	strcpy(Window -> FileName, Line);

    return !Failed;
}

/*****************************************************************************
* Routine to save a net list of the given EEDraw window (file).		     *
*****************************************************************************/
void SaveNetList(EEWindowStruct *Window)
{
    FILE *f;
    int NetNumber;
    int PercentDone, i, j,
	NumOfPolylines = 0;
    BooleanType IsABus;
    char **StrVec, *Str, Line[LINE_LEN], SLine[LINE_LEN_SHORT];
    DrawGenericStruct *p,
	*Phead = Window -> EEDrawList;
    NetListStruct *NetList, *NetListHead;
    DrawPolylineStruct *PolylineStruct;

    /* Seed the Net Number */
    NetNumber=1;
    /* Make sure this window is on top since we are going to draw to it. */
    IntrWndwPop(Window -> IntrLibWindowID, TRUE, FALSE);

    strcpy(Line, Window -> FileName);
    if ((Str = strrchr(Line, '.')) != NULL && strlen(Str) < 5) Str[0] = 0;
    strcat(Line, NETLIST_FILE_EXTENSION);

    IntrQueryLine("File name to save NET LIST:", Line, LINE_LEN - 1,
		  EEPopUpFrameColor, EEPopUpBackColor, EEPopUpForeColor,
		  EEWindowsFrameWidth, INTR_WNDW_PLACE_CENTER);

    if ((f = fopen(Line, "wt")) == NULL) {
	IntrQueryContinue("Failed to open file",
			  EEPopUpFrameColor, EEPopUpBackColor,
			  EEPopUpForeColor, EEPopUpXorColor,
			  EEWindowsFrameWidth, &Cursor,
			  INTR_WNDW_PLACE_CENTER);
	return;
    }

    /* Mark all polylines as untested at first path. */
    for (p = Phead; p != NULL; p = p -> Pnext) {
	switch(p -> StructType) {
	    case DRAW_POLYLINE_STRUCT_TYPE:	     /* Its a polyline item. */
		NumOfPolylines++;
		PolylineStruct = (DrawPolylineStruct *) p;
		PolylineStruct -> Flags = FALSE;
		break;
	}
    }

    GlblNumOfPolylines = NumOfPolylines;

    sprintf(SLine, "Net List - %d%% done.", 0);
    IntrDrawMessage(SLine, EEPopUpForeColor, EEPopUpBackColor);
    PercentDone = 0;

    /* Second path - scan for all polylines and for each untested polyline   */
    /* get its net list and dump it out to the open file.		     */
    for (p = Phead; p != NULL; p = p -> Pnext) {
	switch(p -> StructType) {
	    case DRAW_POLYLINE_STRUCT_TYPE:	     /* Its a polyline item. */
		i = (100 * (NumOfPolylines - GlblNumOfPolylines)) /
							      NumOfPolylines;
		if (i != PercentDone) {
		    IntrEraseMessage();
		    sprintf(SLine, "Net List - %d%% done.", i);
		    PercentDone = i;
		    IntrDrawMessage(SLine, EEPopUpForeColor, EEPopUpBackColor);
		}

		PolylineStruct = (DrawPolylineStruct *) p;
		if (!PolylineStruct -> Flags) {
		    /* We have not visited this polyline - do it now. */
		    IsABus = FALSE;
		    NetListHead = NULL;
		    GetPolylineNetList(PolylineStruct, Phead, &NetListHead,
								    &IsABus);

		    /* Print the net list into the file. */
		    if (NetListHead != NULL) {
			for (NetList = NetListHead, i = 0;
			     NetList != NULL;
			     NetList = NetList -> Pnext, i++);
			if (i > 0) {
			    /* Sort entries in lexicographic order. */
			    StrVec = (char **) MyMalloc(sizeof(char *) * i);
			    for (NetList = NetListHead, j = 0;
				 NetList != NULL;
				 NetList = NetList -> Pnext, j++)
				StrVec[j] = NetList -> Pin;
			    qsort(StrVec, i, sizeof(char *), StringComp);

			    /* Print the sorted list. */
			    fprintf(f, "\nConnection:	N%06d\n",NetNumber++);
			    for (j = 0; j < i; j++)
				fprintf(f, "\t%s\n", StrVec[j]);
			    fprintf(f, "End Connection%s.\n",
				    IsABus ? " (BUS)" : "");
			    MyFree(StrVec);
			}

			/* And delete the memory. */
			while (NetListHead != NULL) {
			    NetList = NetListHead -> Pnext;
			    MyFree(NetListHead);
			    NetListHead = NetList;
			}
		    }
		}
		break;
	}
    }
    IntrEraseMessage();

    fclose(f);
}

/*****************************************************************************
* Simple wrapper for strcmp so qsort above can call it on char ** type.      *
*****************************************************************************/
static int StringComp(const void *Str1, const void *Str2)
{
    return strcmp(*((char **) Str1), *((char **) Str2));
}

/*****************************************************************************
* Routine to search and find all library items intersecting with this line   *
* since other lines may intersect with it other lines are scaned as well.    *
*****************************************************************************/
static void GetPolylineNetList(DrawPolylineStruct *PolylineStruct,
			       DrawGenericStruct *Phead,
			       NetListStruct **NetListHead,
			       BooleanType *IsABus)
{
    int i, j, *OPoints, ONumPts, l1x1, l1y1, l1x2, l1y2,
	*Points = PolylineStruct -> Points,
	NumPts = PolylineStruct -> NumOfPoints;
    BooleanType LinesInter;
    DrawGenericStruct *p;
    DrawPolylineStruct *OPolylineStruct;

    PolylineStruct -> Flags = TRUE;           /* We visited this polyline... */
    RedrawOneStruct((DrawGenericStruct *) PolylineStruct,
		    GR_COPY_PUT, EE_HIGHLIGHT_COLOR);
    GRSetLineStyle(GR_SOLID_LINE, 0, GR_NORM_WIDTH);

    /* Generate the netlist for this polyline */
    GetOnePolylineNetList(PolylineStruct, Phead, NetListHead, IsABus);

    /* And scan for other polylines intersecting this one: */
    for (p = Phead; p != NULL; p = p -> Pnext) {
	switch(p -> StructType) {
	    case DRAW_POLYLINE_STRUCT_TYPE:	     /* Its a polyline item. */
		OPolylineStruct = (DrawPolylineStruct *) p;
		if (!OPolylineStruct -> Flags) {
		    /* Found an untested polyline - do the intersect? */
		    OPoints = OPolylineStruct -> Points;
		    ONumPts = OPolylineStruct -> NumOfPoints;
		    LinesInter = FALSE;
		    for (i = 0; i < NumPts - 1 && !LinesInter; i++) {
			l1x1 = Points[i * 2];
			l1y1 = Points[i * 2 + 1];
			l1x2 = Points[i * 2 + 2];
			l1y2 = Points[i * 2 + 3];

			for (j = 0; j < ONumPts - 1 && !LinesInter; j++) {
			    LinesInter = LinesIntersect(
				l1x1, l1y1,
				l1x2, l1y2,
				OPoints[j * 2], OPoints[j * 2 + 1],
				OPoints[j * 2 + 2], OPoints[j * 2 + 3],
				PolylineStruct -> Width == GR_THICK_WIDTH,
				OPolylineStruct -> Width == GR_THICK_WIDTH,
				Phead);
			}
		    }

		    if (LinesInter)
			GetPolylineNetList(OPolylineStruct, Phead,
					   NetListHead, IsABus);
		}
		break;
	}
    }
}

/*****************************************************************************
* Routine to search and find all library items intersecting with this line.  *
*****************************************************************************/
static void GetOnePolylineNetList(DrawPolylineStruct *PolylineStruct,
				  DrawGenericStruct *Phead,
				  NetListStruct **NetListHead,
				  BooleanType *IsABus)
{
    int i,
	*Points = PolylineStruct -> Points,
	NumPts = PolylineStruct -> NumOfPoints;
    BooleanType
	PolyIsABus = PolylineStruct -> Width == GR_THICK_WIDTH;
    NetListStruct *NewNetList, *NetListItem, *NetListNext;
    DrawLibItemStruct *LibItemStruct;
    DrawGenericStruct *p;

    if (PolyIsABus) *IsABus = TRUE;

    for (p = Phead; p != NULL; p = p -> Pnext) {
	switch(p -> StructType) {
	    case DRAW_LIB_ITEM_STRUCT_TYPE:	      /* Its a library item. */
		LibItemStruct = (DrawLibItemStruct *) p;
		for (i = 0; i < NumPts - 1; i++) {
		    NewNetList = FindLibItemNetList(
				Points[i * 2], Points[i * 2 + 1],
				Points[i * 2 + 2], Points[i * 2 + 3],
				PolyIsABus, Phead, LibItemStruct);
		    while (NewNetList) {
			/* Linear search old pins to see if we have it: */
			for (NetListItem = *NetListHead;
			     NetListItem != NULL;
			     NetListItem = NetListItem -> Pnext)
			    if (strcmp(NetListItem -> Pin,
				       NewNetList -> Pin) == 0) break;

			NetListNext = NewNetList -> Pnext;
			if (NetListItem != NULL) {
			    /* We already have it - delete the new one. */
			    MyFree(NewNetList);
			}
			else {
			    /* Its new - add the net list result list. */
			    NewNetList -> Pnext = *NetListHead;
			    *NetListHead = NewNetList;
			}
			NewNetList = NetListNext;
		    }
		}
	}
    }

    GlblNumOfPolylines--;
}

/*****************************************************************************
* Returns TRUE iff l1 intersects l2. Both line segments are given by thier   *
* end points and assumed to be horizontal or vertical only.		     *
*****************************************************************************/
BooleanType LinesIntersect(int l1x1, int l1y1, int l1x2, int l1y2,
			   int l2x1, int l2y1, int l2x2, int l2y2,
			   BooleanType l1Bus, BooleanType l2Bus,
			   DrawGenericStruct *Phead)
{
    int l1xmin, l1xmax, l1ymin, l1ymax, l2xmin, l2xmax, l2ymin, l2ymax;

    /* Eliminate the common case of intersecting at the end points. */
    if ((l1x1 == l2x1 && l1y1 == l2y1) ||
	(l1x1 == l2x2 && l1y1 == l2y2) ||
	(l1x2 == l2x1 && l1y2 == l2y1) ||
	(l1x2 == l2x2 && l1y2 == l2y2)) return TRUE;

    if (l1x1 == l1x2) {			             /* Line l1 is vertical. */
	l1ymin = MIN(l1y1, l1y2);
	l1ymax = MAX(l1y1, l1y2);

	if (l2x1 == l2x2) {			     /* Line l2 is vertical. */
	    if (l1x1 != l2x1) return FALSE;   /* Both vertical with diff. x. */

	    l2ymin = MIN(l2y1, l2y2);
	    l2ymax = MAX(l2y1, l2y2);

	    return MIN(l1ymax, l2ymax) >= MAX(l1ymin, l2ymin);
	}
	else {					   /* Line l2 is horizontal. */
	    l2xmin = MIN(l2x1, l2x2);
	    l2xmax = MAX(l2x1, l2x2);

	    if ((l2y1 >= l1ymin && l2y1 <= l1ymax &&
		 (l1x1 == l2x1 || l1x1 == l2x2)) ||
		(l2xmin <= l1x1 && l2xmax >= l1x1 &&
		 (l1y1 == l2y1 || l1y2 == l2y1))) {
		/* One line ends on the other middle. Returns TRUE if one of */
		/* the lines is a BUS or a connection exists at that point.  */
		return l1Bus || l2Bus ||
		       ConnectionExistsAtXY(Phead, l1x1, l2y1);
	    }
	    else if (l2xmin <= l1x1 && l2xmax >= l1x1 &&
		     l2y1 >= l1ymin && l2y1 <= l1ymax) {
		/* They intersect. Test if exists a connection at that point */
		/* and return TRUE iff such connection exist.		     */
		/* Also accept it as intersection if one of lines is a bus.  */
		return ConnectionExistsAtXY(Phead, l1x1, l2y1);
	    }
	    else
		return FALSE;
	}
    }
    else {				           /* Line l1 is horizontal. */
	l1xmin = MIN(l1x1, l1x2);
	l1xmax = MAX(l1x1, l1x2);

	if (l2x1 == l2x2) {			     /* Line l2 is vertical. */
	    l2ymin = MIN(l2y1, l2y2);
	    l2ymax = MAX(l2y1, l2y2);

	    if ((l2x1 >= l1xmin && l2x1 <= l1xmax &&
		 (l1y1 == l2y1 || l1y1 == l2y2)) ||
		(l2ymin <= l1y1 && l2ymax >= l1y1 &&
		 (l1x1 == l2x1 || l1x2 == l2x1))) {
		/* One line ends on the other middle. Returns TRUE if one of */
		/* the lines is a BUS or a connection exists at that point.  */
		return l1Bus || l2Bus ||
		       ConnectionExistsAtXY(Phead, l2x1, l1y1);
	    }
	    else if (l2ymin <= l1y1 && l2ymax >= l1y1 &&
		     l2x1 >= l1xmin && l2x1 <= l1xmax) {
		/* They intersect. Test if exists a connection at that point */
		/* and return TRUE iff such connection exist.		     */
		/* Also accept it as intersection if one of lines is a bus.  */
		return ConnectionExistsAtXY(Phead, l2x1, l1y1);
	    }
	    else
		return FALSE;
	}
	else {					   /* Line l2 is horizontal. */
	    if (l1y1 != l2y1) return FALSE; /* Both horizontal with diff. y. */

	    l2xmin = MIN(l2x1, l2x2);
	    l2xmax = MAX(l2x1, l2x2);

	    return MIN(l1xmax, l2xmax) >= MAX(l1xmin, l2xmin);
	}
    }
}

/*****************************************************************************
* Scans the given drawing object list for connection position at X Y and     *
* returns TRUE iff such connection is found.				     *
*****************************************************************************/
static BooleanType ConnectionExistsAtXY(DrawGenericStruct *Phead, int x, int y)
{
    DrawConnectionStruct *ConnectionStruct;

    while (Phead) {
	switch(Phead -> StructType) {
	    case DRAW_CONNECTION_STRUCT_TYPE:	   /* Its a connection item. */
		ConnectionStruct = (DrawConnectionStruct *) Phead;
		if (ABS(x - ConnectionStruct -> PosX) < EESnapDistance &&
		    ABS(y - ConnectionStruct -> PosY) < EESnapDistance)
		    return TRUE;
		break;
	}

	Phead = Phead -> Pnext;
    }

    return FALSE;
}

