# -*- coding: utf-8 -*-

from math import sqrt
from time import sleep

from .. import run_simulation, translated_binaries
#from .. import visualiser 
#from visualiser import Visualiser

from translated_binaries import bin1

#from cpp import virtualmachine
#vm = virtualmachine()
#vm.load('binaries/bin1.obf')

X = 0x2
Y = 0x3

class Rocket( object ):

    def __init__( self, do_graph = True, do_log = True, control = lambda t,rocket : None ):        
        #self.display = Visualiser((750,750))
        self.controller = control
        self.last_x = 0
        self.last_y = 0
        self.last_v = 0
        self.last_r = 0
        self.delta_x = 0.0
        self.delta_y = 0.0
        self.delta_v = 0.0        
        self.delta_r = 0.0
        self.x = 0
        self.y = 0
        self.v = 1
        self.r = 0

    def __call__( self, t, score, fuel, x, y, r_goal ):
    
        self.goal = r_goal
    
        if score > 0:
        
            print "WE WIN!!!"
            print "Score = %s" % score
            sleep(.1)
    
        if t==0:
            self.last_x = x
            self.last_y = y
            return
        if t==1:            
            self.last_v, self.delta_x, self.delta_y = self.vector( self.last_x, self.last_y, x, y )
            self.last_x = x
            self.last_y = y
            return
            
        self.last_x = self.x
        self.last_y = self.y
        self.last_v = self.v
        self.last_r = self.r        
                    
        v, self.delta_x, self.delta_y = self.vector( self.last_x, self.last_y, x, y )
        
        r, blah, blah  = self.vector( 0, 0, x, y )
        
        #self.display.visualise(t, score, fuel, x, y, r_goal)
        self.log( t, score, fuel, x, y, r_goal, v, self.delta_x, self.delta_y, self.delta_v, r, self.goal - r )
            
        self.fuel = fuel
        self.x = x
        self.y = y
        self.v = v
        self.r = r                
        
        self.delta_v = self.last_v - v
        self.delta_r = self.last_r - r
        
        return self.controller(t, self)
          
    def vector( self, last_x, last_y, x, y ):
        delta_x = last_x - x
        delta_y = last_y - y
        v = sqrt( delta_x**2 + delta_y**2 )
        return v, delta_x, delta_y
        
    def log( self, t, score, fuel, x, y, r_goal, v, delta_x, delta_y, delta_v, r, delta_r ):
        print "%s : [ Fuel: %s | X: %.2f | Y: %.2f | target = %s ]" % ( t, fuel, x, y, r_goal )        
        print "        [ Velocity: %.2f | Delta X: %.2f | Delta Y: %.2f | Delta V: %.2f | Range: %.2f | Delta Range: %.2f ]" % ( v, delta_x, delta_y, delta_v, r, delta_r )
        
    def tan_burn( self, v ):
        x,y = self.slope(v)
        return { X: x, Y: y }        

    def tan_break( self, v ):
        x,y = self.slope(v)
        return { X: x*-1.0, Y: y*-1.0 }        
        
    def engine_stop( self ):
        return { X: 0, Y: 0 }        
        
    def slope( self, v ):
        total = abs(self.delta_x) + abs(self.delta_y) * 1.0
        dv_x = self.delta_x/total * (v*1.0)
        dv_y = self.delta_y/total * (v*1.0)
        return dv_x, dv_y

class accel_100( object ):
    
    def __init__( self ):
        self.burn_start = None        
        self.burn_count = 10
        self.impulses = [  200, 0, 0, 100, 0, 0 ]

    def __call__( self, time, rocket ):
        if time < 3: return
        if rocket.x >= 0 and rocket.x + rocket.delta_x <= 0 and self.burn_start is None and self.burn_count:
            print "Top Burn on!"
            self.burn_start = time            
            self.burn_count -= 1
            return rocket.tan_burn( self.impulses[ self.burn_count % 6 ] )
            
        if rocket.y >= 0 and rocket.y + rocket.delta_y <= 0 and self.burn_start is None and self.burn_count:
            print "Top Burn on!"
            self.burn_start = time    
            self.burn_count -= 1
            return rocket.tan_burn( self.impulses[ self.burn_count % 6 ] )            
            
        if rocket.x <= 0 and rocket.x + rocket.delta_x >= 0 and self.burn_start is None and self.burn_count:
            print "Bottom Burn on!"
            self.burn_start = time            
            return rocket.tan_burn( self.impulses[ self.burn_count % 6 ] )
            self.burn_count -= 1                        
            
        if rocket.y <= 0 and rocket.y + rocket.delta_y >= 0 and self.burn_start is None and self.burn_count:
            print "Up Burn on!"
            self.burn_start = time
            return rocket.tan_burn( self.impulses[ self.burn_count % 6 ] )
            self.burn_count -= 1            
            
        if self.burn_start and time - self.burn_start == 5:
            print "Burn stop!"
            self.burn_start = None            
            return rocket.engine_stop()
        if self.burn_start:
            print "Burning."
        else:
            print "No burn."            
        return None              
        
class tangent_burn( object ):

    def __init__( self ):
        self.burn_start = None                    
        self.burn_count = 0
        self.burn_delay = 5000
    

    def __call__( self, time, rocket ):
        if time > 0 and self.burn_start is None and (time % 5100)==0 and self.burn_count < 64:
            self.burn_start = time
            self.burn_count += 1
            self.burn_delay *= 1.05
            return rocket.tan_burn(9)                                                  
        if self.burn_start and time - self.burn_start == 5 :
            self.burn_start = None            
            return rocket.engine_stop()
        if self.burn_count >= 64:
            #if rocket.r < rocket.goal - 1000 and rocket.r + rocket.delta_r >= rocket.goal - 1000:
            if rocket.r < rocket.goal and rocket.r + rocket.delta_r >= rocket.goal:
                self.burn_start = time
                return rocket.tan_break(15)
#            if rocket.r > rocket.goal and rocket.r + rocket.delta_r <= rocket.goal:
#                self.burn_start = time
#                return rocket.tan_break(5)                
            if self.burn_start and self.burn_start + 2 == time:
                self.burn_start = None
                return rocket.engine_stop()


class aoa_burn( object ):

    def __init__( self ):
        self.burn_start = None                    
        self.burn_count = 0
        self.burn_delay = 5000
        self.burn_limit = 50
        self.burn_length = 5
    

    def __call__( self, time, rocket ):
        if time > 0 and self.burn_start is None and (time % self.burn_delay)==0 and self.burn_count < self.burn_limit:
            self.burn_start = time
            self.burn_count += 1
            self.burn_delay *= 1.05
            return rocket.tan_burn(20)                                                  
        if self.burn_start and time - self.burn_start == self.burn_length :
            self.burn_start = None            
            return rocket.engine_stop()
        if self.burn_count >= self.burn_limit:
            #if rocket.r < rocket.goal - 1000 and rocket.r + rocket.delta_r >= rocket.goal - 1000:
            if rocket.r < rocket.goal and rocket.r + rocket.delta_r >= rocket.goal:
                self.burn_start = time
                return rocket.tan_break(15)
#            if rocket.r > rocket.goal and rocket.r + rocket.delta_r <= rocket.goal:
#                self.burn_start = time
#                return rocket.tan_break(5)                
            if self.burn_start and self.burn_start + 2 == time:
                self.burn_start = None
                return rocket.engine_stop()
        
class hohmann( object ):

    EarthG = 398600.0

    def __init__( self ):
        self.action = self.burn_fuel
        self.orbit_count = 0 # 3
        self.coast_count = 4
        self.fuel_to_dump = 6050 + 1200 + 300 + 80
        self.in_range_count = 0
        self.last_in_range_time = None
        
    def burn_fuel( self, time, rocket ):
        if self.fuel_to_dump:
            self.fuel_to_dump -= 1
            if time % 2:
                return rocket.tan_burn(1.0)
            else:
                return rocket.tan_break(1.0)                 
        self.action = self.start       
        return { X : 0, Y : 0 }
        
    def start( self, time, rocket ):
        """
        Wait until we are two seconds away from apogee.
        """
        print "waiting..."
        if rocket.x <= 0 and rocket.x + (rocket.delta_x * 2) >= 0:
            if self.orbit_count:
                self.orbit_count -= 1
            else:                  
                self.action = self.delta_v1    
                                
        
    def delta_v1( self, time, rocket ):        
        d_v = sqrt( hohmann.EarthG / (rocket.r/1000.0) ) * ( sqrt( (2 * (rocket.goal/1000.0)) / ((rocket.r/1000.0) + (rocket.goal/1000.0)) ) - 1 )
        self.action = self.coast
        result = d_v * 1000.0
        # winner without burn_fuel result = result + 2.8
        result = result + 3.5 + 2.17 - .3
        print "DELTA V1 x_pulse = %.2f !!!" % (result)
        return { X : (result) }        
        
    def coast( self, time, rocket ):
        print "coasting..."
        if self.coast_count:
            self.coast_count -= 1
            #sleep(1)
        if rocket.x >= 0 and rocket.x + (rocket.delta_x * 2) <= 0:            
            #self.action = self.delta_v2
            self.action = self.velocity2
        return { X : 0 }            
        
    def delta_v2( self, time, rocket ):
        d_v = sqrt( hohmann.EarthG / (rocket.goal/1000.0) ) * ( 1- sqrt( (2 * (rocket.r/1000.0)) / ((rocket.r/1000.0) + (rocket.goal/1000.0)) ) )
        self.action = self.arrive
        self.coast_count = 10
        print "DELTA V2 x_pulse = %.2f !!!" % ( d_v * -1.0 * 1000.0 )
        return { X : (d_v  * 1000.0) }
        
    def velocity2( self, time, rocket ):
        v = sqrt( hohmann.EarthG * ( (2/(rocket.r/1000.0)) - (1/(rocket.goal/1000.0)) ) ) * 1000.0
        #v = sqrt( hohmann.EarthG * ( (2/(rocket.r/1000)) - (1/(rocket.r/1000)) ) ) * 1000
        print "VELO2 target v = %.2f tan_pulse = %.2f !!!" % ( v, abs(v) - abs(rocket.v) )
        self.coast_count = 3
        self.action = self.arrive
        #return rocket.tan_burn( (abs(v)-abs(rocket.v)) )
        return { X : (abs(v)-abs(rocket.v)) * -1.0 }
        
    def arrive( self, time, rocket ):
        print "Arrival?"
        if self.coast_count:
            self.coast_count -= 1
            sleep(1)        
        return { X : 0 }
                
    def __call__( self, time, rocket ):
        if time < 3: return
        if abs( rocket.r - rocket.goal ) < 1000:
            self.in_range_count += 1
            self.last_in_range_time = time
            print "IN RANGE #%s!!" % self.in_range_count
        else:
            if self.last_in_range_time and self.last_in_range_time == time - 1:
                print "JUST OUT OF RANGE."
                sleep(7)
        return self.action( time, rocket )
        
                        
 
def slow_go():
    return Rocket( control = hohmann() )                    
        
#run_simulation( bin1.OrbitalVirtualMachine(), 1001, Rocket( control = hohmann() ) )
#run_simulation( vm, 1002, Rocket( control = accel_100() ) )
#run_simulation( vm, 1002, Rocket( control = tangent_burn() ) )

