from Tkinter import *
from visual import *
import time,sys,thread

from NumericLattice import *

from VisualSphere import *
from VisualLattice import *

from AnglePlot import *
from PhaseSpace import *

from FFTPlot import *
from PowerSpectra import *

from win32com.client import Dispatch


class Simulation:
    def __init__(self, parent, nSpinsX, nSpinsY, stripeSpacingX, stripeSpacingY, Ja, Jbx, Jby, k, baseSigma, scalePhi, zArray, sigmaArray, phiArray, phaseArray, trackSpins, dt):
        self.parent = parent
        
        self.nSpinsX = nSpinsX
        self.nSpinsY = nSpinsY
        
        self.stripeSpacingX = stripeSpacingX
        self.stripeSpacingY = stripeSpacingY
        
        self.Ja = Ja
        self.Jbx = Jbx
        self.Jby = Jby

        self.zArray = zArray
        self.sigmaArray = sigmaArray
        self.phiArray = phiArray
        self.phaseArray = phaseArray

        self.TrackSpins = trackSpins       


        self.baseSigma = baseSigma
        self.scalePhi = scalePhi
        self.k = k
        self.nTotSpins = nSpinsX*nSpinsY
        self.nUpperLimit = self.nTotSpins
        self.n = -1
        
        self.tracerLength = 256  # int(64/self.nTotSpins**.25)
        
        self.temp = 0.00*abs(self.Ja)
        self.EBu = 0.00*abs(self.Ja)
        self.EBd = 0.00*abs(self.Ja)
        self.EBaxis = vector(0.0,0.0,1.0)

 
        self.paused = 1
        self.pause = 1
        self.t = 0.000
        self.dt = dt
        self.innerLoops = int(50*self.nTotSpins**-.5)+4

        self.innerLoops = 64

        self.powerSpectraTime = 30.0
        self.powerSpectraTimeClk = 30.0



        

        self.tvSphereTracers = BooleanVar()
        self.tvSpherePoints = BooleanVar()
        self.tvSphereVectors = BooleanVar()
        self.tvSphereAxis = BooleanVar()
        self.tvSphereAxisSync = BooleanVar()
        
        self.tvLatticeTracers = BooleanVar()
        self.tvLatticeVectors = BooleanVar()
        self.tvLatticeBonds = BooleanVar()

        


                
        self.tvPhasePoints = BooleanVar()
        self.tvReturnMap = BooleanVar()
        self.tvPlotAngles = BooleanVar()
        
        self.tvPlotFFT = BooleanVar()
        self.tvGetPS = BooleanVar()

        self.tvTrackMaxMin = BooleanVar()
        self.tvUseHamiltonian = BooleanVar()




                
        self.tvSphereTracers.set(1)
        self.tvSpherePoints.set(0)
        self.tvSphereVectors.set(0)
        self.tvSphereAxis.set(0)
        self.tvSphereAxisSync.set(0)
        
        self.tvLatticeTracers.set(0)
        self.tvLatticeVectors.set(0)
        self.tvLatticeBonds.set(0)


              


        
        self.tvPhasePoints.set(0)
        self.tvReturnMap.set(0)
        self.tvPlotAngles.set(0)
        
        self.tvPlotFFT.set(0)
        self.tvGetPS.set(0)

        self.tvTrackMaxMin.set(0)
        self.tvUseHamiltonian.set(0)




        self.nLattice = NumericLattice(self.nSpinsX, self.nSpinsY, self.stripeSpacingX, self.stripeSpacingY, self.TrackSpins)
        self.nLattice.setCouplings(self.Ja, self.Jbx, self.Jby)

        if self.tvUseHamiltonian.get() == 1:
            self.nLattice.setStateFromH(self.k, self.baseSigma, self.n)
        else:
            if self.baseSigma == 0.0:
                self.nLattice.setStateManuallyPhi(self.k, self.scalePhi, self.zArray, self.phiArray, self.phaseArray)
            else:
                self.nLattice.setStateManually(self.k, self.baseSigma, self.zArray, self.sigmaArray, self.phaseArray)
        
        self.nLattice.timeEvolve(self.t, self.dt, self.EBu, self.EBd, self.temp)

        self.vSphere = VisualSphere(self.nSpinsX, self.nSpinsY, self.tracerLength, self.nLattice)
        self.vLattice = VisualLattice(self.nSpinsX, self.nSpinsY, self.stripeSpacingX, self.stripeSpacingY, self.nLattice.couplingXArray, self.nLattice.couplingYArray, self.nLattice)      # change later

        self.anglePlot = AnglePlot(self.nSpinsX, self.nSpinsY, self.nLattice)
        self.phaseSpace = PhaseSpace(self.nSpinsX, self.nSpinsY, self.nLattice)
        self.FFTPlot = FFTPlot(self.nSpinsX, self.nSpinsY, self.nLattice)
        self.powerSpectra = PowerSpectra(self.nSpinsX, self.nSpinsY, self.nLattice, self.dt, self.Jbx, self.powerSpectraTime)


        self.phaseSpace.window.select()

       
        thread.start_new_thread(self.animSpins,(self.innerLoops,))

        self.drag = 0
        self.obs = vector(0.0,0.0,0.0)

        self.planeFlag = 0
        self.timeOfA = 0.0
        self.timeOfB = 0.0

        self.dotSumOld = 0.0
        self.dotSumNew = 0.0

##        self.xlApp = Dispatch("Excel.Application")
##        self.xlApp.Visible = 1
##        self.xlApp.Workbooks.Add()
##
##        self.cell = 0

        

    def animSpins(self,innerLoops):
        print '-----------------------------'
        print "Animation Started"
        print time.ctime()
        self.pause = 0

        print 'Inner Loops', innerLoops     

        while 1:
            self.paused = self.pause
            if self.paused != 1:
                for k in range(innerLoops):
                    self.t += self.dt
                    self.nLattice.timeEvolve(self.t, self.dt, self.EBu, self.EBd, self.temp)

                    if self.tvSphereTracers.get() == 1:      self.vSphere.updateTracers()
                    if self.tvSpherePoints.get() == 1:       self.vSphere.updatePoints()
                    if self.tvSphereVectors.get() == 1:      self.vSphere.updateVectors()
                    if self.tvSphereAxis.get() == 1:         self.vSphere.updateAxis()
                    if self.tvSphereAxisSync.get() == 1:     self.vSphere.updateViewAxis()

                    if self.tvLatticeTracers.get() == 1:     self.vLattice.updateTracers()
                    if self.tvLatticeVectors.get() == 1:     self.vLattice.updateVectors()
                    if self.tvLatticeBonds.get() == 1:       self.vLattice.updateBonds()
               




                    if self.tvPlotAngles.get() == 1:        self.anglePlot.updateCurves(self.t)
                    if self.tvPhasePoints.get() == 1:       self.phaseSpace.addPoint()
                    if self.tvReturnMap.get() == 1:         self.phaseSpace.returnMap()
                    if self.tvPlotFFT.get() == 1:           self.FFTPlot.plotXYtransform()
                    if self.tvTrackMaxMin.get() == 1:       self.checkCoplanar()

                    if self.tvGetPS.get() == 1:
                        if self.powerSpectraTimeClk >= 0.0:
                            self.powerSpectraTimeClk -= self.dt
                            self.powerSpectra.SpectraToFile(self.t)
                        else:
                            self.tvGetPS.set(0)
                            self.powerSpectraTimeClk = self.powerSpectraTime
                            self.powerSpectra.WriteFFT()

##                    if k == 1:
##                        self.xlApp.ActiveSheet.Cells(1,cell).Value = self.nLattice.returnTotalEnergy()
##                        self.cell += 1
                    
                        
            
            if self.paused == 1 or self.dt == 0.0:
                if self.vSphere.window.mouse.events:
                    m = self.vSphere.window.mouse.getevent()
                    if m.click:
                        newpos = self.vSphere.window.mouse.project(normal = self.vSphere.window.forward, d=1)
                        spherePoint = norm(newpos-self.vSphere.window.forward)

                        self.nLattice.moveSpin(vector(spherePoint))


    def randomize(self):
        self.hold()
        self.nLattice.randomizeState()
        self.unhold()

    def distort(self):
        self.hold()
        scale = .1*self.Ja
        self.nLattice.couplingDistortion(scale)
        self.unhold()

    def clear(self):
        self.t = 0.000
        self.phaseSpace.resetPoints()
        self.anglePlot.resetPlots()   

    def reset(self):
        self.hold()
        self.t = 0.000

        self.vSphere.resetTracers()
        self.vLattice.resetTracers()
        
        self.phaseSpace.resetPoints()
        self.anglePlot.resetPlots()
        
        if self.tvUseHamiltonian.get() == 1:
            self.nLattice.setStateFromH(self.k, self.baseSigma, self.n)
        else:
            if self.baseSigma == 0.0:
                self.nLattice.setStateManuallyPhi(self.k, self.scalePhi, self.zArray, self.phiArray, self.phaseArray)
            else:
                self.nLattice.setStateManually(self.k, self.baseSigma, self.zArray, self.sigmaArray, self.phaseArray)
        self.nLattice.clearEnergies()
        self.unhold()

    def deltaE(self):
        self.nLattice.getEnergies()
        deltaEArray = self.nLattice.minEnergyArray - self.nLattice.maxEnergyArray
        print 'delta E Array:\n', deltaEArray
        self.nLattice.clearEnergies()

    def powerSpectra(self):
        self.tvGetPS.set(1)

    def checkCoplanar(self):
        cross12 = norm(cross(self.nLattice.spinArray[0][0][0],self.nLattice.spinArray[1][0][0]))
        cross23 = norm(cross(self.nLattice.spinArray[1][0][0],self.nLattice.spinArray[2][0][0]))
        cross31 = norm(cross(self.nLattice.spinArray[2][0][0],self.nLattice.spinArray[0][0][0]))

        dot2 = abs(dot(cross12,cross23)) # how much spin 2 is in the plane of spin 1 and 3
        dot3 = abs(dot(cross23,cross31))
        dot1 = abs(dot(cross31,cross12))

        self.dotSumNew = dot1+dot2+dot3
        
        if self.dotSumNew < self.dotSumOld and self.planeFlag == 0:       # just left plane, should be within dt of it.
            self.planeFlag = 1
            self.timeOfA = self.timeOfB
            self.timeOfB = self.t

            print '\nSpins 123 Coplanar:'
            print 'Delta t from Last:', self.timeOfB - self.timeOfA
            print 'Spin 1-2 Angle:', (180.0/pi)*arccos(dot(self.nLattice.spinArray[0][0][0],self.nLattice.spinArray[1][0][0]))
            print 'Spin 2-3 Angle:', (180.0/pi)*arccos(dot(self.nLattice.spinArray[1][0][0],self.nLattice.spinArray[2][0][0]))
            print 'Spin 3-1 Angle:', (180.0/pi)*arccos(dot(self.nLattice.spinArray[2][0][0],self.nLattice.spinArray[0][0][0]))
    ##                            print 'Spin 1-z Angle:', (180.0/pi)*arccos(dot(self.nLattice.spinArray[0][0][0],vector(0.0,0.0,1.0)))
    ##                            print 'Spin 2-z Angle:', (180.0/pi)*arccos(dot(self.nLattice.spinArray[1][0][0],vector(0.0,0.0,1.0)))
    ##                            print 'Spin 3-z Angle:', (180.0/pi)*arccos(dot(self.nLattice.spinArray[2][0][0],vector(0.0,0.0,1.0)))
                                       
        if self.dotSumNew > self.dotSumOld and self.planeFlag == 1:       # on approach
            self.planeFlag = 0

        self.dotSumOld = self.dotSumNew      
        
    def hold(self):
        self.pause = 1
        while self.paused == 0: pass

    def unhold(self):
        self.pause = 0



