/*
Copyright (C) 2005 David Green <green@couchpotato.net>
All Rights Reserved.

This file is part of Aelfengard.

Aelfengard is proprietary software. You may not redistribute it without
prior written permission from the copyright holder.
*/

package common.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;

import common.Face;
import common.FaceModel;
import common.FaceModel.FacePart;
import common.clientevent.FaceChooserClientEvent;

/**
 * The Face Chooser window.
 * 
 * @author David Green <green@couchpotato.net>
 */
public class FaceChooser extends JFrame {

    private static final long serialVersionUID = 3544949952699576885L;
    
    private FaceModel model; // the currently-selected face model (elf_m1, kithian_f1, etc)
    private List<Integer> parts; // the currently-selected parts (nose #3, hair #7, etc)
    
    private final JPanel sidePanel = new JPanel(); // the panel containing the selection buttons
    private final JLabel faceLabel = new JLabel(); // a very big label that contains the face icon
    private final JButton modelButton; // a button for selecting a new model
    private final JButton randomButton; // a button for selecting random parts
    private final JPanel loadingPanel = new JPanel(new BorderLayout()); // displays "loading" animation when needed
    private final JProgressBar loadingBar = new JProgressBar(); // loading animation when needed
    private final JButton saveButton = new JButton("Save & Exit"); // save button
    private final JButton cancelButton = new JButton("Cancel"); // cancel button
    
    // A collection of the current parts buttons
    private Collection<JButton> buttons = new ArrayList<JButton>();
    
    // For use by the random button
    private static final Random random = new Random();
    
    /**
     * Creates a new face chooser window.
     * @param face the initial face selection, or null for no default
     */
    public FaceChooser(Face face) {

        super("Face Chooser");
        
        //getContentPane().setBackground(Color.black);
        
        modelButton = new JButton("Model");
        modelButton.setAlignmentX(0.5f);
        randomButton = new JButton("Random");
        randomButton.setAlignmentX(0.5f);
        
        sidePanel.setLayout(new BoxLayout(sidePanel, BoxLayout.Y_AXIS));
        sidePanel.setBackground(Color.black);
        sidePanel.setBorder(new EmptyBorder(5, 5, 5, 5));
        JPanel facePanel = new JPanel();
        facePanel.setOpaque(false);
        facePanel.setLayout(new BoxLayout(facePanel, BoxLayout.Y_AXIS));
        faceLabel.setAlignmentX(0.5f);
        facePanel.add(Box.createVerticalGlue());
        facePanel.add(faceLabel);
        facePanel.add(Box.createVerticalGlue());

        JPanel mainPanel = new JPanel();
        mainPanel.setBackground(Color.black);
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.X_AXIS));
        mainPanel.add(Box.createHorizontalGlue());
        mainPanel.add(sidePanel);
        mainPanel.add(Box.createHorizontalStrut(5));
        mainPanel.add(facePanel);
        mainPanel.add(Box.createHorizontalGlue());
        add(mainPanel, BorderLayout.CENTER);
        
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        buttonPanel.setOpaque(false);

        JLabel loadingLabel = new JLabel("Loading:");
        loadingLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
        loadingLabel.setForeground(Color.white);
        loadingBar.setOpaque(false);
        loadingBar.setBorder(new CompoundBorder(new EmptyBorder(10, 0, 10, 0), loadingBar.getBorder()));
        loadingBar.setIndeterminate(true);
        loadingPanel.setOpaque(false);
        loadingPanel.add(loadingLabel, BorderLayout.WEST);
        loadingPanel.add(loadingBar, BorderLayout.CENTER);
        
        JPanel bottomPanel = new JPanel(new BorderLayout());
        bottomPanel.setBackground(Color.black);
        bottomPanel.add(loadingPanel, BorderLayout.CENTER);
        bottomPanel.add(buttonPanel, BorderLayout.EAST);
        
        add(bottomPanel, BorderLayout.SOUTH);

        modelButton.addActionListener(new ActionListener() {
            
            public void actionPerformed(ActionEvent e) {
                nextModel();
            }
        
        });
        
        randomButton.addActionListener(new ActionListener() {
        
            public void actionPerformed(ActionEvent e) {
                randomizeParts();
                redrawFace();
            }
        
        });
        
        cancelButton.addActionListener(new ActionListener() {
        
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        
        });
        
        saveButton.addActionListener(new ActionListener() {
        
            public void actionPerformed(ActionEvent e) {
                GameClient.send(new FaceChooserClientEvent(new Face(model, parts)));
                dispose();
            }
        
        });
        
        if (face == null) {
            nextModel(); // no face was sent into constructor, so just select a random one...
        }
        else {
            // otherwise display the face that was sent into the constructor...
            model = face.getFaceModel();
            parts = new ArrayList<Integer>(face.getParts());
            prepareModel();
        }
        setLocationByPlatform(true);
    }
    
    /**
     * Cycles to the next model in the list.
     */
    private void nextModel() {
        int modelNum = model == null ? -1 : model.ordinal();
        FaceModel[] models = FaceModel.values();
        model = models[++modelNum % models.length];
        randomizeParts();
        prepareModel();
    }
    
    /**
     * Sets up everything associated with a new model.
     */
    private void prepareModel() {
        buttons.clear();
        sidePanel.removeAll();
        sidePanel.add(Box.createVerticalGlue());
        sidePanel.add(modelButton);
        sidePanel.add(Box.createVerticalStrut(5));
        sidePanel.add(randomButton);
        sidePanel.add(Box.createVerticalStrut(20));
        List<FacePart> faceParts = model.getFaceParts();
        int idx = 0;
        // create buttons for the non-static parts
        for (FacePart facePart : faceParts) {
            if (facePart.isStatic()) {
                continue;
            }
            if (idx > 0) {
                sidePanel.add(Box.createVerticalStrut(5));
            }
            final int fidx = idx++;
            final int partCount = facePart.getCount();
            JButton partButton = new JButton(facePart.getName());
            partButton.setAlignmentX(0.5f);
            sidePanel.add(partButton);
            partButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    int value = parts.get(fidx);
                    value %= partCount;
                    value++;
                    parts.set(fidx, value);
                    redrawFace();
                }
            });
            buttons.add(partButton);
        }
        sidePanel.add(Box.createVerticalGlue());
        // Display "Loading" icon until load is complete
        faceLabel.setIcon(FaceIcon.LOADING_IMAGE);
        redrawFace();
        pack();
        // TODO: Why is this needed?
        // For some reason we get old button effects after changing models to a model with less buttons :(
        repaint();
    }
    
    /**
     * Choose random parts for the current model.
     */
    private void randomizeParts() {
        List<Integer> oldParts = parts;
        parts = new ArrayList<Integer>();
        for (FacePart facePart : model.getFaceParts()) {
            if (facePart.isStatic()) {
                continue;
            }
            int part = 1;
            for (int i = 0; i < 10; i++) {
                // try 10 times to get a different part
                part = random.nextInt(facePart.getCount()) + 1;
                if (oldParts == null) {
                    break; // no old parts at all
                }
                if (parts.size() >= oldParts.size()) {
                    break; // there was no old part for this slot
                }
                if (oldParts.get(parts.size()) != part) {
                    break;
                }
            }
            parts.add(part);
        }
    }
    
    /**
     * Start loading animation, start thread to load images from net, display final product when done.
     * This method does not block.
     */
    private void redrawFace() {
        setLoading(true);
        loadingBar.setIndeterminate(true);
        loadingPanel.setVisible(true);
        new Thread() {
            @Override
            public void run() {
                Icon icon = null;
                try {
                    icon = new FaceIcon(new Face(model, parts));
                }
                finally {
                    final Icon ficon = icon;
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            faceLabel.setIcon(ficon);
                            setLoading(false);
                        }
                    });
                }
            }
        }.start();
    }
    
    /**
     * Turn on/off "loading" mode (disabled buttons, loading animation, etc).
     * @param loading true if loading mode should be turned on, false otherwise
     */
    private void setLoading(boolean loading) {
        for (JButton b : buttons) {
            b.setEnabled(!loading);
        }
        saveButton.setEnabled(!loading);
        cancelButton.setEnabled(!loading);
        modelButton.setEnabled(!loading);
        randomButton.setEnabled(!loading);
        loadingPanel.setVisible(loading);
        loadingBar.setIndeterminate(loading);
        repaint();
    }
    
}