/*****************************************************************************
*   Module to handle libraries (second part - drawing and interaction).	     *
*									     *
* Written by:  Gershon Elber			IBM PC Ver 1.0,	Oct. 1989    *
*****************************************************************************/

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#ifdef __MSDOS__
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include <alloc.h>
#endif /* __MSDOS__ */

#include "program.h"
#include "priorque.h"
#include "eelibs.h"
#include "eelibsl.h"
#include "EELayer.h"
#include "eeredraw.h"
#include "eestring.h"
#include "igraph.h"
#include "primary.h"

enum {
    TMENU_NONE = 10,
    TMENU_ROTATE_R,
    TMENU_ROTATE_L,
    TMENU_MIRROR_X,
    TMENU_MIRROR_Y
};

static void DrawLibPartAux(LibraryEntryStruct *Entry, int PartX, int PartY,
						int TransMat[2][2], int Multi);
static char *LookUpPinText(char *Pins, int PinNum);
static void MapAngles(int *Angle1, int *Angle2, int TransMat[2][2]);
static LibraryEntryStruct *FindLibPart(char *Name);

/*****************************************************************************
* Routine to draw the given part at given position, transformed/mirror as    *
* specified, and in the given drawing mode. Only this one is visible...	     *
*****************************************************************************/
void DrawLibPart(DrawLibItemStruct *DrawLibItem)
{
    LibraryEntryStruct *Entry;
    char Line[LINE_LEN];

    if ((Entry = FindLibPart(DrawLibItem -> ChipName)) == NULL) {
	sprintf(Line, "Failed to find part \"%s\" in library",
						DrawLibItem -> ChipName);
	FatalError(Line);
    }

    DrawLibPartAux(Entry,
		   DrawLibItem -> PosX,
		   DrawLibItem -> PosY,
		   DrawLibItem -> Transform,
		   DrawLibItem -> Multi);

    if (Entry -> DrawName &&
	DrawLibItem -> ChipNameOrient != TEXT_ORIENT_NON)
	PutTextInfo(DrawLibItem -> ChipNameOrient,
		    DrawLibItem -> ChipNameX,
		    DrawLibItem -> ChipNameY,
		    1,
		    DrawLibItem -> ChipName);
    if (DrawLibItem -> PartNameOrient != TEXT_ORIENT_NON)
	PutTextInfo(DrawLibItem -> PartNameOrient,
		    DrawLibItem -> PartNameX,
		    DrawLibItem -> PartNameY,
		    1,
		    DrawLibItem -> PartName);
}

/*****************************************************************************
* Routine to find a part in one of the libraries given its name.	     *
*****************************************************************************/
static LibraryEntryStruct *FindLibPart(char *Name)
{
    LibraryEntryStruct *Entry, DummyEntry;
    LibraryStruct *Lib = LibraryList;

    DummyEntry.Pins = NULL;			/* Used only to call PQFind. */
    DummyEntry.Drawings = NULL;
    DummyEntry.Multi = NULL;
    strcpy(DummyEntry.Name, Name);

    PQCompFunc((PQCompFuncType) LibraryEntryCompare);

    while (Lib) {
	if ((Entry = (LibraryEntryStruct *) PQFind(Lib -> Entries,
						   (VoidPtr) &DummyEntry))
	    != NULL)
	    return Entry;

	Lib = Lib -> Pnext;
    }
    return NULL;
}

/*****************************************************************************
* Routine to draw the given part at given position, transformed/mirror as    *
* specified, and in the given drawing mode.				     *
*****************************************************************************/
static void DrawLibPartAux(LibraryEntryStruct *Entry, int PartX, int PartY,
						int TransMat[2][2], int Multi)
{
    int i, x1, y1, x2, y2, IsMulti, *NextPinMulti, NextPinNum, t1, t2,
	y, MapX1, MapY1, MapX2, MapY2, PinWidth, *Poly;
    char Line[LINE_LEN], *NextPinText;
    LibraryDrawEntryStruct *DEntry;

    IsMulti = Entry -> NumOfUnits > 0;
    if (IsMulti) {
	/* Make NextMulti point on next pin number in multi array: */
	NextPinMulti = &Entry -> Multi[Entry -> PinsPerUnit * --Multi];

	NextPinText = NULL;
    }
    else {
	NextPinNum = 1;
	NextPinMulti = NULL;

	/* Make a copy of all pin text info so we can use strtok routine on: */
	strncpy(Line, Entry -> Pins, LINE_LEN - 1);
	NextPinText = strtok(Line, PIN_SEPERATOR);
    }

    if (Entry -> Drawings != NULL) {
	DEntry = Entry -> Drawings;
	while (DEntry != NULL) {
	    switch (DEntry -> DrawType) {
		case ARC_DRAW_TYPE:
    			if(!ReturnLayerMode(DEntry->Layer))
				break;
		    t1 = DEntry -> U.Arc.t1;
		    t2 = DEntry -> U.Arc.t2;
		    MapAngles(&t1, &t2, TransMat);
		    IGArc(PartX + TransMat[0][0] * DEntry -> U.Arc.x +
				  TransMat[0][1] * DEntry -> U.Arc.y,
			  PartY + TransMat[1][0] * DEntry -> U.Arc.x +
				  TransMat[1][1] * DEntry -> U.Arc.y,
			  t1,
			  t2,
			  DEntry -> U.Arc.r);
		    break;
		case CIRCLE_DRAW_TYPE:
    			if(!ReturnLayerMode(DEntry->Layer))
				break;
		    IGCircle(PartX + TransMat[0][0] * DEntry -> U.Circ.x +
				     TransMat[0][1] * DEntry -> U.Circ.y,
			     PartY + TransMat[1][0] * DEntry -> U.Circ.x +
				     TransMat[1][1] * DEntry -> U.Circ.y,
			     DEntry -> U.Circ.r);
		    break;
		case TEXT_DRAW_TYPE:
    			 if(!ReturnLayerMode(DEntry->Layer))
				break;

		    /* The text orientation may need to be flipped if the    */
		    /* transformation matrix cuases xy axes to be flipped.   */
		    t1 = (TransMat[0][0] == 0) ^ (DEntry -> U.Text.Horiz !=
						  TEXT_ORIENT_HORIZ);
		    x1 = PartX + TransMat[0][0] * DEntry -> U.Text.x
			       + TransMat[0][1] * DEntry -> U.Text.y;
		    y1 = PartY + TransMat[1][0] * DEntry -> U.Text.x
			       + TransMat[1][1] * DEntry -> U.Text.y;
		    PutTextInfo(t1 ? TEXT_ORIENT_HORIZ : TEXT_ORIENT_VERT,
				x1, y1,	1, DEntry -> U.Text.Text);
		    break;
		case SQUARE_DRAW_TYPE:
    			 if(!ReturnLayerMode(DEntry->Layer))
				break; 
		    x1 = PartX + TransMat[0][0] * DEntry -> U.Sqr.x1
			       + TransMat[0][1] * DEntry -> U.Sqr.y1;
		    y1 = PartY + TransMat[1][0] * DEntry -> U.Sqr.x1
			       + TransMat[1][1] * DEntry -> U.Sqr.y1;
		    x2 = PartX + TransMat[0][0] * DEntry -> U.Sqr.x2
			       + TransMat[0][1] * DEntry -> U.Sqr.y2;
		    y2 = PartY + TransMat[1][0] * DEntry -> U.Sqr.x2
			       + TransMat[1][1] * DEntry -> U.Sqr.y2;
		    IGMoveTo(x1, y1);
		    IGLineTo(x1, y2);
		    IGLineTo(x2, y2);
		    IGLineTo(x2, y1);
		    IGLineTo(x1, y1);
		    break;
		case LINE_DRAW_TYPE:
    		 	if(!ReturnLayerMode(DEntry->Layer))
				break; 
		    x1 = PartX + TransMat[0][0] * DEntry -> U.Line.x1
			       + TransMat[0][1] * DEntry -> U.Line.y1;
		    y1 = PartY + TransMat[1][0] * DEntry -> U.Line.x1
			       + TransMat[1][1] * DEntry -> U.Line.y1;
		    x2 = PartX + TransMat[0][0] * DEntry -> U.Line.x2
			       + TransMat[0][1] * DEntry -> U.Line.y2;
		    y2 = PartY + TransMat[1][0] * DEntry -> U.Line.x2
			       + TransMat[1][1] * DEntry -> U.Line.y2;
		    if (DEntry -> U.Line.Invert) {
			MapX1 = SIGN(x2 - x1);
			MapY1 = SIGN(y2 - y1);
			IGCircle(MapX1 * INVERT_PIN_RADIUS + x1,
				 MapY1 * INVERT_PIN_RADIUS + y1,
				 INVERT_PIN_RADIUS);
			IGMoveTo(MapX1 * INVERT_PIN_RADIUS * 2 + x1,
				 MapY1 * INVERT_PIN_RADIUS * 2 + y1);
			IGLineTo(x2, y2);
		    }
		    else {
			IGMoveTo(x1, y1);
			IGLineTo(x2, y2);
		    }
		    if (IsMulti & ReturnLayerMode(LAYER_PIN)) {
			PutLineTextInfo(x1, y1, x2, y2,
			    LookUpPinText(Entry -> Pins, *NextPinMulti),
			    *NextPinMulti,
			    Entry -> TextInside,
			    Entry -> DrawNums);
			NextPinMulti++;
		    }
		    else {
			PutLineTextInfo(x1, y1, x2, y2,
			    NextPinText,
			    NextPinNum,
			    Entry -> TextInside,
			    Entry -> DrawNums);
			NextPinText = strtok(NULL, PIN_SEPERATOR);
			NextPinNum++;
		    }
		    break;
		case POLYLINE_DRAW_TYPE:
    			if(!ReturnLayerMode(DEntry->Layer))
				break;
		    Poly = (int *) MyMalloc(sizeof(int) * 2 *
							DEntry -> U.Poly.n);
		    for (i = 0; i < DEntry -> U.Poly.n; i++) {
			Poly[i * 2] = PartX +
			    TransMat[0][0] * DEntry -> U.Poly.PolyList[i * 2] +
			    TransMat[0][1] * DEntry -> U.Poly.PolyList[i * 2 + 1];
			Poly[i * 2 + 1] = PartY +
			    TransMat[1][0] * DEntry -> U.Poly.PolyList[i * 2] +
			    TransMat[1][1] * DEntry -> U.Poly.PolyList[i * 2 + 1];
		    }
		    IGPoly(DEntry -> U.Poly.n, Poly, DEntry -> U.Poly.Fill);
		    MyFree((VoidPtr) Poly);
		    break;
	    }
	    DEntry = DEntry -> Pnext;
	}
    }
    else {		  /* NULL Drawing - draw simple a box with all pins: */
	y1 = Entry -> TextInside ? PIN_WIDTH * Entry -> NumOfPins / 4 :
				   PIN_WIDTH * Entry -> NumOfPins / 2;
	x1 = Entry -> TextInside ? (int) (y1 * CHIP_BOX_ASPECT_TI) :
				   (int) (y1 * CHIP_BOX_ASPECT_TO);
        /* But make sure we can still print somethings inside... */
	if (x1 < DRAW_TEXT_WIDTH * 12) x1 = DRAW_TEXT_WIDTH * 12;
	x2 = x1 / 2;
	y2 = y1 / 2;
	x1 = -x2;
	y1 = -y2;
	PinWidth = Entry -> TextInside ? PIN_WIDTH / 2 : PIN_WIDTH;
	if(ReturnLayerMode(LAYER_GATE)){
		IGMoveTo(PartX + TransMat[0][0] * x1 + TransMat[0][1] * y1,
		 	PartY + TransMat[1][0] * x1 + TransMat[1][1] * y1);
		IGLineTo(PartX + TransMat[0][0] * x1 + TransMat[0][1] * y2,
		 	PartY + TransMat[1][0] * x1 + TransMat[1][1] * y2);
		IGLineTo(PartX + TransMat[0][0] * x2 + TransMat[0][1] * y2,
		 	PartY + TransMat[1][0] * x2 + TransMat[1][1] * y2);
		IGLineTo(PartX + TransMat[0][0] * x2 + TransMat[0][1] * y1,
		 	PartY + TransMat[1][0] * x2 + TransMat[1][1] * y1);
		IGLineTo(PartX + TransMat[0][0] * x1 + TransMat[0][1] * y1,
		 	PartY + TransMat[1][0] * x1 + TransMat[1][1] * y1);
		for (i = 0; i < Entry -> NumOfPins / 2; i++) {
	    	y = y2 - PinWidth / 2 - i * PinWidth;
	    	MapX1 = PartX + TransMat[0][0] * x1 + TransMat[0][1] * y;
	    	MapY1 = PartY + TransMat[1][0] * x1 + TransMat[1][1] * y;
	    	MapX2 = PartX + TransMat[0][0] * (x1 - PIN_LENGTH) + TransMat[0][1] * y;
	    	MapY2 = PartY + TransMat[1][0] * (x1 - PIN_LENGTH) + TransMat[1][1] * y;
	    	IGMoveTo(MapX1, MapY1);
	    	IGLineTo(MapX2, MapY2);
	    	if (IsMulti & ReturnLayerMode(LAYER_PINNUM)) {
			PutLineTextInfo(MapX1, MapY1, MapX2, MapY2,
		    	LookUpPinText(Entry -> Pins, *NextPinMulti),
		    	*NextPinMulti,
		    	Entry -> TextInside,
		    	Entry -> DrawNums);
			NextPinMulti++;
	    	}
	   	else if(ReturnLayerMode(LAYER_PINNUM)) {
			PutLineTextInfo(MapX1, MapY1, MapX2, MapY2,
		    	NextPinText,
		    	NextPinNum,
		    	Entry -> TextInside,
                    	Entry -> DrawNums);
			NextPinText = strtok(NULL, PIN_SEPERATOR);
			NextPinNum++;
	    	}
	    }
	}
	for (i = Entry -> NumOfPins / 2 + 1; i <= Entry -> NumOfPins; i++) {
	    y = y1 + PinWidth / 2 + (i - Entry -> NumOfPins / 2 - 1) *
							    PinWidth;
	    MapX1 = PartX + TransMat[0][0] * x2 + TransMat[0][1] * y;
	    MapY1 = PartY + TransMat[1][0] * x2 + TransMat[1][1] * y;
	    MapX2 = PartX + TransMat[0][0] * (x2 + PIN_LENGTH) + TransMat[0][1] * y;
	    MapY2 = PartY + TransMat[1][0] * (x2 + PIN_LENGTH) + TransMat[1][1] * y;
	    if(ReturnLayerMode(LAYER_GATE)){
	    	IGMoveTo(MapX1, MapY1);
	    	IGLineTo(MapX2, MapY2);
	    }
	    if (IsMulti & ReturnLayerMode(LAYER_PINNUM)) {
		PutLineTextInfo(MapX1, MapY1, MapX2, MapY2,
		    LookUpPinText(Entry -> Pins, *NextPinMulti),
		    *NextPinMulti,
		    Entry -> TextInside,
                    Entry -> DrawNums);
		NextPinMulti++;
	    }
	    else if(ReturnLayerMode(LAYER_PINNUM)) {
		PutLineTextInfo(MapX1, MapY1, MapX2, MapY2,
		    NextPinText,
		    NextPinNum,
		    Entry -> TextInside,
                    Entry -> DrawNums);
		NextPinText = strtok(NULL, PIN_SEPERATOR);
		NextPinNum++;
	    }
	}
    }
}

/*****************************************************************************
* Given the Pins string and PinNum returns NULL terminated string of that    *
* pin allocated statically.						     *
*****************************************************************************/
static char *LookUpPinText(char *Pins, int PinNum)
{
    static char Pin[MAX_PIN_INFO];
    int i, Count = 1;

    while (*Pins && Count != PinNum) {
	if (*Pins++ == PIN_SEPERATOR[0]) Count++;
    }

    if (*Pins) {
	i = 0;
	while (i < MAX_PIN_INFO - 1 && *Pins != PIN_SEPERATOR[0] && *Pins != 0)
	    Pin[i++] = *Pins++;
	Pin[i] = 0;
	return i > 0 ? Pin : NULL;
    }
    else {
	return NULL;
    }
}

/*****************************************************************************
* Routine to rotate the given angular direction by the given Transformation. *
* Input (and output) angles must be as follows:				     *
* Angle1 in [0..360], Angle2 > Angle1 in [0..720]. Arc is assumed to be less *
* than 180 degrees.							     *
* Algorithm:								     *
* Map the angles to a point on the unit circle which is mapped using the     *
* transform (only mirror and rotate so it remains on the unit circle) to     *
* a new point which is used to detect new angle.			     *
*****************************************************************************/
static void MapAngles(int *Angle1, int *Angle2, int TransMat[2][2])
{
    int Angle;
    RealType x, y, t;

    x = cos(*Angle1 * M_PI / 180.0);
    y = sin(*Angle1 * M_PI / 180.0);
    t = x * TransMat[0][0] + y * TransMat[0][1];
    y = x * TransMat[1][0] + y * TransMat[1][1];
    x = t;
    *Angle1 = (int) (atan2(y, x) * 180.0 / M_PI + 0.5);

    x = cos(*Angle2 * M_PI / 180.0);
    y = sin(*Angle2 * M_PI / 180.0);
    t = x * TransMat[0][0] + y * TransMat[0][1];
    y = x * TransMat[1][0] + y * TransMat[1][1];
    x = t;
    *Angle2 = (int) (atan2(y, x) * 180.0 / M_PI + 0.5);

    NORMALIZE_ANGLE(*Angle1);
    NORMALIZE_ANGLE(*Angle2);
    if (*Angle2 < *Angle1) *Angle2 += 360;

    if (*Angle2 - *Angle1 > 180) {	     /* Need to swap the two angles. */
	Angle = (*Angle1);
	*Angle1 = (*Angle2);
	*Angle2 = Angle;

	NORMALIZE_ANGLE(*Angle1);
	NORMALIZE_ANGLE(*Angle2);
	if (*Angle2 < *Angle1) *Angle2 += 360;
    }

    *Angle1 -= 1;    /* As the angles loaded are decreased by 1 when loaded. */
    *Angle2 += 1;
}
