#import pygame._view
import math, random, sys, os, pygame, copy
from pygame.locals import *

pygame.init()
#pygame.mixer.music.load('shabby.ogg')
#pygame.mixer.music.play(-1)
os.environ['SDL_VIDEO_CENTERED'] = '1'
screen = pygame.display.set_mode((800,600))
pygame.display.set_caption('Oh, what fun! Left click to create ball, right click to delete all, up/down to change accuracy.')
clock = pygame.time.Clock()

#icon = pygame.Surface((32,32))
#icon.set_colorkey((0,0,0))
#rawicon = pygame.image.load('icon.bmp')
#pixArr = pygame.PixelArray(icon)
#for x in range(0,32):
        #for y in range(0,32):
                #pixArr[x][y] = rawicon.get_at((x,y))
#del pixArr

#pygame.display.set_icon(icon)

# bullshit
def acceleration(v1, v2):
        direction = v1.sub(v2)
        length = direction.length()
        normal = direction.normalized()
        return normal.mul(G/(length**2))

class Body():
        def __init__(self, x, y, radius):
                self.px = x
                self.py = y
                self.ax = 0
                self.ay = 0
                self.radius = radius
                self.color = random.choice(([255,0,0],[0,255,0],[0,0,255]))
                self.rect = pygame.Rect(x,y,radius*2,radius*2)
                
        def accelerate(self, delta):
                self.rect.x += self.ax * (delta**2)
                self.rect.y += self.ay * (delta**2)
                self.ax = 0
                self.ay = 0
        def inertia(self, delta):
                x = self.rect.x * 2 - self.px
                y = self.rect.y * 2 - self.py
                self.px = copy.copy(self.rect.x)
                self.py = copy.copy(self.rect.y)
                self.rect.x = x
                self.rect.y = y
        def draw(self):
                if self.color[0] == 255:
                        if self.color[1] == 255:
                                self.color[0] -= 5
                        elif self.color[2] > 0:
                                self.color[2] -= 5
                        else:
                                self.color[1] += 5
                elif self.color[1] == 255:
                        if self.color[2] == 255:
                                self.color[1] -= 5
                        elif self.color[0] > 0:
                                self.color[0] -= 5
                        else:
                                self.color[2] += 5
                elif self.color[2] == 255:
                        if self.color[0] == 255:
                                self.color[2] -= 5
                        elif self.color[1] > 0:
                                self.color[1] -= 5
                        else:
                                self.color[0] += 5
                pygame.draw.circle(screen, (255-self.color[0],255-self.color[1],255-self.color[2]), (int(self.rect.x), int(self.rect.y)), self.radius+2)
                pygame.draw.circle(screen, self.color, (int(self.rect.x), int(self.rect.y)), self.radius)

class Vector():
        def __init__(self, x, y):
                self.x = x
                self.y = y
        def length(self):
                return math.sqrt(self.x**2 + self.y**2)
        def normalized(self):
                return Vector(self.x/self.length(), self.y/self.length())
        def distance(self, vector):
                return math.sqrt((self.x - vector.x)**2 + (self.y - vector.y)**2)
        def zero(self):
                self.x = 0
                self.y = 0
        def add(self, vector):
                return Vector(self.x + vector.x, self.y + vector.y)
        def iadd(self, vector):
                self.x += vector.x
                self.y += vector.y
        def sub(self, vector):
                return Vector(self.x - vector.x, self.y - vector.y)
        def isub(self, vector):
                self.x -= vector.x
                self.y -= vector.y
        def mul(self, scalar):
                return Vector(self.x * scalar, self.y * scalar)
        def imul(self, scalar):
                self.x *= scalar
                self.y *= scalar
        def div(self, scalar):
                return Vector(self.x / scalar, self.y / scalar)
        def idiv(self, scalar):
                self.x /= scalar
                self.y /= scalar
        def int(self):
                return (int(self.x), int(self.y))
        
class Pointmass():
        def __init__(self, x, y, fixed=False):
                self.position = Vector(x,y)
                self.previous = Vector(x,y)
                self.acceleration = Vector(0,0)
                self.fixed = fixed
        def accelerate(self, vector):
                if not self.fixed:
                        self.acceleration.iadd(vector)
                        #print('accelerated, new pos:',self.position.int())
        def simulate(self, delta):
                if not self.fixed:
                        self.acceleration.imul(delta**2)
                        position = copy.copy(self.position)
                        #print('position var created, value:',position.int())
                        position.imul(2)
                        #print('position imul\'d, value:',position.int())
                        position.isub(self.previous)
                        #print('position isub\'d, value:',position.int())
                        position.iadd(self.acceleration)
                        #print('position iadd\'d, value:',position.int())
                        self.previous = copy.copy(self.position)
                        #print('previous position set to current, value:',self.previous.int())
                        self.position = position
                        #print('new position set, value:',self.position.int())
                        self.acceleration.zero()
        def correct(self, vector):
                if not self.fixed:
                        self.position.iadd(vector)
        def draw(self):
                if not self.fixed:
                        color = (248, 248, 128)
                else:
                        color = (216, 32, 248)
                pygame.draw.circle(screen, color, self.position.int(), 3)

class Constraint():
        def __init__(self, p1, p2, target=None):
                self.point1 = p1
                self.point2 = p2
                if target != None:
                        self.target = target
                else:
                        self.target = p1.position.distance(p2.position)
        def resolve(self, delta):
                pos1 = self.point1.position
                pos2 = self.point2.position
                direction = pos2.sub(pos1)
                length = direction.length()
                factor = (length-self.target)/length
                correction = direction.mul(factor).mul(delta)
                
                self.point1.correct(correction)
                correction.imul(-1)
                self.point2.correct(correction)
        def draw(self):
                pygame.draw.line(screen, (32,200,240), self.point1.position.int(), self.point2.position.int(), 3)

points = []
constraints = []
bodies = []
gravity = Vector(0,2)
steps = 8
delta = 1/steps
damping = 0.98

#bg = pygame.image.load('bg.jpg')

#bridge simulation
points.append(Pointmass(50,450,True))
points.append(Pointmass(50,500,True))
for x in range(100,351,50):
        for y in range(450,501,50):
                points.append(Pointmass(x,y))
points.append(Pointmass(750,450,True))
points.append(Pointmass(750,500,True))
for x in range(100,351,50):
        for y in range(450,501,50):
                points.append(Pointmass(800-x,y))

for x in range(2,14):
        if x%2 == 0:
                constraints.append(Constraint(points[x-2], points[x]))
        else:
                for y in range(x-3,x):
                        constraints.append(Constraint(points[y], points[x]))
for x in range(16,28):
        if x%2 == 0:
                constraints.append(Constraint(points[x-2], points[x]))
        else:
                for y in range(x-3,x):
                        constraints.append(Constraint(points[y], points[x]))

while True:
        mousepos = pygame.mouse.get_pos()
        for event in pygame.event.get():
                if event.type == KEYDOWN:
                        if event.key == K_ESCAPE:
                                pygame.quit()
                                sys.exit()
                        elif event.key == K_UP:
                                steps += 1
                                delta = 1/steps
                        elif event.key == K_DOWN:
                                if steps >= 2:
                                        steps -= 1
                                        delta = 1/steps
                elif event.type == QUIT:
                        pygame.quit()
                        sys.exit()
                elif event.type == MOUSEBUTTONDOWN:
                        if event.button == 1:
                                bodies.append(Body(mousepos[0], mousepos[1], 52))
                        elif event.button == 3:
                                bodies = []
        screen.fill((0,0,0))
        #screen.blit(bg,(0,0))
        for x in range(0,steps):
                for c in constraints:
                        c.resolve(delta)
                for p in points:
                        p.accelerate(gravity.mul(delta))
                        p.simulate(delta)
        for c in constraints:
                c.draw()
        for p in points:
                p.draw()
        for b in bodies:
                for x in range(0,steps):
                        b.ay += 0.5
                        b.accelerate(delta)
                        clist = b.rect.collidelistall([body.rect for body in bodies])
                        for i in clist:
                                if True:
                        #for b1 in bodies:
                                #for b2 in bodies:
                                        if b != bodies[i]:
                                                #print b, bodies[i]
                                                length = ((b.rect.x - bodies[i].rect.x)**2 + (b.rect.y - bodies[i].rect.y)**2)**.5
                                                target = b.radius + bodies[i].radius
                                                if length < target:
                                                        v1x = b.rect.x - b.px
                                                        v1y = b.rect.y - b.py
                                                        v2x = bodies[i].rect.x - bodies[i].px
                                                        v2y = bodies[i].rect.y - bodies[i].py
                                                        if length != 0:
                                                                factor = (length-target)/length
                                                        else:
                                                                factor = 0
                                                        b.rect.x -= x*factor/2
                                                        b.rect.y -= y*factor/2
                                                        bodies[i].rect.x += x*factor/2
                                                        bodies[i].rect.y += y*factor/2
                        if b.rect.x - b.radius < 0:
                                b.rect.x = b.radius
                        elif b.rect.x + b.radius > screen.get_width():
                                b.rect.x = screen.get_width() - b.radius
                        if b.rect.y - b.radius < 0:
                                b.rect.y = b.radius
                        elif b.rect.y + b.radius > screen.get_height():
                                b.rect.y = screen.get_height() - b.radius
                        b.inertia(delta)
                        clist = b.rect.collidelistall([bodies[i].rect for body in bodies])
                        for i in clist:
                                if True:
                        #for b1 in bodies:
                                #for b2 in bodies:
                                        if b != bodies[i]:
                                                length = ((b.rect.x - bodies[i].rect.x)**2 + (b.rect.y - bodies[i].rect.y)**2)**.5
                                                target = b.radius + bodies[i].radius
                                                if length < target:
                                                        v1x = b.rect.x - b.px
                                                        v1y = b.rect.y - b.py
                                                        v2x = bodies[i].rect.x - bodies[i].px
                                                        v2y = bodies[i].rect.y - bodies[i].py
                                                        if length != 0:
                                                                factor = (length-target)/length
                                                        else:
                                                                factor = 0
                                                        b.rect.x -= x*factor/2
                                                        b.rect.y -= y*factor/2
                                                        bodies[i].rect.x += x*factor/2
                                                        bodies[i].rect.y += y*factor/2
                                                        if length != 0:
                                                                f1 = (damping*(x*v1x+y*v1y))/(length**2)
                                                                f2 = (damping*(x*v2x+y*v2y))/(length**2)
                                                                v1x += f2*x-f1*x
                                                                v2x += f1*x-f2*x
                                                                v1y += f2*y-f1*y
                                                                v2y += f1*y-f2*y
                                                                b.px = b.rect.x - v1x
                                                                b.py = b.rect.y - v1y
                                                                bodies[i].px = bodies[i].rect.x - v2x
                                                                bodies[i].py = bodies[i].rect.y - v2y
                b.draw()
        pygame.display.flip()
        clock.tick(60)
