'''
Created on 23 juil. 2009

@author: Samuel Benveniste
'''
from math import floor, ceil
import pygame
import pygame.midi
import sys
import colorsys
import constants
from gradients import gradients
from logging.PickleableEvent import PickleableEvent
from instruments.Instrument import Instrument
from cursor.WarpingCursor import *
from controllers.Wiimote import Wiimote
from logging.EventLog import EventLog


class StaticFamiliarizer:
    '''
    The screen on which the game is played
    
        wiimotes: 
                The wiimotes used in this session
        window:
            The main display window
        screen:
            The main display surface
        clock:
            The clock used to animate the screen
        savedScreen:
            The background that is painted every time
        playerScreen:
            The buffer for painting everything before bliting
        width:
            The width of the window in pixels
        height:
            The height of the window in pixels
        extendScale :
            True if the scale is G to C instead of C to C
        cascade:
            True if crossing from note to note with a button pressed triggers a new note
        scaleSize:
            The size of the scale used
        cursorPositions:
            The positions of the cursors on the screen, in pixels
    '''
    
    
    
    def __init__(self, wiimotes, window, screen, clock, joys, portOffset,activeWiimotes,replay = False, level = 0, defaultInstrumentChannel = 16, defaultNote = 60, eventLog = None):
        '''
        Constructor
        '''
        self.firstClickTime = None
        self.firstClickInTime = None
        self.duration = None
        self.clicks = 0
        self.clicksIn = 0
        
        pygame.font.init()
        self.font = pygame.font.Font(None,60)
        self.congratulations = ["Bien !","Tres Bien !","Bravo !","Excellent !","Felicitations !"]
        self.renderedCongratulations = [self.font.render(congratulation,False,(0,0,0)) for congratulation in self.congratulations]
        self.congratulationCount = None
        self.isCongratulating = False
        self.congratulationTimer = 0
        self.congratulationLength = 2000
        self.congratulationPos = None
                
        self.blinkLength = 200
        self.minimalVelocity = 64
        self.shortScaleSize = 8
        self.longScaleSize = 11
        self.borderSize = 5
        self.savedHighlightedNote = 0
        self.scaleFactor = 1
        self.wiimotes = wiimotes
        self.window = window
        self.screen = screen
        self.clock = clock
        self.width = int(floor(screen.get_width()*self.scaleFactor))
        self.height = int(floor(screen.get_height()*self.scaleFactor))
        self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2)        
        self.joys = joys
        self.portOffset = portOffset
        self.savedScreen = pygame.Surface(self.screen.get_size())
        self.savedScreen.fill((255,255,255))
        self.playerScreen = pygame.Surface(self.savedScreen.get_size())
        self.playerScreen.blit(self.savedScreen, (0, 0))
        self.cursorPositions = []
        self.level = level
        self.nextLevel = None
        self.activeWiimotes = activeWiimotes
        
        for i in range(len(self.wiimotes)):
            #Set the screen for the cursors (it can't be set before)
            self.wiimotes[i].cursor.screen = self.playerScreen
            self.cursorPositions.append(self.wiimotes[i].cursor.centerPosition)
        
        if eventLog == None:
            self.eventLog = EventLog()
            self.replay = False
        else:
            self.eventLog = eventLog
            self.replay = replay
        
        self.defaultInstrumentChannel = defaultInstrumentChannel
        self.defaultNote = defaultNote
        
        self.done = False
        self.backToInstrumentChoice = False
        self.easyMode = False

        self.noteRects = []
        self.boundingRect = None
        self.notes = []
        self.buttonDown = []
        self.velocityLock = []
        
        self.drawBackground()
        self.initializeWiimotes()
        events = pygame.event.get()
        
        #The main loop
        while not self.done :
            
            self.playerScreen.blit(self.savedScreen, (0, 0))
            
            # Limit frame speed to 50 FPS
            #
            timePassed = self.clock.tick(10000)
                
            if self.replay:
                self.eventLog.update(timePassed)
                pickledEventsToPost = self.eventLog.getPickledEvents() 
                for pickledEvent in pickledEventsToPost:
                    pygame.event.post(pickledEvent.event)
            
            events = pygame.event.get()
            
            if not self.replay:
                pickledEvents = [PickleableEvent(event.type,event.dict) for event in events]
                if pickledEvents != [] :
                    self.eventLog.appendEventGroup(pickledEvents)
            
            for event in events:
                self.input(event)
            
            if self.isCongratulating :
                self.congratulationTimer += timePassed
                if self.congratulationTimer < self.congratulationLength :
                    self.blitCongratulation()
                else :
                    self.isCongratulating = False
                            
            for i in range(len(self.wiimotes)):
                if self.activeWiimotes[i]:
                    self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])
                    if self.buttonDown[i] :
                        self.wiimotes[i].cursor.flash()
                    self.wiimotes[i].cursor.blit(self.playerScreen)
            
            self.screen.blit(self.playerScreen, (0,0))
            
            pygame.display.flip()
        
        for i in range(len(self.wiimotes)):
            if self.activeWiimotes[i]:
                if self.notes[i] != None :
                    self.wiimotes[i].stopNote(self.notes[i])
        if self.replay :
            self.duration = self.eventLog.getCurrentTime()            
        
    def drawBackground(self):
        self.savedScreen.fill((255,255,255))
        if self.level == 0 :
            A = [4]    
        else :
            A = [1,7]
        
        self.noteRects = [pygame.Rect(i * self.width / 11+self.blitOrigin[0], self.blitOrigin[1], (self.width / 11 + 1)*3, self.height+1) for i in A]
        
        #create bounding rect
        self.boundingRect = self.noteRects[0].unionall(self.noteRects)
        
        #fill the rectangles with a color gradient
        #We start with blue
        startingHue = 0.66666666666666663
        
        for rectNumber in range(len(self.noteRects)) :
            colorRatio = float(A[rectNumber]) / (11 - 1)
            #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up
            hue = startingHue * (1 - colorRatio)
            #The color of the bottom of the rectangle in hls coordinates
            bottomColorHls = (hue, 0.6, 1)
            #The color of the top of the rectangle in hls coordinates
            topColorHls = (hue, 0.9, 1)
            
            #convert to rgb ranging from 0 to 255
            bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]
            topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]
            #add transparency
            bottomColorRgb.append(255)
            topColorRgb.append(255)
            #convert to tuple
            bottomColorRgb = tuple(bottomColorRgb)
            topColorRgb = tuple(topColorRgb)            
            
            self.savedScreen.blit(gradients.vertical(self.noteRects[rectNumber].size, topColorRgb, bottomColorRgb), self.noteRects[rectNumber])
            
            pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[rectNumber], 2)
            
    def initializeWiimotes(self):
        for loop in self.wiimotes:
            if loop.port == None :
                loop.port = pygame.midi.Output(loop.portNumber)
            self.notes.append(0)
            self.buttonDown.append(False)
            self.velocityLock.append(False)
    
    def updateCursorPositionFromJoy(self, joyEvent):
        joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()
        correctedJoyId = constants.joyNames.index(joyName)
        if correctedJoyId < len(self.cursorPositions):
            if joyEvent.axis == 0 :
                self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])
            if joyEvent.axis == 1 :
                self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height()))
    
    def heightToVelocity(self, pos, controllerNumber):
        velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)
        return(velocity)
    
    def widthToNote(self, pos):
        nn = 0
        try :
            while self.noteRects[nn].collidepoint(pos) == False:
                nn = nn + 1
            return(nn)
        except(IndexError):
            return(None)
        
    def congratulate(self,targetRect,posy):
        if self.congratulationCount != None :
            if self.congratulationCount < len(self.congratulations)-1:
                self.congratulationCount += 1
        else :
            self.congratulationCount = 0
        self.congratulationTimer = 0
        self.congratulationPos = (targetRect.left+(targetRect.width-self.renderedCongratulations[self.congratulationCount].get_width())/2,posy)
        self.isCongratulating = True
        
    def resetCongratulation(self):
        self.congratulationCount = None
        self.congratulationPos = None
        self.isCongratulating = False
            
    def blitCongratulation(self):
        self.playerScreen.blit(self.renderedCongratulations[self.congratulationCount],self.congratulationPos)        
    
    def input(self, event): 
        
        print event
        
        if event.type == pygame.QUIT:
            for loop in self.wiimotes:
                del loop.port
            pygame.midi.quit()
            sys.exit(0) 
        
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_q:
                self.nextLevel = None
                self.done = True
                
            if event.key == pygame.K_w:
                self.nextLevel = 0
                self.done = True
                
            if event.key == pygame.K_e:
                self.nextLevel = 1
                self.done = True
                
            if event.key == pygame.K_r:
                self.nextLevel = 2
                self.done = True
                
            if event.key == pygame.K_t:
                self.nextLevel = 3
                self.done = True 
        
        if event.type == pygame.JOYAXISMOTION:
            
           
            joyName = pygame.joystick.Joystick(event.joy).get_name()
            correctedJoyId = constants.joyNames.index(joyName)
            if self.activeWiimotes[correctedJoyId]:
                self.updateCursorPositionFromJoy(event)
                wiimote = self.wiimotes[correctedJoyId]
                pos = self.cursorPositions[correctedJoyId]
    
                if self.buttonDown[correctedJoyId]:
                    wiimote.cursor.flash()
                    if self.notes[correctedJoyId] != None:
                        velocity = self.heightToVelocity(pos, correctedJoyId)
                        CCHexCode = wiimote.getCCHexCode()
                        wiimote.port.write_short(CCHexCode, 07, velocity)
        
        if event.type == pygame.JOYBUTTONDOWN :
            
            joyName = pygame.joystick.Joystick(event.joy).get_name()
            correctedJoyId = constants.joyNames.index(joyName)
            if self.activeWiimotes[correctedJoyId]:
                wiimote = self.wiimotes[correctedJoyId]
                pos = self.cursorPositions[correctedJoyId]
                wiimote.cursor.flash()
                if self.replay :
                    self.clicks += 1
                    if self.firstClickTime == None :
                        self.firstClickTime = self.eventLog.getCurrentTime()
    
                if not self.buttonDown[correctedJoyId]:
                    self.notes[correctedJoyId] = self.widthToNote(pos)
                    
                    velocity = self.heightToVelocity(pos, correctedJoyId)
                    
                    if self.notes[correctedJoyId] != None :            
                        wiimote.playNote(self.notes[correctedJoyId],velocity)
                        self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])
                        if self.replay :
                            self.clicksIn += 1
                            if self.firstClickInTime == None :
                                self.firstClickInTime = self.eventLog.getCurrentTime()
                    else :
                        self.resetCongratulation()
                        
                    self.buttonDown[correctedJoyId] = True
        
        if event.type == pygame.JOYBUTTONUP:
            joyName = pygame.joystick.Joystick(event.joy).get_name()
            correctedJoyId = constants.joyNames.index(joyName)
            if self.activeWiimotes[correctedJoyId]:
                wiimote = self.wiimotes[correctedJoyId]
                wiimote.stopNote(self.notes[correctedJoyId])
                self.buttonDown[correctedJoyId] = False
                self.velocityLock[correctedJoyId] = False
            
        if event.type == pygame.MOUSEMOTION:
            
            self.updateCursorPositionFromMouse(event)
            
            correctedJoyId = 0
            while not self.activeWiimotes[correctedJoyId] :
                correctedJoyId += 1
            wiimote = self.wiimotes[correctedJoyId]
            pos = self.cursorPositions[correctedJoyId]

            if self.buttonDown[correctedJoyId]:
                wiimote.cursor.flash()
                if self.notes[correctedJoyId] != None:
                    velocity = self.heightToVelocity(pos, correctedJoyId)
                    CCHexCode = wiimote.getCCHexCode()
                    wiimote.port.write_short(CCHexCode, 07, velocity)                            
        
        if event.type == pygame.MOUSEBUTTONDOWN:
            
            if event.button == 1:
                correctedJoyId = 0
                while not self.activeWiimotes[correctedJoyId] :
                    correctedJoyId += 1
                wiimote = self.wiimotes[correctedJoyId]
                pos = self.cursorPositions[correctedJoyId]
                wiimote.cursor.flash()
                if self.replay :
                    self.clicks += 1
                    if self.firstClickTime == None :
                        self.firstClickTime = self.eventLog.getCurrentTime()
    
                if not self.buttonDown[correctedJoyId]:
                    self.notes[correctedJoyId] = self.widthToNote(pos)
                    
                    velocity = self.heightToVelocity(pos, correctedJoyId)
                    
                    if self.notes[correctedJoyId] != None :            
                        wiimote.playNote(self.notes[correctedJoyId],velocity)
                        self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])
                        if self.replay :
                            self.clicksIn += 1
                            if self.firstClickInTime == None :
                                self.firstClickInTime = self.eventLog.getCurrentTime()
                    else :
                        self.resetCongratulation()
                        
                    self.buttonDown[correctedJoyId] = True
            
            if event.button == 2:
                
                self.done = True
        
        if event.type == pygame.MOUSEBUTTONUP:
            
            correctedJoyId = 0
            while not self.activeWiimotes[correctedJoyId] :
                correctedJoyId += 1
            wiimote = self.wiimotes[correctedJoyId]
            wiimote.stopNote(self.notes[correctedJoyId])
            self.buttonDown[correctedJoyId] = False
            self.velocityLock[correctedJoyId] = False
        
    def hasChanged(self):
        return(True)
    
    def updateCursorPositionFromMouse(self, mouseEvent):
        correctedJoyId = 0
        while not self.activeWiimotes[correctedJoyId] :
            correctedJoyId += 1
        self.cursorPositions[correctedJoyId] = mouseEvent.pos
                    
if __name__ == "__main__" :
    pygame.init()
    modeResolution = (1024,768)
    window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)
    pygame.font.init()
        
    pygame.midi.init()
    instruments = [Instrument(constants.scaleDict["majorScale"], i + 1, "".join(["../instruments/instrumentImages/", constants.instrumentImagePathList[i], ".jpg"]), constants.octaves[i]) for i in range(9)]
    
    joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]
    for joy in joys:
        if joy[1] in constants.joyNames:
            pygame.joystick.Joystick(joy[0]).init() 
    
    ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]
    portOffset = ports.index(constants.portNames[0])
    print(portOffset)
    
    events = pygame.event.get()
    
    screen = pygame.display.get_surface()
    clock = pygame.time.Clock()        
    cursorImages = [createImageListFromPath('../cursor/cursorImages/black', 11),createImageListFromPath('../cursor/cursorImages/red', 11)]
    durations = [75 for i in range(len(cursorImages[0]))]
    
    wiimoteCount = 1
    cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),flashImage = '../cursor/cursorImages/black/flash.png' ) for i in range(wiimoteCount)]
    wiimotes = [Wiimote(i, i + portOffset, None, instruments[i], cursors[i]) for i in range(wiimoteCount)]
    
    fam = StaticFamiliarizer(instruments, wiimotes, window, screen, clock, joys, portOffset)
    
    for loop in fam.wiimotes:
                del loop.port
    
    pygame.midi.quit()
    
    pygame.quit()  
