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 *



class Simulation:
    def __init__(self, parent, nSpinsX, nSpinsY, stripeSpacingX, stripeSpacingY, Ja, Jbx, Jby, k, baseSigma, scalePhi, zArray, sigmaArray, phiArray, phaseArray, 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.baseSigma = baseSigma
        self.scalePhi = scalePhi
        self.k = k
        self.nTotSpins = nSpinsX*nSpinsY
        self.nUpperLimit = self.nTotSpins
        self.n = -1
        
        self.tracerLength = 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.tvPoints = BooleanVar()
        self.tvTracers = BooleanVar()
        self.tvSphereVectors = BooleanVar()
        self.tvAxis = BooleanVar() 
        
        self.tvLatticeVectors = BooleanVar()
        self.tvEnergyBonds = BooleanVar()
                
        self.tvPhasePoints = BooleanVar()
        self.tvReturnMap = BooleanVar()
        self.tvPlotAngles = BooleanVar()
        self.tvPlotFFT = BooleanVar()

        self.tvUseHamiltonian = BooleanVar()



                
        self.tvTracers.set(1)
        self.tvPoints.set(0)
        self.tvSphereVectors.set(0)
        self.tvAxis.set(0)
        
        self.tvLatticeVectors.set(1)
        self.tvEnergyBonds.set(0)
        
        self.tvPhasePoints.set(0)
        self.tvReturnMap.set(0)
        self.tvPlotAngles.set(0)
        self.tvPlotFFT.set(0)
        
        self.tvUseHamiltonian.set(0)




        self.nLattice = NumericLattice(self.nSpinsX, self.nSpinsY, self.stripeSpacingX, self.stripeSpacingY)
        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.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.phaseSpace.window.select()

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

        self.drag = 0
        self.obs = vector(0.0,0.0,0.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.tvTracers.get() == 1:           self.vSphere.updateTracers()
                    if self.tvPoints.get() == 1:            self.vSphere.updatePoints()
                    if self.tvSphereVectors.get() == 1:     self.vSphere.updateVectors()
                    if self.tvAxis.get() == 1:              self.vSphere.updateAxis()

                    if self.tvLatticeVectors.get() == 1:    self.vLattice.updateVectors()
                    if self.tvEnergyBonds.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.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.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.unhold()



        

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

    def unhold(self):
        self.pause = 0



