#!/usr/bin/python
#-*- coding:utf8 -*-

#TODO: Als class umbauen, und Methoden zur Rückgabe von Objekt, "Parse-Protocoll", Bild, ...

#Ein nicht wirklich sinnvolles Bauteil
Beispiel1 = """EESchema-LIBRARY Version 2.2  Date: 01/06/2010-18:05:26
#
# DIL16
#
DEF DIL16 U 0 40 Y Y 1 0 N
F0 "U" 0 -100 49STS H V C C
F1 "DIL16" 0 100 49STS H V C C
DRAW
S -500 -600 500 600 0 0 0 N
X PIN1 1 -800 400 300 R 50 50 1 1 I
X PIN2 2 800 -400 300 L 50 50 1 1 I
X PIN3 3 0 -900 300 U 50 50 1 1 I
X PIN4 4 0 900 300 D 50 50 1 1 I
X PIN5 5 -800 300 300 R 50 50 1 1 I
X PIN6 6 -800 200 300 R 50 50 1 1 I I
X PIN7 7 -800 100 300 R 50 50 1 1 I C
X PIN8 8 -800 0 300 R 50 50 1 1 I IC
X PIN9 9 800 -300 300 L 50 50 1 1 I L
X PIN10 10 800 -200 300 L 50 50 1 1 I CL
X PIN11 11 800 -100 300 L 50 50 1 1 I V
X PIN12 12 800 0 300 L 50 50 1 1 O
X PIN13 13 800 100 300 L 50 50 1 1 B
X PIN14 14 800 200 300 L 50 50 1 1 T
X PIN15 15 800 300 300 L 50 50 1 1 P
X PIN16 16 800 400 300 L 50 50 1 1 U
ENDDRAW
ENDDEF
#
#End Library
"""

#Auch dieses Bauteil aus einer öffentlichen lib scheint (bei näherem Hinsehen) etwas seltsam zu sein. Zum Testen aber egal.
Beispiel2 = """EESchema-LIBRARY Version 2.3  Date: 11/5/2009-21:04:17
#
# 26ET31
#
DEF 26ET31 U 0 0 Y N 4 F N
F0 "U" -400 250 60 H V C C
F1 "26ET31" -375 -250 60 H V C C
ALIAS ET7272 DS26LS31 AM26LS31
$FPLIST
 SO16E
 SO16N
 16DIP300
 16DIP-ELL300
$ENDFPLIST
DRAW
P 3 0 1 0  225 0  -250 200  -250 200 N
P 4 0 1 0  225 0  -250 -200  -250 -200  -250 -200 N
P 3 0 1 0  -250 -200  -250 200  -250 200 N
X Vcc 16 -175 475 300 D 60 60 1 1 B
X EN/VCCB 4 -50 475 350 D 60 60 1 1 I
X A+ 3 350 -100 300 L 60 60 1 1 O I
X B- 5 350 -100 300 L 60 60 2 1 O I
X C- 11 350 -100 300 L 60 60 3 1 O I
X D- 13 350 -100 300 L 60 60 4 1 O I
X A+ 6 350 100 350 L 60 60 2 1 O
X A+ 14 350 100 350 L 60 60 4 1 O
X A+ 10 350 100 350 L 60 60 3 1 O
X A+ 2 350 100 350 L 60 60 1 1 O
X BIN 9 -550 0 300 R 60 60 3 1 I
X BIN 15 -550 0 300 R 60 60 4 1 I
X BIN 7 -550 0 300 R 60 60 2 1 I
X BIN 1 -550 0 300 R 60 60 1 1 I
X GND 8 -175 -475 300 U 60 60 1 1 B
X EN- 12 -50 -475 350 U 60 60 1 1 I I
ENDDRAW
ENDDEF
#
#End Library
"""

doc = """http://en.wikibooks.org/wiki/Kicad/file_formats#Schematic_Libraries_Files_Format

Sizes and coordinates are given in mils (1/1000 inch)

The format is as follows :
DEF name reference unused text_offset draw_pinnumber draw_pinname unit_count units_locked option_flag
F0 reference posx posy text_size text_orient visibility htext_justify vtext_justify
F1 name posx posy text_size text_orient visibility htext_justify vtext_justify
$FPLIST
footprint list
$ENDFPLIST
ALIAS name1 name2 name3 fields list
DRAW
list graphic elements and pins
ENDDRAW
ENDDEF

DEF name reference unused text_offset draw_pinnumber draw_pinname unit_count units_locked option_flag
F0 reference posx posy text_size text_orient visibile htext_justify vtext_justify
F1 name posx posy text_size text_orient visibility htext_justify vtext_justify
$FPLIST
This line exists if one or more footprints are specified. Footprint names can have wildcards.
$ENDFPLIST
ALIAS name1 name2 name3…

A (Arc) posx posy radius start_angle end_angle unit convert thickness fill startx starty endx endy
C (Circle) posx posy radius unit convert thickness fill
P (Polyline) point_count unit convert thickness (posx posy)* fill
S (Rectangle) startx starty endx endy unit convert thickness fill
X (Pin) name num posx posy length direction name_text_size num_text_size unit convert electrical_type pin_type
  electrical_type: I=Input, O=Output, B=Bidi, T=tristate, P=Passive, U=Unspecified, W=Power In, w=Power Out, C=Open Collector, E=Open Emitter
  pin_type: N=No Draw, I=Invert (hollow circle), C=Clock, L=Low In (IEEE), V=Low Out (IEEE)
T (Text) direction posx posy text_size text_type unit convert text
  All ~ characters are replaced with spaces.
"""

debug = True
#debug = False

FPLIST = []

Electrical_types = {'I': 'Input', 'O': 'Output', 'B': 'Bidi', 'T': 'Tristate', 'P': 'Passive', 'U': 'Unspecified', 'W': 'Power In', 'w': 'Power Out', 'C': 'Open Collector', 'E': 'Open Emitter'}
Pin_types = {'N': 'No Draw', 'I': 'Inverted', 'C': 'Clock', 'L': 'Low In (IEEE)', 'V': 'Low Out (IEEE)'} # Inverted = hollow circle

def Parse(library):
	FPLISTread = False
	DRAW = False

	for line in (library).split('\n'):
		line = line.strip('\r')
		if line.startswith('#End Library'):
			if debug: print '=' * 80
		
		elif line.startswith('#'):
			pass # Ignore comment
		
		elif FPLISTread:
			if line.startswith("$ENDFPLIST"):
				FPLISTread = False
				if debug: print 'Footprints:', FPLIST
			else:
				FPLIST.append(line.strip(' '))
		elif line == '':
			pass
		
		else:
			while line.find('  ') >= 0:
				line = line.replace('  ', ' ')
			fields = line.split(' ')
			CMD = fields[0]
	
			if CMD == 'EESchema-LIBRARY':
				if debug: print '=' * 40, line
		
			elif CMD == 'DEF':
				#print line
				name, reference, unused, text_offset, draw_pinnumber, draw_pinname, unit_count, units_locked, option_flag = fields[1:] #DEF
				if debug: print 'Device:', name, '(Units: ' + unit_count + ')'
	
			elif CMD == 'F0':
				reference, posx, posy, text_size, text_orient, visibile, htext_justify, vtext_justify = fields[1:] #F0
	
			elif CMD == 'F1':
				name, posx, posy, text_size, text_orient, visibility, htext_justify, vtext_justify = fields[1:] #F1
			
			elif CMD == '$FPLIST':
				FPLISTread = True
			
			elif CMD == '$ENDFPLIST':
				print 'Error: Parsing $ENDFPLIST' # Should never be here
			elif CMD == 'ALIAS':
				ALIAS = fields[1:] #ALIAS
				if debug: print 'Aliase:', ALIAS
			elif CMD == 'DRAW':
				DRAW = True
			
			elif CMD == 'A':
				posx, posy, radius, start_angle, end_angle, unit, convert, thickness, fill, startx, starty, endx, endy = fields[1:] #A
				if debug: print 'Arc: (%s,%s) r=%s, %s .. %s' % (posx, posy, radius, start_angle, end_angle)
				
			elif CMD == 'C':
				posx, posy, radius, unit, convert, thickness, fill = fields[1:] #C
				if debug: print 'Circle: (%s,%s) r=%s' % (posx, posy, radius)
				
			elif CMD == 'P':
				point_count, unit, convert, thickness = fields[1:5]
				posxy = fields[5:-1]
				fill = fields[-1]
				if debug:
					print 'Polyline (%s points):' % (point_count,),
					for i in range(len(posxy)/2):
						print '(%s,%s)' % (posxy[i*2],posxy[i*2+1]),
					print
				
			elif CMD == 'S':
				startx, starty, endx, endy, unit, convert, thickness, fill = fields[1:] #S
				if debug: print 'Rectangle: (%s,%s)..(%s,%s)' % (startx, starty, endx, endy)
				
			elif CMD == 'X':
				#X PIN16 16 800 400 300 L 50PUTS 50PNTS 1 1 U
				name, num, posx, posy, length, direction, name_text_size, num_text_size, unit, convert, electrical_type = fields[1:12] #X
				if len(fields) == 13: #pin_type seems to be optional
					pin_type = fields[12]
				else:
					pin_type = None
				if debug:
					pt = Electrical_types[electrical_type]
					if pin_type:
						for c in pin_type:
							pt += ' ' + Pin_types[c]
					print 'Pin %s: %s (%s), unit %s (%s,%s)' % (num, name.replace('<', '&lt;'), pt, unit, posx, posy)
					#TODO: The replacement of < is a bad hack. This should be done in a completely different way:
					#- The debug/print constructs should be functions, and the whole Parse should return a string,
					#  which can then be properly escaped by the calling structure.
					#- Scanning files of unknown origiin is a security risk anyway, so every item must be sanitized.
				
			elif CMD == 'T':
				direction, posx, posy, text_size, text_type, unit, convert, text = fields[1:] #T
				if debug: print 'Text:', text
	
			elif CMD == 'ENDDRAW':
				DRAW = False
			
			elif CMD == 'ENDDEF':
				if debug: print '-' * 40
			
			else:
				if debug: print '*** Unrecognized CMD "%s" in line: %s' % (CMD, line)

if __name__ == '__main__':
	print 'Testbauteil 1:'
	Parse(Beispiel1)
	print
	print 'Testbauteil 2:'
	Parse(Beispiel2)
	print 'Done.'
