import pygame
import random

# creating the data structure for pieces
# setting up global vars
# functions
# - create_grid
# - draw_grid
# - draw_window
# - rotating shape in main
# - setting up the main

"""
10 x 20 square grid
shapes: S, Z, I, O, J, L, T
represented in order by 0 - 6
"""

pygame.font.init()

# GLOBALS VARS
s_width = 800       #screen width
s_height = 700      #screen height
play_width = 300  # meaning 300 // 10 = 30 width per block          size of box
play_height = 600  # meaning 600 // 20 = 20 height per block
block_size = 30

top_left_x = (s_width - play_width) // 2
top_left_y = s_height - play_height

# SHAPE FORMATS

S = [['.....',
      '......',
      '..00..',
      '.00...',
      '.....'],
     ['.....',
      '..0..',
      '..00.',
      '...0.',
      '.....']]

Z = [['.....',
      '.....',
      '.00..',
      '..00.',
      '.....'],
     ['.....',
      '..0..',
      '.00..',
      '.0...',
      '.....']]

I = [['..0..',
      '..0..',
      '..0..',
      '..0..',
      '.....'],
     ['.....',
      '0000.',
      '.....',
      '.....',
      '.....']]

O = [['.....',
      '.....',
      '.00..',
      '.00..',
      '.....']]

J = [['.....',
      '.0...',
      '.000.',
      '.....',
      '.....'],
     ['.....',
      '..00.',
      '..0..',
      '..0..',
      '.....'],
     ['.....',
      '.....',
      '.000.',
      '...0.',
      '.....'],
     ['.....',
      '..0..',
      '..0..',
      '.00..',
      '.....']]

L = [['.....',
      '...0.',
      '.000.',
      '.....',
      '.....'],
     ['.....',
      '..0..',
      '..0..',
      '..00.',
      '.....'],
     ['.....',
      '.....',
      '.000.',
      '.0...',
      '.....'],
     ['.....',
      '.00..',
      '..0..',
      '..0..',
      '.....']]

T = [['.....',
      '..0..',
      '.000.',
      '.....',
      '.....'],
     ['.....',
      '..0..',
      '..00.',
      '..0..',
      '.....'],
     ['.....',
      '.....',
      '.000.',
      '..0..',
      '.....'],
     ['.....',
      '..0..',
      '.00..',
      '..0..',
      '.....']]

shapes = [S, Z, I, O, J, L, T]
shape_colors = [(0, 255, 0), (255, 0, 0), (0, 255, 255), (255, 255, 0), (255, 165, 0), (0, 0, 255), (128, 0, 128)]


# index 0 - 6 represent shape


class Piece(object):
    def __init__(self,x,y,shape):
        self.x=x
        self.y=y
        self.shape=shape
        self.color= shape_colors[shapes.index(shape)]
        self.rotation=0


def create_grid(locked_positions={}):
    grid = [[(0,0,0) for x in range(10)] for x in range(20)]        #pelas 20 linhas crio sublista c 10 cores cada
    for i in range(len(grid)):  #linhas
        for j in range(len(grid[i])):       #colunas
            if(j,i) in locked_positions:
                c= locked_positions[(j,i)]
                grid[i][j]=c
    return grid

def convert_shape_format(shape):
    positions=[]
    format = shape.shape[shape.rotation % len(shape.shape)]     #rotação 1 % 4 = segunda sublista

    for i, line in enumerate(format):
        row= list(line)
        for j, column in enumerate(row):
            if column == '0':
                positions.append((shape.x +j, shape.y +i))

    for i,pos in enumerate(positions):
        positions[i]=(pos[0]-2,pos[1]-4)        #move tudo um pouco para cima e esquerda

    return positions

def valid_space(shape, grid):
    accepted_pos=[[(j,i) for j in range(10) if grid[i][j] == (0,0,0)] for i in range(20)]        # [[(0,1)],[(2,3)]] --> [(0,1),(2,3)]
    accepted_pos = [j for sub in accepted_pos for j in sub]                                      # flattens the list
    formatted = convert_shape_format(shape)

    for pos in formatted:
        if pos not in accepted_pos:
            if pos[1] >-1:              #
                return False
    return True


def check_lost(positions):
    for pos in positions:
        x,y=pos
        if y<1:
            return True
    return False


def get_shape():
    return Piece(5,0,random.choice(shapes))


def draw_text_middle(text, size, color, surface):
    font= pygame.font.SysFont("arial",size,bold=True)
    label= font.render(text,1,color)
    surface.blit(label,(top_left_x+play_width/2 -(label.get_width()/2),top_left_y+ play_height/2-(label.get_height()/2)))


def draw_grid(surface, grid):
    sx=top_left_x
    sy=top_left_y

    for i in range(len(grid)):
        pygame.draw.line(surface, (128,128,128), (sx, sy+i*block_size),(sx+play_width,sy+i*block_size))
        for j in range(len(grid[i])):
            pygame.draw.line(surface, (128, 128, 128), (sx+j*block_size, sy ),(sx+j*block_size , sy+ play_height))

    


def clear_rows(grid, locked):
    inc=0                                   #indica quantas rows foram apagadas para o shift
    for i in range(len(grid)-1,-1,-1):      #loop througth grid backwards
        row=grid[i]
        if(0,0,0) not in row:               # esta toda a linha pintada
            inc+=1
            ind=i
            for j in range(len(row)):
                try:
                    del locked[(j,i)]               #apaga a linha completada
                except:
                    continue
    if inc>0:
        for key in sorted(list(locked), key=lambda x:x[1])[::-1]:       # [(0,1), (0,0)]---> [(0,0),(0,1)]  de baixo para cima para não haver overwrite
            x,y=key
            if y<ind:                               #se esta a cima da linha apagada
                newKey=(x,y+inc)                    #shift para baixo
                locked[newKey]= locked.pop(key)
    return inc


def draw_next_shape(shape, surface):
    font = pygame.font.SysFont('arial',30)
    label =font.render('Next shape',1,(255,255,255))

    sx = top_left_x + play_width +50
    sy= top_left_y + play_height/2 -100
    format =shape.shape[shape.rotation %len(shape.shape)]

    for i ,line in enumerate(format):
        row= list(line)
        for j,column in enumerate(row):
            if column =='0':
                pygame.draw.rect(surface,shape.color,(sx+j*block_size,sy+i*block_size,block_size,block_size),0)

    surface.blit(label,(sx+10,sy-30))


def draw_window(surface,grid,score=0,last_score=0):
    surface.fill((0, 0, 0))

    pygame.font.init()
    font = pygame.font.SysFont('arial', 60)
    label = font.render('Best Game Ever', 1, (255, 255, 255))  # text
    surface.blit(label, (top_left_x + play_width / 2 - label.get_width() / 2, 30))  # memoria

    #current score
    font = pygame.font.SysFont('arial', 30)
    label = font.render('Score : '+ str(score), 1, (255, 255, 255))

    sx = top_left_x + play_width + 50
    sy = top_left_y + play_height / 2 - 100

    surface.blit(label,(sx+20,sy + 160))

    #last score
    label = font.render('High Score : ' + str(last_score), 1, (255, 255, 255))

    sx = top_left_x - 200
    sy = top_left_y + 200

    surface.blit(label, (sx + 20, sy + 160))

    for i in range(len(grid)):
        for j in range(len(grid[i])):
            pygame.draw.rect(surface, grid[i][j], (top_left_x + j*block_size, top_left_y + i*block_size,block_size,block_size),0)

    pygame.draw.rect(surface, (128,0,128), (top_left_x,top_left_y,play_width, play_height), 4)

    draw_grid(surface,grid)

def update_score(nscore):
    score= max_score()
    with open('scores.txt','w') as f:
        if nscore>int(score):
            f.write(str(nscore))
        else:
            f.write(str(score))

def max_score():
    with open('scores.txt','r') as f:
        lines=f.readlines()
        score= lines[0].strip()

    return score

def main(win):
    last_score=max_score()
    locked_positions = {}
    grid = create_grid(locked_positions)

    change_piece = False
    run=True
    current_piece=get_shape()
    next_piece=get_shape()
    clock= pygame.time.Clock()
    fall_time=0
    fall_speed= 0.27
    level_time=0
    score=0

    while run:
        grid =create_grid(locked_positions)
        fall_time += clock.get_rawtime()            #qto tempo passou desde o ultimo tick ---> ms
        level_time+= clock.get_rawtime()
        clock.tick()

        #increases the speed of the piece during the game
        if level_time/1000>5:
            level_time=0
            if fall_speed> 0.12:
                fall_speed-=0.005

        if fall_time/1000 >fall_speed:
            fall_time=0
            current_piece.y +=1
            if not(valid_space(current_piece,grid)) and current_piece.y>0:
                current_piece.y-=1
                change_piece =True

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run= False
                pygame.display.quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    current_piece.x+=1
                    if not(valid_space(current_piece,grid)):
                        current_piece.x-=1
                if event.key == pygame.K_LEFT:
                    current_piece.x-=1
                    if not(valid_space(current_piece,grid)):
                        current_piece.x+=1
                if event.key == pygame.K_DOWN:
                    current_piece.y+=1
                    if not(valid_space(current_piece,grid)):
                        current_piece.y-=1
                if event.key == pygame.K_UP:
                    current_piece.rotation+=1
                    if not(valid_space(current_piece,grid)):
                        current_piece.rotation-=1

        shape_pos = convert_shape_format(current_piece)

        for i in range(len(shape_pos)):
            x,y=shape_pos[i]
            if y>-1:
                grid[y][x] =current_piece.color

        if change_piece:
            for pos in shape_pos:               # lockedpositions --> {(1,2):(255,0,0)}
                p=(pos[0],pos[1])
                locked_positions[p] =current_piece.color
            current_piece=next_piece
            next_piece= get_shape()
            change_piece=False
            score+= clear_rows(grid,locked_positions)*10

        draw_window(win,grid,score,last_score)
        draw_next_shape(next_piece, win)
        pygame.display.update()

        if check_lost(locked_positions):
            draw_text_middle("U SUCK!",80,(255,255,255),win)
            pygame.display.update()
            pygame.time.delay(1500)
            run=False
            update_score(score)


def main_menu(win):
    run=True
    while run:
        win.fill((0,0,0))
        draw_text_middle("Press any key",60,(255,255,255),win)
        pygame.display.update()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run= False
            if event.type == pygame.KEYDOWN:
                main(win)
    pygame.display.quit()


win = pygame.display.set_mode((s_width, s_height))
pygame.display.set_caption('Tetris')
main_menu(win)  # start game