from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ObjectProperty, BooleanProperty
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.clock import Clock
from random import choice
from kivy.metrics import sp, dp
from kivy.core.audio import SoundLoader
from kivy.storage.jsonstore import JsonStore


from pipe import Pipe
from stickman import StickMan


class Background(Widget):
    floor_texture = ObjectProperty(None)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # Create textures
        self.floor_texture = Image(source="assets/floor.png").texture
        self.floor_texture.wrap = 'repeat'
        self.floor_texture.uvsize = (Window.width / self.floor_texture.width, -1)

    def scroll_textures(self, time_passed):
        # update pos
        self.floor_texture.uvpos = ((self.floor_texture.uvpos[0] + time_passed * 4) % Window.width, self.floor_texture.uvpos[1])

        # redraw textures
        texture = self.property('floor_texture')
        texture.dispatch(self)


class MainApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.pipes = []
        self.GRAVITY = Window.height * 0.8
        self.die_flag = 0
        self.game_active = False
        self.speed = 1.2
        self.floor_height = Window.height / 9
        self.score = 0
        self.pause = False
        self.icon = 'ico.png'
        # play sound
        self.soundtrack = SoundLoader.load('sounds/soundtrack.wav')
        self.soundtrack.play()
        self.soundtrack.loop = True
        self.soundtrack.volume = 0.45

        # store data
        self.store = JsonStore('highscore.json')
        self.skins = self.store.get('skins')['id']

        self.shop_page = 1

    # stickman movement
    def move_stickman(self, time_passed):
        stickman = self.root.ids.stickman
        stickman.y = stickman.y + stickman.velocity * time_passed * 5

        if stickman.y <= self.floor_height - stickman.sliding:
            stickman.y = self.floor_height - stickman.sliding
            stickman.velocity = 0
            stickman.double_jump = 0

        if stickman.top > Window.height:
            stickman.top = Window.height
            stickman.velocity = 0
        else:
            stickman.velocity = stickman.velocity - self.GRAVITY * time_passed

        self.check_collision()

        # if window size change
        self.GRAVITY = Window.height * 0.8
        self.floor_height = Window.height / 9

    def update_score(self):
        self.score += int(round(10 ** self.speed, 0))
        if self.root.ids.score.text[0] != "Y" and self.root.ids.score.text[0] != "N":
            if self.score > 1000:
                temp = round(self.score / 1000, 1)
                self.root.ids.score.text = str(temp) + 'K'
            else:
                self.root.ids.score.text = str(self.score)

    def update_speed(self):
        if self.speed <= 1.46:
            self.speed += 0.00006

    # checking collisions
    def check_collision(self):
        stickman = self.root.ids.stickman
        if stickman.x < -stickman.size[0]:
            if stickman.lifes:
                stickman.pos = (Window.width / 6, self.floor_height)
                stickman.lifes = False
                self.root.ids.lifes.text = "LIFES: 1"
            else:
                self.root.ids.lifes.text = "GAME OVER"
                self.game_over()
        if -stickman.size[0] < stickman.x < Window.width / 6:
            stickman.x += dp(0.5)

        for pipe in self.pipes:
            if pipe.collide_widget(stickman):
                # bottom pipe
                if stickman.y < (pipe.pipe_center - pipe.GAP_SIZE / 2.0):
                    if stickman.x > pipe.x + 3 * pipe.size[0] / 4:
                        stickman.x = pipe.x + 3 * pipe.size[0] / 4
                        stickman.x += pipe.size[0] / 4
                    else:
                        stickman.x = pipe.x - stickman.size[0]
                # top pipe
                if stickman.top > (pipe.pipe_center + pipe.GAP_SIZE / 2.0):
                    stickman.x = pipe.x - stickman.size[0]

    # game is over
    def game_over(self):
        stickman = self.root.ids.stickman

        # game is not active
        self.game_active = False
        stickman.game_active = False
        stickman = self.root.ids.stickman

        # show buttons
        self.root.ids.start_button.disabled = False
        self.root.ids.start_button.opacity = 1
        self.root.ids.quit_button.disabled = False
        self.root.ids.quit_button.opacity = 1
        self.root.ids.shop_button.disabled = False
        self.root.ids.shop_button.opacity = 1
        self.root.ids.help_button.disabled = False
        self.root.ids.help_button.opacity = 1
        self.root.ids.pause_button.disabled = True

        # save highscore and points
        self.root.ids.score.text = "YOUR SCORE: {}".format(self.score)
        self.save_highscore()
        self.save_points()

        # delete pipes
        for pipe in self.pipes:
            self.root.remove_widget(pipe)
        self.frames.cancel()
        stickman.stop_current_action()

        # hide stickman, reset lifes
        self.speed = 1.2
        stickman.pos = (-2 * stickman.size[1], self.floor_height)
        stickman.lifes = 2
        self.die_flag = 0

    # what to do in next frame
    def next_frame(self, time_passed):
        self.move_stickman(time_passed)
        self.move_pipes(time_passed)
        self.root.ids.background.scroll_textures(time_passed)
        self.update_score()
        self.update_speed()

    # start the game
    def start_game(self):
        if not self.game_active:
            stickman = self.root.ids.stickman
            stickman.pos = (dp(60), self.floor_height)

            # game is active
            self.game_active = True
            stickman.game_active = True

            # hide buttons
            self.root.ids.pause_button.disabled = False
            self.root.ids.quit_button.disabled = True
            self.root.ids.quit_button.opacity = 0
            self.root.ids.shop_button.disabled = True
            self.root.ids.shop_button.opacity = 0
            self.root.ids.help_button.disabled = True
            self.root.ids.help_button.opacity = 0
            self.root.ids.left_button.disabled = True
            self.root.ids.right_button.disabled = True
            self.pipes = []

            # start generating frames, set variables
            self.frames = Clock.schedule_interval(self.next_frame, 1 / 60.)
            self.root.ids.score.text = str(0)
            self.root.ids.lifes.text = "LIFES: 2"
            self.score = 0

            # pipes
            num_pipes = 30
            factor = min(self.speed, 1.1)
            distance_between_pipes = Window.width / (num_pipes - 28) * 2 * factor

            # show and hide labels
            self.root.ids.score.opacity = 1
            self.root.ids.highscore.opacity = 0
            self.root.ids.score_coins.opacity = 0
            self.root.ids.lifes.opacity = 1

            # generate pipes
            for i in range(num_pipes):
                pipe = Pipe()
                pipe.pipe_center = choice([((self.root.height) / 9), ((self.root.height) / 2), (5*self.root.height)/6])
                pipe.size_hint = (None, None)
                pipe.pos = (Window.width + i * distance_between_pipes, self.floor_height)
                pipe.size = (dp(64), self.root.height - self.floor_height)

                self.pipes.append(pipe)
                self.root.add_widget(pipe)

    def move_pipes(self, time_passed):
        # scroll pipes
        for pipe in self.pipes:
            pipe.x -= dp(time_passed * 100 * 6 * self.speed)

        num_pipes = 30
        factor = min(self.speed, 1.1)
        distance_between_pipes = Window.width / (num_pipes - 28) * 2 * factor

        pipe_xs = list(map(lambda pipe: pipe.x, self.pipes))
        right_most_x = max(pipe_xs)

        if right_most_x <= Window.width - distance_between_pipes:
            most_left_pipe = self.pipes[pipe_xs.index(min(pipe_xs))]
            most_left_pipe.x = Window.width

    def mute_sound(self):
        if self.soundtrack.volume > 0:
            self.soundtrack.volume = 0
            self.root.ids.mute_button.text = "UNMUTE"
        else:
            self.soundtrack.volume = 0.45
            self.root.ids.mute_button.text = "MUTE"

    def save_highscore(self):
        actual_highscore = self.store.get('highscore')['score']
        if self.score > actual_highscore:
            self.store.put('highscore', score=self.score)
            self.root.ids.score.text = "NEW HIGH SCORE: {}".format(self.score)
            self.root.ids.score_coins.opacity = 1
            self.root.ids.score_coins.text = "COINS +{}".format(self.score//1000)
        else:
            self.root.ids.score.text = "YOUR SCORE: {}".format(self.score)
            self.root.ids.highscore.opacity = 1
            self.root.ids.highscore.text = "HIGH SCORE: {}".format(actual_highscore)
            self.root.ids.score_coins.opacity = 1
            self.root.ids.score_coins.text = "COINS +{}".format(self.score//1000)

    def save_points(self):
        actual_points = self.store.get('coins')['points']
        actual_points += self.score//1000
        self.store.put('coins', points=actual_points)

    def pause_game(self):
        stickman = self.root.ids.stickman
        if not self.pause:
            self.root.ids.pause_button.text = "UNPAUSE"
            self.root.ids.score.text = "GAME PAUSED"
            self.frames.cancel()
            stickman.frames.cancel()
            self.pause = True
            stickman.pause = True
        else:
            self.pause = False
            stickman.pause = False
            self.root.ids.pause_button.text = "PAUSE"
            self.frames = Clock.schedule_interval(self.next_frame, 1 / 60.)
            stickman.start_run()

    def shop(self):
        # hide buttons and score label
        self.root.ids.quit_button.disabled = True
        self.root.ids.quit_button.opacity = 0
        self.root.ids.start_button.disabled = True
        self.root.ids.start_button.opacity = 0
        self.root.ids.shop_button.disabled = True
        self.root.ids.shop_button.opacity = 0
        self.root.ids.help_button.disabled = True
        self.root.ids.help_button.opacity = 0
        self.root.ids.score.opacity = 0
        self.root.ids.score_coins.opacity = 0
        self.root.ids.highscore.opacity = 0

        # show return button
        self.root.ids.return_button.disabled = False
        self.root.ids.return_button.opacity = 1

        # show shop menu
        self.shop_page = 1
        self.root.ids.coins.opacity = 1
        self.root.ids.lifes.opacity = 1
        coins = self.store.get('coins')['points']
        self.root.ids.lifes.text = "COINS: {}".format(coins)
        self.update_shop_labels()

        # show buttons
        self.root.ids.right_button.opacity = 1
        self.root.ids.right_button.disabled = False
        self.root.ids.right_button.pos = self.root.width - self.root.ids.right_button.width - dp(20), self.root.height/2

        self.root.ids.shop0.opacity = 1
        self.root.ids.shop0.disabled = False
        self.root.ids.shop0.pos[1] += dp(1000)
        self.root.ids.shop0.background_normal = 'assets/shop/shop0.png'
        self.root.ids.shop0.background_down = 'assets/shop/shop0.png'

        self.root.ids.shop1.opacity = 1
        self.root.ids.shop1.disabled = False
        self.root.ids.shop1.pos[1] += dp(1000)
        self.root.ids.shop1.background_normal = 'assets/shop/shop1.png'
        self.root.ids.shop1.background_down = 'assets/shop/shop1.png'

        self.root.ids.shop2.opacity = 1
        self.root.ids.shop2.disabled = False
        self.root.ids.shop2.pos[1] += dp(1000)
        self.root.ids.shop2.background_normal = 'assets/shop/shop2.png'
        self.root.ids.shop2.background_down = 'assets/shop/shop2.png'

    def return_button(self):
        self.root.ids.quit_button.disabled = False
        self.root.ids.quit_button.opacity = 1
        self.root.ids.start_button.disabled = False
        self.root.ids.start_button.opacity = 1
        self.root.ids.shop_button.disabled = False
        self.root.ids.shop_button.opacity = 1
        self.root.ids.help_button.disabled = False
        self.root.ids.help_button.opacity = 1
        self.root.ids.score.opacity = 1
        self.root.ids.highscore.opacity = 1

        self.root.ids.return_button.disabled = True
        self.root.ids.return_button.opacity = 0

        if self.root.ids.help_button1.opacity > 0:
            self.root.ids.help_button1.opacity = 0
            self.root.ids.help_button1.disabled = True

            self.root.ids.help_button2.opacity = 0
            self.root.ids.help_button2.disabled = True

        if self.root.ids.coins.opacity > 0:
            self.root.ids.coins.opacity = 0
            self.root.ids.right_button.opacity = 0
            self.root.ids.right_button.disabled = True
            self.root.ids.right_button.pos = self.root.width, 0
            self.root.ids.left_button.opacity = 0
            self.root.ids.left_button.disabled = True
            self.root.ids.left_button.pos = self.root.width, 0
            self.root.ids.shop0.opacity = 0
            self.root.ids.shop0.disabled = True
            self.root.ids.shop0.pos[1] -= dp(1000)

            self.root.ids.shop1.opacity = 0
            self.root.ids.shop1.disabled = True
            self.root.ids.shop1.pos[1] -= dp(1000)

            self.root.ids.shop2.opacity = 0
            self.root.ids.shop2.disabled = True
            self.root.ids.shop2.pos[1] -= dp(1000)

    def choose_or_buy(self, button_id):
        stickman = self.root.ids.stickman
        skins = self.skins
        coins = self.store.get('coins')['points']
        page = self.shop_page
        offset = (page - 1) * 3
        factor = 1 if page == 1 else offset
        if page > 1:
            button_id += 1
        price = button_id * 100 * factor
        if page > 1:
            button_id -= 1
        # if u have u can set
        if self.skins[button_id + offset] == "1":
            stickman.skin_id = button_id + offset
            self.store.put('last', id=button_id)
        else:
            if coins >= price:
                # save skin list
                self.skins = skins[0:button_id + offset] + "1" + skins[button_id + offset + 1:]
                self.store.put('skins', id=self.skins)
                # reduce coins
                coins -= price
                self.store.put('coins', points=coins)
                # set skin
                stickman.skin_id = button_id + offset
                # change coins number
                self.root.ids.lifes.text = "COINS: {}".format(coins)
                self.store.put('last', id=button_id+offset)
        self.update_shop_labels()

    def update_shop_labels(self):
        page = self.shop_page
        offset = (page - 1) * 3
        if page == 1:
            self.root.ids.right_button.opacity = 1
            self.root.ids.right_button.disabled = False

            self.root.ids.left_button.opacity = 0
            self.root.ids.left_button.disabled = True

            self.root.ids.shop0.text = "CLASSIC"
            self.root.ids.shop0.background_normal = 'assets/shop/shop0.png'
            self.root.ids.shop0.background_down = 'assets/shop/shop0.png'

            self.root.ids.shop1.text = "BLUE - BOUGHT" if self.skins[1] == '1' else "BLUE - 100"
            self.root.ids.shop1.background_normal = 'assets/shop/shop1.png'
            self.root.ids.shop1.background_down = 'assets/shop/shop1.png'

            self.root.ids.shop2.text = "SILVER - BOUGHT" if self.skins[2] == '1' else "SILVER - 200"
            self.root.ids.shop2.background_normal = 'assets/shop/shop2.png'
            self.root.ids.shop2.background_down = 'assets/shop/shop2.png'
        if page == 2:
            self.root.ids.left_button.opacity = 1
            self.root.ids.left_button.disabled = False

            self.root.ids.right_button.opacity = 0
            self.root.ids.right_button.disabled = True

            self.root.ids.shop0.text = "FADE - BOUGHT" if self.skins[offset] == '1' else "FADE - 300"
            self.root.ids.shop0.background_normal = 'assets/shop/shop3.png'
            self.root.ids.shop0.background_down = 'assets/shop/shop3.png'

            self.root.ids.shop1.text = "GOLD - BOUGHT" if self.skins[offset+1] == '1' else "GOLD - 600"
            self.root.ids.shop1.background_normal = 'assets/shop/shop4.png'
            self.root.ids.shop1.background_down = 'assets/shop/shop4.png'

            self.root.ids.shop2.text = "DIAMOND - BOUGHT" if self.skins[offset+2] == '1' else "DIAMOND - 900"
            self.root.ids.shop2.background_normal = 'assets/shop/shop5.png'
            self.root.ids.shop2.background_down = 'assets/shop/shop5.png'


if __name__ == '__main__':
    MainApp().run()
