'''
Created on 15 juil. 2009

@author: Samuel Benveniste
'''

import pygame
import pygame.midi
import sys
import time
import pickle
import random

from numpy import array
from numpy.linalg import norm

from math import floor

from gui.constants import *
from PlayingScreen import PlayingScreen
from instruments.Instrument import Instrument
from cursor.WarpingCursor import *
from controllers.Wiimote import Wiimote
from logging.EventLog import EventLog
from logging.PickleableEvent import PickleableEvent 

joyNames = ["PPJoy Virtual joystick 1", "PPJoy Virtual joystick 2", "PPJoy Virtual joystick 3"]
portNames = ["Out To MIDI Yoke:  1"]
majorScale = [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]
minorScale = [55, 56, 58, 60, 62, 63, 65, 67, 68, 70, 72]
myxolydianScale = [55, 57, 58, 60, 62, 64, 65, 67, 69, 70, 72]
dorianScale = [55, 57, 58, 60, 62, 63, 65, 67, 69, 70, 72]
instrumentImagePathList = ["piano", "guitare", "accordeon", "violon", "flute", "tuba", "orgue", "violoncelle", "celesta"]
octaves = [0, -1, 0, 1, 1, -2, 0, -1, 0]

class Familiarizer:
    '''
    The screen for choosing instruments
    
        instruments: 
                The available instruments            
        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
        done:
            Goes to True when all instruments have been selected
        cursorPositions:
            The positions of the cursors on the screen, in pixels
        imageRects:
            The rectangles where the images of the instruments are located
        focus:
            The numbers of the instruments currently in focus
    '''
    
    def __init__(self, instruments, wiimotes, window, screen, clock, joys, portOffset, eventLog=None, replay = False, logFilePath = None, scaleFactor = 1, level = 1):
        '''
        Constructor
        
            instruments: 
                The instruments for this session           
            wiimotes: 
                The wiimotes used in this session
        '''
        
        
        self.scaleFactor = scaleFactor
        
        self.instruments = instruments
        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.currentWiimote = 0
        self.done = False        
        
        self.cursorPositions = []
        self.imageRects = []
        self.displayedInstruments = []
        self.savedImageRects = []
        self.focus = []
        self.level = level
        
        if eventLog == None:
            self.eventLog = EventLog()
            self.replay = False
        else:
            self.eventLog = eventLog
            self.replay = replay
                        
        self.savedScreen = pygame.Surface(self.screen.get_size())        
        
        self.displayedInstruments.append(0)
        if level == 1 :
            self.displayedInstruments.append(2)
        
        self.savedScreen.fill((255, 255, 255))
        for i in range(len(self.displayedInstruments)):
            self.imageRects.append(self.drawInstrument(self.displayedInstruments[i]))
                    
        self.savedImageRects = self.imageRects[:]
        
        #Draw the initial cursor on the buffer
        self.playerScreen = pygame.Surface(self.savedScreen.get_size())
        self.playerScreen.blit(self.savedScreen, (0, 0))
        
        for i in range(len(self.wiimotes)):
            #Create the list of instrument focus (one focus per wiimote)
            self.focus.append(None)
            #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)
            
        self.wiimotes[self.currentWiimote].cursor.blit(self.playerScreen)
        
        #The main loop
        while self.done == False :
            
            #Clear the cursors from the screen
            self.drawBackground()
            self.playerScreen.blit(self.savedScreen, (0, 0))
            
            # Limit frame speed to 50 FPS
            #
            timePassed = self.clock.tick(50)
            
            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 self.eventFilter(event)]
                if pickledEvents != [] :
                    self.eventLog.appendEventGroup(pickledEvents)
            
            for event in events:
                self.input(event)
            
            for i in range(len(self.wiimotes)):
                self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])
                
            self.wiimotes[self.currentWiimote].cursor.blit(self.playerScreen)  
            
            self.focus[self.currentWiimote] = None
                          
            for i in range(len(self.imageRects)) :
                if self.imageRects[i].collidepoint(self.cursorPositions[self.currentWiimote]):
                    self.focus[self.currentWiimote] = i
                                
            self.screen.blit(self.playerScreen, (0, 0))
            
            pygame.display.flip()
            
    def input(self, event):
        if event.type == pygame.QUIT:
            pygame.midi.quit()
            sys.exit()
        if event.type == pygame.JOYAXISMOTION:
            self.updateCursorPositionFromJoy(event)
        if event.type == pygame.JOYBUTTONDOWN :
            self.joyClicked(event)
        if event.type == pygame.MOUSEBUTTONDOWN:
            self.mouseClicked(event)
        if event.type == pygame.MOUSEMOTION:
            self.updateCursorPositionFromMouse(event)
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_q:
                self.done = True                            
                     
    def updateCursorPositionFromJoy(self, joyEvent):
        joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()
        correctedJoyId = 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 assignInstrumentToWiimote(self, joyEvent):
        joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()
        correctedJoyId = joyNames.index(joyName)
        if self.zoomed[correctedJoyId] == self.focus[correctedJoyId]:
            self.wiimotes[correctedJoyId].instrument = self.instruments[self.focus[correctedJoyId]]
            self.zoomed[correctedJoyId] = None
            self.imageRects = self.savedImageRects[:]
            if self.currentWiimote<len(self.wiimotes)-1:
                self.currentWiimote = self.currentWiimote+1
        else:
            self.zoomed[correctedJoyId] = self.focus[correctedJoyId]
        if self.hasFinished():
            self.done = True
    
    def updateCursorPositionFromMouse(self, mouseEvent):
        self.cursorPositions[0] = mouseEvent.pos
    
    def assignInstrumentToMouse(self, mouseEvent):
        self.wiimotes[0].instrument = self.instruments[self.focus[0]]
        if self.hasFinished():
            self.done = True
    
    def hasFinished(self):
        finished = True
        for wiimote in self.wiimotes:
            if wiimote.instrument == None:
                finished = False
        return(finished)
    
    def eventFilter(self, event):
        c = event.type
        if c == 17:
            return False
        elif c == pygame.MOUSEMOTION or pygame.MOUSEBUTTONDOWN or pygame.MOUSEBUTTONUP or pygame.JOYAXISMOTION or pygame.JOYBUTTONDOWN or pygame.JOYBUTTONUP or pygame.KEYDOWN:
            return True
        else:
            return False
        
    def drawInstrument(self,instrumentNumber,drawPos = None):
        if not drawPos :
            drawPos = array([random.randint(0,self.width/2),random.randint(0,self.height/2)])
        curImage = pygame.image.load(self.instruments[instrumentNumber].image).convert_alpha()
        scaledImage = pygame.transform.smoothscale(curImage, (self.width / 2, self.height / 2))
        imageRect = self.savedScreen.blit(scaledImage, drawPos + self.blitOrigin)
        pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), imageRect, 5)
        return imageRect           
    
    def drawBackground(self):
        self.savedScreen.fill((255, 255, 255))
        for i in range(len(self.displayedInstruments)):
            self.drawInstrument(self.displayedInstruments[i], self.imageRects[i].topleft)
            if i in self.focus:
                pygame.draw.rect(self.savedScreen, pygame.Color(0, 255, 0, 255), self.imageRects[i], 10)
    
    def mouseClicked(self,mouseEvent):
        correctedJoyId = 0
        self.wiimotes[correctedJoyId].cursor.flash(400)
        if self.focus[correctedJoyId] != None :
            self.imageRects.pop(self.focus[correctedJoyId])
            instrumentNumber = self.displayedInstruments.pop(self.focus[correctedJoyId])
            self.drawBackground()
            self.imageRects.append(self.drawInstrument(instrumentNumber))
            self.displayedInstruments.append(instrumentNumber)
            self.wiimotes[correctedJoyId].instrument = self.instruments[instrumentNumber]
            octave = self.wiimotes[correctedJoyId].instrument.octave
            noteOnHexCode = self.wiimotes[correctedJoyId].getNoteOnHexCode()
            baseTime = pygame.midi.time()
            self.wiimotes[correctedJoyId].port.write([[[noteOnHexCode,60+12*octave,127],baseTime],[[noteOnHexCode,60+12*octave,0],baseTime+500],[[noteOnHexCode,65+12*octave,100],baseTime+510],[[noteOnHexCode,65+12*octave,0],baseTime+1000]])
            self.wiimotes[correctedJoyId].instrument = None
            
    def joyClicked(self,joyEvent):
        joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()
        correctedJoyId = joyNames.index(joyName)
        self.wiimotes[correctedJoyId].cursor.flash(400)
        if self.focus[correctedJoyId] != None :
            self.imageRects.pop(self.focus[correctedJoyId])
            instrumentNumber = self.displayedInstruments.pop(self.focus[correctedJoyId])
            self.drawBackground()
            self.imageRects.append(self.drawInstrument(instrumentNumber))
            self.displayedInstruments.append(instrumentNumber)
            self.wiimotes[correctedJoyId].instrument = self.instruments[instrumentNumber]
            octave = self.wiimotes[correctedJoyId].instrument.octave
            noteOnHexCode = self.wiimotes[correctedJoyId].getNoteOnHexCode()
            baseTime = pygame.midi.time()
            self.wiimotes[correctedJoyId].port.write([[[noteOnHexCode,60+12*octave,127],baseTime],[[noteOnHexCode,60+12*octave,0],baseTime+500],[[noteOnHexCode,65+12*octave,100],baseTime+510],[[noteOnHexCode,65+12*octave,0],baseTime+1000]])
            self.wiimotes[correctedJoyId].instrument = None
                                    

def zoomRect(rect, ratio):
    zoomedRect = rect.inflate(int(floor((ratio - 1) * rect.width)), int(floor((ratio - 1) * rect.height)))
    return(zoomedRect)        

if __name__ == "__main__":
    pygame.init()
    #pygame.event.set_blocked([pygame.MOUSEBUTTONDOWN,pygame.MOUSEBUTTONUP,pygame.MOUSEMOTION])
    
    pygame.midi.init()
    instruments = [Instrument(majorScale, i + 1, "".join(["../instruments/instrumentImages/", instrumentImagePathList[i], ".jpg"]), octaves[i]) for i in range(9)]
    
    joys = [pygame.joystick.Joystick(id).get_name() for id in range(pygame.joystick.get_count())]
    joyOffset = joys.index(joyNames[0])
    pygame.joystick.Joystick(joyOffset).init()
    print(joyOffset)  
    
    ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]
    portOffset = ports.index(portNames[0])
    print(portOffset)
    
    window = pygame.display.set_mode((1280, 1024),pygame.FULLSCREEN)
    screen = pygame.display.get_surface()
    clock = pygame.time.Clock()        
    cursorImages = createImageListFromPath('../cursor/cursorImages/black', 11)
    durations = [75 for i in range(len(cursorImages))]
    
    extsc = False
    casc = False
    
    jadbt = [3, 4, 5, 3, 4, 4, 5, 6, 6, 5, 3, 3, 4, 5, 3, 4, 4, 5, 6, 7, 3]
    song = None
    
    cursors = [WarpingCursor(None, cursorImages, durations, (300 * i, 300 * i),'../cursor/cursorImages/black/flash.png') for i in range(1)]
    wiimotes = [Wiimote(i, i + portOffset, pygame.midi.Output(i+portOffset,latency = 20), None, cursors[i]) for i in range(1)]
    familiarize = Familiarizer(instruments, wiimotes, window, screen, clock, joyOffset, portOffset)
    for wiimote in wiimotes:
        del wiimote.port            
    
    pygame.midi.quit()
    sys.exit()
