/*
 * 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.views;

import java.util.Timer;
import java.util.TimerTask;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import com.nokia.example.drumkit.SoundManager;
import com.nokia.example.drumkit.components.*;
import com.nokia.example.drumkit.helpers.ImageLoader;

/**
 * Class containing the drumkit logic
 */
public class TouchAndTypeDrumkit
        extends DrumKit
        implements Button.Listener, WheelMenu.Listener
{
    private boolean recording = false;
    private int centerX = 0;
    private int centerY = 0;
    private Drum selectedDrum;
    private final WheelMenu wheel;
    private final Image backgroundImage;
    private final Image splashImage;
    private final Image titleImage;
    private Timer timer = null;
    private Timer longTapTimer = null;
    private boolean landscape = false;

    /**
     * Constructor.
     * @param soundManager The sound manager instance.
     */
    public TouchAndTypeDrumkit(SoundManager soundManager) {
        super(soundManager);
        
        if (getWidth() == 400 && getHeight() == 240) {
            landscape = true;
        }
        else {
            landscape = false;
        }
        
        if (landscape) {
            backgroundImage = ImageLoader.loadImage("/pads_landscape.png");
        }
        else {
            backgroundImage = ImageLoader.loadImage("/pads.png");
        }
        
        splashImage = ImageLoader.loadImage("/hit.png");
        titleImage = ImageLoader.loadImage("/app_name.png");

        centerX = getWidth() / 2;
        centerY = getHeight() / 2;

        createButtons();
        createDrums();

        wheel = new WheelMenu(this);
    }

    /**
     * Create UI buttons
     */
    public final void createButtons() {
        buttons = new Button[5];

        int y = getHeight() - 56;

        buttons[STOP_BUTTON] = new Button("/stop.png", "/stop_pressed.png", "/play_disabled.png", this);
        buttons[STOP_BUTTON].disable();
        buttons[PLAY_BUTTON] = new Button("/play.png", "/play_pressed.png", "/play_disabled.png", this);
        buttons[PLAY_BUTTON].disable();
        buttons[REC_BUTTON] = new Button("/record_pressed.png", "/record_pressed.png", "/record_disabled.png", this);
        buttons[EXIT_BUTTON] = new Button("/exit.png", "/exit_pressed.png", "/exit.png", this);
        buttons[MODE_BUTTON] = new Button("/padmode.png", "/padmode.png", "/padmode.png", this);
        buttons[MODE_BUTTON].setVisible(false);
        
        if (landscape) {
            buttons[STOP_BUTTON].setPosition(220, 0);
            buttons[PLAY_BUTTON].setPosition(127, 0);
            buttons[REC_BUTTON].setPosition(220, 0);
            buttons[EXIT_BUTTON].setPosition(357, 0);
            buttons[MODE_BUTTON] = new Button("/padmode.png", "/padmode.png", "/padmode.png", this);
            buttons[MODE_BUTTON].setVisible(true);        
            buttons[MODE_BUTTON].setPosition(5, 0);            
        }
        else {
            buttons[STOP_BUTTON].setPosition(9, y);
            buttons[PLAY_BUTTON].setPosition(59, y);
            buttons[REC_BUTTON].setPosition(9, y);
            buttons[EXIT_BUTTON].setPosition(getWidth() - 48, 5);
        }
    }

    /**
     * Define touch sensitive areas
     */
    public final void createDrums() {
        if (landscape) {
            drums = new Drum[7];
            drums[0] = new Drum(splashImage, SoundManager.SAMPLE_KICK, -1, 116, 3);
            drums[1] = new Drum(splashImage, SoundManager.SAMPLE_SNARE, 102, 136, 3);
            drums[2] = new Drum(splashImage, SoundManager.SAMPLE_CRASH, 45, 19, 3);
            drums[3] = new Drum(splashImage, SoundManager.SAMPLE_TOM1, 143, 52, 3);
            drums[4] = new Drum(splashImage, SoundManager.SAMPLE_TOM2, 283, 108, 3);
            drums[5] = new Drum(splashImage, SoundManager.SAMPLE_RIDE_BELL, 241, 17, 3);
            drums[6] = new Drum(splashImage, SoundManager.SAMPLE_HIHAT_CLOSED, 192, 136, 3);
        }
        else {
            drums = new Drum[4];
            drums[0] = new Drum(splashImage, SoundManager.SAMPLE_KICK, centerX - 8, centerY + 31, 3);
            drums[1] = new Drum(splashImage, SoundManager.SAMPLE_SNARE, centerX - 116, centerY - 43, 3);
            drums[2] = new Drum(splashImage, SoundManager.SAMPLE_TOM2, centerX - 5, centerY - 108, 3);
            drums[3] = new Drum(splashImage, SoundManager.SAMPLE_CRASH, centerX - 112, centerY - 165, 3);
        }
    }

    /**
     * Handle show events
     */
    public void showNotify() {
        super.showNotify();
        timer = new Timer();
    }

    /**
     * Handle hide events
     */
    public void hideNotify() {
        super.hideNotify();
        timer.cancel();
    }

    /**
     * Render graphics
     *
     * @param g
     */
    public void paint(Graphics g) {
        int anchor = Graphics.HCENTER | Graphics.VCENTER;
        g.setColor(0xf0f0f0);
        
        // Draw background
        if (landscape) {
            g.drawImage(backgroundImage, 0, 0, 0);
        }
        else {
            g.fillRect(0, 0, getWidth(), getHeight());
            g.drawImage(backgroundImage, centerX, centerY, anchor);
            g.drawImage(titleImage, centerX + 34, 28, anchor);
        }
        
        // Draw buttons
        for (int i = 0; i < buttons.length; i++) {
            buttons[i].paint(g);
        }

        // Draw drum highlights
        if (kitEnabled) {
            for (int i = 0; i < drums.length; i++) {
                if (drums[i].needsRepaint()) {
                    drums[i].draw(g);

                    // In portrait UI mode the application logo needs redrawing
                    // if the overlapping pad was highlighted.
                    if (!landscape && i == 3) {
                        g.setClip(centerX + 34 - (titleImage.getWidth() / 2), 28 - (titleImage.getHeight() / 2), titleImage.getWidth(), titleImage.getHeight());
                        g.drawImage(backgroundImage, centerX, centerY, anchor);
                        g.drawImage(titleImage, centerX + 34, 28, anchor);
                    }
                }
            }
        }

        wheel.paint(g);
    }

    /**
     * Deliver pressed events
     *
     * @param x
     * @param y
     */
    protected void pointerPressed(int x, int y) {
        if (multipointTouchListener == null) {
            Button b = checkButtonsPressed(x, y);
            
            if (b != null) {
                b.pointerPressed(x, y);
                return;
            }

            if (kitEnabled) {
                Drum d = getDrumAt(x, y);
                
                if (d != null) {
                    soundManager.playSound(d.getIndex());
                }
            }
        }
        
        if (kitEnabled) {
            Drum d = getDrumAt(x, y);
            
            if (d != null) {
                if (!recording) {
                    monitorLongTap(x, y);
                }
            }
        }

        wheel.pointerPressed(x, y);
    }

    /**
     * Deliver dragged events
     *
     * @param x
     * @param y
     */
    protected void pointerDragged(int x, int y) {
        if (multipointTouchListener == null) {
            for (int i = 0; i < buttons.length; i++) {
                buttons[i].pointerDragged(x, y);
            }
            
            if (kitEnabled) {
                Drum d = getDrumAt(x, y);
                
                if (d != null && d != selectedDrum) {
                    selectedDrum = d;
                    soundManager.playSound(d.getIndex());
                    
                    if (longTapTimer != null) {
                        longTapTimer.cancel();
                        longTapTimer = null;
                    }
                }
            }
        }
        
        wheel.pointerDragged(x, y);
    }

    /**
     * Deliver released events
     *
     * @param x
     * @param y
     */
    protected void pointerReleased(int x, int y) {
        if (multipointTouchListener == null) {
            checkButtonsReleased(x, y);

            if (kitEnabled) {
                Drum pad = getDrumAt(x, y);
                
                if (pad != null) {
                    pad.setHit(false);
                }
            }
        }
        wheel.pointerReleased(x, y);
        
        // Stop monitoring long taps
        if (longTapTimer != null) {
            longTapTimer.cancel();
            longTapTimer = null;
        }
    }

    /**
     * Custom implementation for long tap gesture
     *
     * @param x
     * @param y
     */
    private void monitorLongTap(final int x, final int y) {
        // Reset long tap timer
        if (longTapTimer != null) {
            longTapTimer.cancel();
        }
        
        longTapTimer = new Timer();
        selectedDrum = getDrumAt(x, y);

        // Task that shows prepares wheel menu to show up in the right place
        longTapTimer.schedule(
                new TimerTask() {
                    public void run() {
                        wheel.setOrigin(x, y);
                        // Calculate destination coordinates for the wheel menu
                        centerX = getWidth() / 2;
                        centerY = getHeight() / 2;
                        int destinationX = centerX - (int) (0.2 * (centerX - x));
                        int destinationY = centerY - (int) (0.5 * (centerY - y));
                        wheel.setDestination(destinationX, destinationY);
                        soundManager.stop();

                        // Disable buttons
                        for (int i = 0; i < buttons.length; i++) {
                            buttons[i].disable();
                        }

                        // Preselect a percussion and open the menu
                        wheel.select(selectedDrum.getIndex());
                        wheel.show();
                        timer = new Timer();
                        
                        timer.schedule(new TimerTask() {
                            public void run() {
                                repaint();
                            }
                        }, 0, 50);

                        kitEnabled = false;
                    }
                }, 1000);
    }

    /**
     * Handle wheelOpened event
     */
    public void wheelOpened() {
        kitEnabled = false;
        soundManager.stop();
        buttons[REC_BUTTON].disable();
        buttons[EXIT_BUTTON].disable();
        buttons[MODE_BUTTON].disable();
    }

    /**
     * Handle wheelClosed event
     */
    public void wheelClosed() {
        buttons[REC_BUTTON].enable();
        buttons[EXIT_BUTTON].enable();
        buttons[MODE_BUTTON].enable();
        kitEnabled = true;
        timer.cancel();
        repaint();
    }

    /**
     * Handle wheelItemSelected event
     */
    public void wheelItemSelected(Button button) {
        int percussion = wheel.getSelectedIndex();
        
        if (percussion > -1) {
            // Load and play the selected sample
            selectedDrum.setIndex(percussion);
            soundManager.playSound(percussion);
        }
    }
    
    /**
     * Gets drum at the specified coordinates. Drums are assumed to be circular
     * with 55 pixel radius.
     * 
     * @return pressed drum
     */    
    public Drum getDrumAt(int x, int y) {
        for (int i = 0; i < drums.length; i++) {
            int cx = drums[i].getX() + drums[i].getWidth() / 2;
            int cy = drums[i].getY() + drums[i].getHeight() / 2;
            int dx = x - cx;
            int dy = y - cy;
            double dist = Math.sqrt(dx * dx + dy * dy);
            
            if (dist < 55) {
                return drums[i];
            }
        }
        
        return null;
    }
}
