/*
 * Copyright © 2012 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners.
 * See LICENSE.TXT for license information.
 */

package com.nokia.example.drumkit.components;

import com.nokia.example.drumkit.Main;
import com.nokia.example.drumkit.SoundManager;
import com.nokia.mid.ui.multipointtouch.MultipointTouch;
import com.nokia.mid.ui.multipointtouch.MultipointTouchListener;
import java.util.Hashtable;
import javax.microedition.lcdui.game.GameCanvas;

public abstract class DrumKit extends GameCanvas {
    protected SoundManager soundManager;
    protected SoundManager.Listener soundManagerListener;
    
    protected Button[] buttons;
    protected static final int STOP_BUTTON = 0;
    protected static final int PLAY_BUTTON = 1;
    protected static final int REC_BUTTON = 2;
    protected static final int EXIT_BUTTON = 3;
    protected static final int MODE_BUTTON = 4;
    
    protected Drum[] drums;
    
    protected boolean paintBackground;
    protected boolean kitEnabled;
    protected boolean painting;    
    protected Thread painterThread;
    
    private Hashtable multipointTouchIdToDrumMap;
    protected Object multipointTouchListener;
    
    /**
     * Constructor.
     * @param sm The sound manager instance.
     */
    public DrumKit(SoundManager sm) {
        super(false);
        this.soundManager = sm;
        kitEnabled = false;
        paintBackground = true;
        setFullScreenMode(true);
        
        soundManagerListener = new SoundManager.Listener() {
            public void soundPlayed(int index) {
                if (kitEnabled) {
                    for (int i = 0; i < drums.length; i++) {
                        if (drums[i].getIndex() == index) {
                            drums[i].hit();
                            repaint();
                        }
                    }
                }
            }

            public void playingStarted() {
                paintBackground = true;
                buttons[STOP_BUTTON].setVisible(true);
                buttons[REC_BUTTON].setVisible(false);
                buttons[PLAY_BUTTON].disable();
                repaint();
            }

            public void playingStopped() {
                paintBackground = true;
                buttons[STOP_BUTTON].setVisible(false);
                buttons[REC_BUTTON].setVisible(true);
                buttons[PLAY_BUTTON].enable();
                repaint();
            }

            public void recordingStarted() {
                paintBackground = true;
                buttons[STOP_BUTTON].setVisible(true);
                buttons[STOP_BUTTON].enable();
                buttons[REC_BUTTON].setVisible(false);
                buttons[PLAY_BUTTON].disable();
                repaint();
            }

            public void recordingStopped() {
                paintBackground = true;
                buttons[STOP_BUTTON].setVisible(false);
                buttons[REC_BUTTON].setVisible(true);
                buttons[PLAY_BUTTON].enable();
                repaint();
            }
        }; 
        
        try {
            Class.forName("com.nokia.mid.ui.multipointtouch.MultipointTouch");
            multipointTouchIdToDrumMap = new Hashtable();
            
            multipointTouchListener = new MultipointTouchListener() {
                public void pointersChanged(int[] pointerIds) {
                    if (!kitEnabled) {
                        return;
                    }
                    
                    int pointerId;
                    int x;
                    int y;
                    int state;

                    // Loop through the changed touch points
                    for (int i = 0; i < pointerIds.length; i++) {
                        // Get the touch point ID
                        pointerId = pointerIds[i];

                        // Get the touch point state
                        state = MultipointTouch.getState(pointerId);

                        // Get the touch point x and y coordinates
                        x = MultipointTouch.getX(pointerId);
                        y = MultipointTouch.getY(pointerId);

                        // Handle the UI update based on the touch point state,
                        // ID, and coordinates
                        switch (state) {
                            case MultipointTouch.POINTER_PRESSED: {
                                Button b = checkButtonsPressed(x, y);
                                if (b != null) {
                                    b.pointerPressed(x, y);
                                    return;
                                }

                                Drum drum = getDrumAt(x, y);
                                if (drum != null) {
                                    multipointTouchIdToDrumMap.put(new Integer(pointerId), drum);
                                    soundManager.playSound(drum.getIndex());
                                }                    
                                repaint();
                                break;
                            }
                            case MultipointTouch.POINTER_DRAGGED: {
                                Button b = checkButtonsPressed(x, y);
                                if (b != null) {
                                    return;
                                }

                                // Check if the user has dragged over another drum
                                Drum drum = getDrumAt(x, y);
                                Drum prevDrum = ((Drum) multipointTouchIdToDrumMap.get(new Integer(pointerId)));
                                if (drum != null && prevDrum != drum) {
                                    multipointTouchIdToDrumMap.put(new Integer(pointerId), drum);
                                    soundManager.playSound(drum.getIndex());
                                    repaint();
                                }
                                break;
                            }
                            case MultipointTouch.POINTER_RELEASED:
                                checkButtonsReleased(x, y);
                                multipointTouchIdToDrumMap.remove(new Integer(pointerId));
                                repaint();
                                break;
                        }
                    }
                }
            };
        }
        catch (ClassNotFoundException e) {
        }
    }
    
    /**
     * Handle show events
     */
    public void showNotify() {
        painting = true;
        
        // Start a painter thread that monitors changed components and triggers
        // a repaint when needed.
        painterThread = new Thread() {
            public void run() {
                while (painting) {
                    for (int i = 0; i < drums.length; i++) {
                        if (drums[i].needsRepaint()) {
                            repaint();
                            break;
                        }
                    }
                    
                    try {
                        Thread.sleep(50);
                    }
                    catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        
        painterThread.start();
        paintBackground = true;
        kitEnabled = true;
        soundManager.addListener(soundManagerListener);
        
        if (multipointTouchListener != null ) {
            MultipointTouch.getInstance().addMultipointTouchListener(
                    (MultipointTouchListener)multipointTouchListener);
        }
        
        super.showNotify();
    }
    
    /**
     * Handle hide events
     */
    public void hideNotify() {
        kitEnabled = false;
        painting = false;
        soundManager.removeListener(soundManagerListener);
        
        if (multipointTouchListener != null ) {
            MultipointTouch.getInstance().removeMultipointTouchListener(
                    (MultipointTouchListener)multipointTouchListener);
        }
    }
    
    /**
     * Checks pressed buttons.
     * 
     * @return pressed button or null if no buttons were pressed.
     */    
    protected Button checkButtonsPressed(int x, int y) {
        for (int i = 0; i < buttons.length; i++) {
            if (buttons[i].contains(x, y)
                    && buttons[i].isEnabled()
                    && buttons[i].isVisible())
            {
                buttons[i].pointerPressed(x, y);
                paintBackground = true;
                return buttons[i];
            }
        }
        
        return null;
    }

    /**
     * Checks released buttons.
     * 
     * @return true if a button was released, false if not.
     */       
    protected boolean checkButtonsReleased(int x, int y) {
        for (int i = 0; i < buttons.length; i++) {
            if (buttons[i].contains(x, y)
                    && buttons[i].isEnabled()
                    && buttons[i].isVisible())
            {
                buttons[i].pointerReleased(x, y);
                return true;
            }
        }
        
        return false;
    }

    /**
     * Handle button clicks
     *
     * @param button
     */
    public void clicked(Button button) {
        if (button == buttons[EXIT_BUTTON]) {
            Main.getInstance().destroyApp(true);
            Main.getInstance().notifyDestroyed();
        }
        
        if (button == buttons[REC_BUTTON]) {
            soundManager.startRecording();
        }
        
        if (button == buttons[PLAY_BUTTON]) {
            soundManager.playRecord();
        }
        
        if (button == buttons[STOP_BUTTON]) {
            soundManager.stop();
        }
        
        if (button == buttons[MODE_BUTTON]) {
            soundManager.stop();
            Main.getInstance().switchKit();
        }
    }
    
    /**
     * Returns drum at specified coordinates.
     *
     * @param x
     * @param y
     */
    public Drum getDrumAt(int x, int y) {
        for (int i = 0; i < drums.length; i++) {
            if (drums[i] != null) {
                if (drums[i].isHit(x, y)) {
                    return drums[i];
                }
            }
        }
        
        return null;
    }
}
