import math
import cmath
from string import *
from pdPythonLib import *
import os

class PCFShape:
    def __init__(self):
        self.shapeType = 1  #1=ellipse 2=regular polygon 3=rectangle
        self.width = 1
        self.height = 1
        self.rix = 1
        
    def InsertShapeInLayout(self,appClass,Layoutname,xpos,ypos):
        f = appClass
        if (self.shapeType == 1):
            # shape is a circle
            f.AddCmd(Layoutname+".cdev.insertshape(1,ellipse,"+repr(xpos)+","+repr(ypos)+")")
            f.AddCmd(Layoutname+".cdev.shapes[1].width="+repr(self.width))
            f.AddCmd(Layoutname+".cdev.shapes[1].height="+repr(self.height))
            
        elif (self.shapeType == 2):
            # shape is a regular polygon
            f.AddCmd(Layoutname+".cdev.insertshape(1,polygon,"+repr(xpos)+","+repr(ypos)+")")
            # default polygon has 4 points. delete them.
            f.AddCmd(Layoutname+".cdev.shapes[1].removepoint(1)")
            f.AddCmd(Layoutname+".cdev.shapes[1].removepoint(1)")
            f.AddCmd(Layoutname+".cdev.shapes[1].removepoint(1)")
            f.AddCmd(Layoutname+".cdev.shapes[1].removepoint(1)")
            
            # Now insert shape using points list
            l = len(self.poly_xlist)
            for i in range (0,l):
                x = self.poly_xlist[i]
                y = self.poly_ylist[i]
                f.AddCmd(Layoutname+".cdev.shapes[1].insertpoint(1)")
                f.AddCmd(Layoutname+".cdev.shapes[1].plist[1].x="+repr(x))
                f.AddCmd(Layoutname+".cdev.shapes[1].plist[1].y="+repr(y))
                
        elif (self.shapeType == 3):
            # shape is a rectangle
            f.AddCmd(Layoutname+".cdev.insertshape(1,rectangle,"+repr(xpos)+","+repr(ypos)+")")
            f.AddCmd(Layoutname+".cdev.shapes[1].width="+repr(self.width))
            f.AddCmd(Layoutname+".cdev.shapes[1].height="+repr(self.height))
        f.AddCmd(Layoutname+".cdev.shapes[1].xalign=1")
        f.AddCmd(Layoutname+".cdev.shapes[1].yalign=1")
        f.Exec("")

    def SetShapeProperties(self,appClass,Layoutname,ShapeInx,rix,alpha):
        f = appClass
        InxStr = repr(ShapeInx)
        f.AddCmd(Layoutname+".cdev.shapes["+InxStr+"].nr11="+repr(rix))
        f.AddCmd(Layoutname+".cdev.shapes["+InxStr+"].nr22="+repr(rix))
        f.AddCmd(Layoutname+".cdev.shapes["+InxStr+"].alpha11="+repr(alpha))
        f.AddCmd(Layoutname+".cdev.shapes["+InxStr+"].alpha22="+repr(alpha))
        f.Exec("")
               
  
class PCLayout:
    def __init__(self):
        self.nbulk = 2.5
        self.alphaBulk = 0
        self.nhole = 1
        self.holeRad = 0.175
        self.latSpace = 0.5
        self.IsHex = 1      # 1 = hexagonal lattice, 2 = square
        self.nColumns = 12
        self.nRows = 12
        self.bulk = PCFShape()
        self.latticeHole = PCFShape()
        
    def SetMOLABOptions(self,f,Layoutname):
        # USED BY GENERATELayout()
        f.AddCmd(Layoutname+".svp.lambda=1.55")
        f.AddCmd(Layoutname+".svp.hcurv=0")
        f.AddCmd(Layoutname+".svp.solvid=6")
        f.AddCmd(Layoutname+".svp.hsymmetry=0")
        f.AddCmd(Layoutname+".svp.vsymmetry=0")
        f.AddCmd(Layoutname+".svp.buff=V3 1 300 300 15 25 0 5 5")
        f.AddCmd(Layoutname+".mlp.autorun=1")
        f.AddCmd(Layoutname+".mlp.speed=0")
        f.AddCmd(Layoutname+".mlp.mintefrac=100")
        f.AddCmd(Layoutname+".mlp.maxtefrac=100")
        f.AddCmd(Layoutname+".mlp.maxnmodes=40")
        f.AddCmd(Layoutname+".mlp.evstart=1e+050")
        f.AddCmd(Layoutname+".mlp.evend=-1e+050")
        f.AddCmd(Layoutname+".mlp.nx=100")
        f.AddCmd(Layoutname+".mlp.ny=1")

    def InsertBulk(self,f,Layoutname):
        # Used by GenerateLayout() to insert the bulk material
        self.bulk.shapeType=3

        # Set length and width of the Layout        
        if self.IsHex == 1:
            LayoutLength = (self.nColumns-1)*0.5*self.latSpace
            LayoutWidth = self.latSpace*math.cos(math.pi/6)*(self.nRows-1)
        else:
            LayoutLength = (self.nColumns-1)*self.latSpace
            LayoutWidth = (self.nRows-1)*self.latSpace
            
        f.AddCmd(Layoutname+".cdev.width="+repr(LayoutLength)+"")
        f.AddCmd(Layoutname+".cdev.height="+repr(LayoutWidth)+"")
                       
        # Bulk will be placed in center
        bulkxpos = LayoutLength/2.0
        bulkypos = 0
        self.bulk.width=LayoutLength
        self.bulk.height=LayoutWidth
        self.bulk.InsertShapeInLayout(f,Layoutname,bulkxpos,bulkypos)
        self.bulk.SetShapeProperties(f,Layoutname,1,self.nbulk,self.alphaBulk)
                        
        f.Exec("")
        return LayoutLength,LayoutWidth

    def InsertLatticeHoles(self,f,Layoutname,LayoutLength,LayoutWidth):
        # Used by GenerateLayout() to insert the lattice
                   
        self.latticeHole.width = 2.0*self.holeRad
        self.latticeHole.height = 2.0*self.holeRad

        # Now determine the lattice points
        # First point is bottom left (0,Width/2)
        # We will do alternative rows
        xpos = LayoutWidth/2.0
        while xpos >= -1*LayoutWidth/2.0:
            zpos = 0
            while zpos <= LayoutLength:
                self.latticeHole.InsertShapeInLayout(f,Layoutname,zpos,xpos)
                zpos = zpos + self.latSpace
            if self.IsHex == 1:  
                xpos = xpos - (self.latSpace*math.sqrt(3))
            else:
                xpos = xpos - self.latSpace

        # now the "odd" rows
        if self.IsHex == 1:            
            xpos = LayoutWidth/2.0 - (self.latSpace*math.sqrt(3)/2.0)
            while xpos >= -1*LayoutWidth/2.0:
                zpos = self.latSpace/2.0
                while zpos <= LayoutLength:
                    self.latticeHole.InsertShapeInLayout(f,Layoutname,zpos,xpos)
                    zpos = zpos + self.latSpace
                xpos = xpos - (self.latSpace*math.sqrt(3))
                
                
    def AddNotesSubnode(self,f,Layoutname):
        # This Notes subnode will store all specs of the waveguide.
        f.AddCmd(Layoutname+".addsubnode(notesTB,\"Specs\")")
        tempstring = "Bulk Index:   " + repr(self.nbulk)
        f.AddCmd(Layoutname+".subnodes[1].appendtext("+tempstring+")")
        f.AddCmd(Layoutname+".subnodes[1].appendtext(\"\\n\")")
        tempstring = "Hole Index:   " + repr(self.nhole)
        f.AddCmd(Layoutname+".subnodes[1].appendtext("+tempstring+")")
        f.AddCmd(Layoutname+".subnodes[1].appendtext(\"\\n\")")
        tempstring = "Hole radius:   " + repr(self.holeRad)
        f.AddCmd(Layoutname+".subnodes[1].appendtext("+tempstring+")")
        f.AddCmd(Layoutname+".subnodes[1].appendtext(\"\\n\")")
        tempstring = "Lattice Period:   " + repr(self.latSpace)
        f.AddCmd(Layoutname+".subnodes[1].appendtext("+tempstring+")")
        f.AddCmd(Layoutname+".subnodes[1].appendtext(\"\\n\")")
        tempstring = "Number of Columns:   " + repr(self.nColumns)
        f.AddCmd(Layoutname+".subnodes[1].appendtext("+tempstring+")")
        f.AddCmd(Layoutname+".subnodes[1].appendtext(\"\\n\")")
        tempstring = "Number of Rows:   " + repr(self.nRows)
        f.AddCmd(Layoutname+".subnodes[1].appendtext("+tempstring+")")
        f.AddCmd(Layoutname+".subnodes[1].appendtext(\"\\n\")")
        if self.IsHex == 0:
            temp_string = "N"
        else:
            temp_string = "Y"
        tempstring = "Hexagonal Lattice:   " + temp_string
        f.AddCmd(Layoutname+".subnodes[1].appendtext("+tempstring+")")
        f.AddCmd(Layoutname+".subnodes[1].appendtext(\"\\n\")")
        f.Exec("")
    def GenerateLayout(self,projectDir,pdAppClassName,prjname):
        # THIS IS THE MAIN FUNCTION OF PCLayout
        f = pdAppClassName
        Layoutname = "PhotonicCrystal"
        f.AddCmd("app.setwdir("+projectDir+")")
        f.AddCmd("app.addsubnode(fimmwave_prj,"+prjname+")")
        f.AddCmd("Ref& "+prjname+"=app.findnode("+prjname+")")
        f.AddCmd(prjname+".addsubnode(FPLayoutNode,"+Layoutname+")")
        f.AddCmd("Ref& "+Layoutname+"="+prjname+".findnode("+Layoutname+")")
        
        # Now Set size of Layout and bulk material          
        [LayoutLength,LayoutWidth] = self.InsertBulk(f,Layoutname)    
        
        # Now Insert holes
        self.InsertLatticeHoles(f,Layoutname,LayoutLength,LayoutWidth)
                      
        # Now set default MOLAB Options
        self.SetMOLABOptions(f,Layoutname)       
            
        # Now create a Notes subnode and include all specs of the waveguide
        self.AddNotesSubnode(f,Layoutname)

        # prjectDir is currently "" and the file is saved in the working dir (with settings file)    
        f.Exec(prjname+".savetofile(" +projectDir+ "\\" +prjname+ ".prj)") 
    
       

        
