#!/usr/bin/env python3

import sys, socket, time, math

class Vector:
       def __init__(self, x, y):
               self.x, self.y = x, y

       def __repr__(self):
               return '<Vector({x}, {y})>'.format(x = self.x, y = self.y)

       def __add__(self, other):
               return Vector(self.x + other.x, self.y + other.y)

       def __sub__(self, other):
               return Vector(self.x - other.x, self.y - other.y)

       def __mul__(self, other):
               return Vector(self.x * other, self.y * other)

       def __truediv__(self, other):
               return Vector(self.x / other, self.y / other)

       def length(self):
               return math.sqrt(self.x**2 + self.y**2)

       def dot(self, v):
               return self.x*v.x + self.y*v.y

class Cloud:
       def __init__(self, px, py, vx, vy, vapor):
               self.px, self.py, self.vx, self.vy, self.vapor = px, py, vx, vy, vapor
               self.pos = Vector(px, py)
               self.vel = Vector(vx, vy)

       def cloud_repr(self, name):
               return '<{name}({pos}, {vel}, {vapor}>'.format(
                               name = name, pos = self.pos,
                               vel = self.vel, vapor = self.vapor)

       def get_radius(self):
               return int(math.floor(math.sqrt(self.vapor)))

       def overlap(self, cloud):
               return (self.pos - cloud.pos).length() < self.get_radius() + cloud.get_radius()

       def intersection(self, cloud):
               a = cloud.vel.x * self.vel.x - cloud.vel.y * self.vel.x
               b = cloud.vel.x * (cloud.pos.y - self.pos.y) - cloud.vel.y * (cloud.pos.x - self.pos.x)
               if a == 0 and b == 0:
                       print('overlap')
                       return True
               elif a != 0:
                       v = Vector(self.pos.x + b/a * self.vel.x, self.pos.y + b/a * self.vel.y)
                       print('collision:', v)
                       return True
               return False

class Thunderstorm(Cloud):
       def __repr__(self):
               return Cloud.cloud_repr(self, 'Thunderstorm')

class Raincloud(Cloud):
       def __repr__(self):
               return Cloud.cloud_repr(self, 'Raincloud')

class State:
       def __init__(self, host, port):
               family = socket.AF_INET
               #if socket.has_ipv6:
               #       family |= socket.AF_INET6
               self.sock = socket.socket(family)
               self.sock.connect((host, port))
               self.s = self.sock.makefile(mode = 'rw', newline = '\n')

       def readline(self):
               return self.s.readline().strip()

       def writeline(self, s):
               self.s.write(s + '\n')
               self.s.flush()

       def read_state(self):
               self.writeline('GET_STATE')
               s = self.readline()
               if not len(s):
                       raise Exception('blah')
               you = -1
               storms = []
               clouds = []
               while s != 'END_STATE':
                       cmd, data = s.split(None, 1)
                       if cmd == 'YOU':
                               you = int(data)
                       elif cmd == 'THUNDERSTORM':
                               storms.append(Thunderstorm(*[float(x) for x in data.split()]))
                       elif cmd == 'RAINCLOUD':
                               clouds.append(Raincloud(*[float(x) for x in data.split()]))
                       s = self.readline()
               self.storms = storms
               self.clouds = clouds
               self.you = you
               self.me = self.storms[you]

       def nearest_cloud(self, v):
               storms = list(self.storms)
               del storms[self.you]
               clouds = self.clouds + storms
               return sorted(clouds, key = lambda c: (c.pos - v).length())

               nearest = -1
               nearest_len = 0
               for i, c in enumerate(self.clouds):
                       if nearest == -1 or (c.pos - v).length() < nearest_len:
                               nearest = i
                               nearest_len = (c.pos - v).length()
               return self.clouds[nearest]

       def wind(self, direction):
               self.writeline('WIND {x} {y}'.format(x = direction.x, y = direction.y))
               print('WIND {x} {y} ({s}, vapor is {v})'.format(x = direction.x, y = direction.y, s = direction.length(), v = self.me.vapor))
               s = self.readline()
               print(s)
               return s == 'OK'

state = State('localhost', 1986)
state.writeline('NAME {0}'.format(sys.argv[0]))
print(state.readline())
print(state.sock.getpeername())

flying = False
dest = Vector(0, 0)

last_dist = 0

def get_vector(dest_cloud, me):
       dest = dest_cloud.pos
       direction = dest - me.pos
       last_dist = direction.length()
       #direction /= direction.length()
       #direction *= 300
       if direction.length() > me.vapor / 2:
               direction /= direction.length()
               direction *= me.vapor / 3
       return direction - me.vel + dest_cloud.vel

while 1:
       start = time.time()
       state.read_state()

       me = state.storms[state.you]
       nearest = state.nearest_cloud(me.pos)
       dest_nearest = state.nearest_cloud(dest)

       if not flying:# or (flying and (dest_nearest[0].pos - me.pos).length() > 100):
               nearest.sort(key = lambda t: math.sqrt(1280**2 + 720**2)-t.vapor)
               vec = Vector(0, 0)
               for dest_cloud in nearest:
                       if dest_cloud.vapor > me.vapor - 310:
                               continue
                       vec = get_vector(dest_cloud, me)
                       dest = dest_cloud.pos
                       last_dist = (me.pos - dest).length()
                       v = dest_cloud.pos - me.pos
                       use = True
                       # try to avoid collision course with big clouds
                       for dc in nearest:
                               if dc == dest_cloud:
                                       continue
                               w = dc.pos - me.pos
                               pb = me.pos + (v * (w.dot(v) / v.dot(v)))
                               if pb.length() <= math.sqrt(me.vapor) + math.sqrt(dc.vapor):
                                       use = False
                                       break
                       if use:
                               print('dest: {0}: {1}, {2}: {3}, dist: {4}'.format(dest, dest_cloud.vapor, me.pos, me.vapor, (me.pos - dest).length()))
                               break

               ##dest_cloud = nearest[0]
               ##dest = dest_cloud.pos
               #me.intersection(dest_cloud)
               # direction to cloud
               ##direction = dest - me.pos
               ##last_dist = direction.length()
               ##direction /= direction.length()
               ##direction *= 100

               # TODO: fix
               ##temp = dest_cloud.vel * 60
               #temp = Vector(sum([dest_cloud.vel.x * (0.999**i) for i in range(1, 11)]),
                               #sum([dest_cloud.vel.y * (0.999**i) for i in range(1, 11)]))
               ##print(temp)
               #direction += temp

               #direction /= direction.length() / 120
               # direction we need to fire in
               #real_dir = direction - me.vel
               ##real_dir = direction - me.vel
               #real_dir += dest_cloud.vel
               #real_dir /= real_dir.length()
               ##strength = real_dir.length()
               #if strength < 1:
                       #strength = 1
               #elif strength > me.vapor / 2:
                       #strength = me.vapor / 2
               #flying = state.wind(dest - me.pos, 100)
               flying = state.wind(vec)
               print('flying =', flying)
       else:
               dest_cloud = dest_nearest[0]
               if me.overlap(dest_cloud):
                       pass
                       #flying = False
               else:
                       dist = (dest_cloud.pos - me.pos).length()
                       if dist > last_dist + 1:
                               flying = False
                       else:
                               last_dist = dist

       t = time.time() - start
       # use 0.11 seconds as base to make sure we don't break the rules
       time.sleep(0.11 - t)