package s3games.gui;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import s3games.engine.GameState;
import s3games.engine.Game;
import s3games.engine.GameSpecification;
import s3games.engine.Location;
import s3games.engine.Move;
import s3games.util.Switch;

/** Implements the window that visualizes the game progress and detects user 
 * moves as mouse clicks */
public class GameWindow extends javax.swing.JFrame 
{
    /** currently played game */
    Game game;
    /** the board canvas that shows the game progress */
    BoardCanvas boardCanvas;
    /** a repaint request */
    boolean repaint;

    /** text that is shown to the user of various info */
    ArrayList<String> outputTexts;
    /** the winner of the last game */
    String winner;
    /** for drawing of text = distance of right gap from left side */
    int offsetX;  
    
    /** determines if any element has been selected for a move */
    boolean isSelectedElement;
    /** which element has been last selected */
    String selectedElementName;
    
    /** the move that was performed for the mouse player */
    public Move lastMove;
    /** synchronization object for the mouse player to indicate a user has made
     * a new move */
    public final Object lastMoveReady;
    /** is it our turn? should we allow the user to make the move? */
    public boolean waitingForMove;
    /** list of allowed moves */
    public ArrayList<Move> allowedMoves;
    
    /** switch indicates whether the game is still on */
    private Switch gameRuns;
    
    /** Creates the game window and displays it */
    public GameWindow(Switch gameSwitch) 
    {
        gameRuns = gameSwitch;
        initComponents();
        
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                gameRuns.off();
                synchronized(lastMoveReady)
                {
                    lastMoveReady.notify();
                }                
            }
        });
        
        boardCanvas = (BoardCanvas) canvas1;
        lastMoveReady = new Object();
        waitingForMove = false;
        
        outputTexts = null;
        repaint = true; 
        isSelectedElement = false;
        
        winner = "";
    }

    /** shows a message box with the exception message to the user */
    public void showException(Exception e)
    {
        //TODO report exception to user
         e.printStackTrace();
         JOptionPane.showMessageDialog(this, e.getMessage(), "Ops, we got a little problem", JOptionPane.ERROR_MESSAGE);
    }
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        canvas1 = new BoardCanvas();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Board");
        setBounds(new java.awt.Rectangle(0, 0, 0, 0));
        setPreferredSize(new java.awt.Dimension(600, 600));
        setResizable(false);
        addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                formKeyPressed(evt);
            }
        });

        canvas1.setBackground(new java.awt.Color(255, 255, 255));
        canvas1.setPreferredSize(new java.awt.Dimension(600, 600));
        canvas1.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                canvas1MousePressed(evt);
            }
        });
        canvas1.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                canvas1KeyPressed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(canvas1, javax.swing.GroupLayout.DEFAULT_SIZE, 634, Short.MAX_VALUE)
                .addGap(152, 152, 152))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(canvas1, javax.swing.GroupLayout.DEFAULT_SIZE, 620, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void canvas1MousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_canvas1MousePressed
        if (waitingForMove) {
            GameSpecification gs = boardCanvas.gameSpec;
            GameState egs = boardCanvas.gState;
            Map<String,String> elements = egs.elementLocations;
            
            int x = evt.getX();
            int y = evt.getY();
 
            if (!isSelectedElement) {     //iterate through each movable element... 
                  for (Map.Entry<String, String> entry : elements.entrySet()) {
                       String elementLoc = entry.getValue();
                       Location loc = gs.locations.get(elementLoc);
                       if (gs.locationTypes.get(loc.type).shape.isInside(x, y, loc.point) && isElementAllowed(entry.getKey())) {
                          isSelectedElement = true;
                          selectedElementName = entry.getKey();
                          boardCanvas.setSelectedElement(selectedElementName);
                          return;
                       }
                  } 
            } else {                      //deselect or identify new location  
                String elementLoc = egs.elementLocations.get(selectedElementName);
                Location loc1 = gs.locations.get(elementLoc);
                if (gs.locationTypes.get(loc1.type).shape.isInside(x, y, loc1.point)) {                          //if was selected actually selected again, selection will be canceled
                    isSelectedElement = false;
                    boardCanvas.setSelectedElement(null);
                } else {       //iterate through each location...  
                    for (Map.Entry<String, Location> entry : gs.locations.entrySet()) {
                       Location loc = entry.getValue();
                       
                       if (gs.locationTypes.get(loc.type).shape.isInside(x, y, loc.point) && isMoveAllowed(selectedElementName, entry.getKey())) { 
                            isSelectedElement = false;
                            boardCanvas.setSelectedElement(null);
                           
                            String fromLoc= egs.elementLocations.get(selectedElementName);
                 
                            lastMove = new Move(fromLoc,entry.getKey(),selectedElementName, gs);
                            
                            synchronized(lastMoveReady)
                            {
                                lastMoveReady.notify();
                                waitingForMove = false;
                            }
                            return;                         
                        }   
                    }
                }
            }
        }
    }//GEN-LAST:event_canvas1MousePressed

    /** returns true if the user can move this element */
    private boolean isElementAllowed(String element) 
    {
        for (Move move: allowedMoves) 
            if (element.equals(move.element)) 
                return true;
        return false;
    }  
    
    /** returns true if the element can be moved to the specified location */
    private boolean isMoveAllowed(String element, String toLocation) 
    {
        for (Move move: allowedMoves) 
            if (element.equals(move.element) && toLocation.equals(move.to)) 
                return true;        
        return false;
    }
    
    private void formKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_formKeyPressed
       if (Character.toLowerCase(evt.getKeyChar()) == 'r') {
           repaint = !repaint;
           this.repaint();
        }
    }//GEN-LAST:event_formKeyPressed

    private void canvas1KeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_canvas1KeyPressed
        this.formKeyPressed(evt);   //added because of varied focus on window components...
    }//GEN-LAST:event_canvas1KeyPressed


    /** set the reference to the specified game and the game running switch,
     * show the game board in the window. */
    public void setGame(Game g, Switch gameRuns)
    {
        this.game = g;
        winner = "";
        isSelectedElement = false;
        
        GameSpecification gs = game.gameSpecification;
        try {
            //resize the window + canvas according to image
            Image bgImage = ImageIO.read(new File(gs.boardBackgroundFileName));
            this.setSize(bgImage.getWidth(this)+132,bgImage.getHeight(this)+30);  //130 takes panel and 30 because top panel also takes a place and his height is counted to total size of jframe...
            offsetX=bgImage.getWidth(this)+12;
            //boardCanvas.setPreferredSize(new java.awt.Dimension(bgImage.getWidth(this),bgImage.getHeight(this)));
            boardCanvas.setSize(new java.awt.Dimension(bgImage.getWidth(this),bgImage.getHeight(this)));
        } catch (IOException ex) {
            Logger.getLogger(GameWindow.class.getName()).log(Level.SEVERE, null, ex);
        }
        boardCanvas.setGame(gs);
    }
    
    /** set the current state of the game and visualize it */
    public void setState(GameState egs) 
    {         
        //generate array of strings for output text
        int currentPlayer = egs.currentPlayer;
        synchronized(this) {
            outputTexts = new ArrayList<String>();
            outputTexts.add("Player on move:"); 
            outputTexts.add("  "+boardCanvas.gameSpec.playerNames[currentPlayer-1]);
            outputTexts.add("Scores: ");
            for(int i=0; i< boardCanvas.gameSpec.playerNames.length; i++) {
                String name = boardCanvas.gameSpec.playerNames[i];
                int score = egs.playerScores[i];
                outputTexts.add((i+1)+" "+name+": "+score);   //for output are players indexing from 1
            }

            if (egs.winner >= 0) {   //game finished
               winner = ((egs.winner!=0)?boardCanvas.gameSpec.playerNames[egs.winner-1]+" wins!":"Draw!");                  //if someone won
            }
        }
        this.repaint();             
        if (repaint) {
            boardCanvas.setState(egs);
            boardCanvas.repaint();
        }
    }
    
    /** display the contents of the window - paint the current game situation */
    @Override
    public void paint(Graphics g) {   
        g.clearRect(offsetX-12, 0, this.getWidth(), this.getHeight());  //clear last text
        synchronized(this) {
            //draw output texts from array of strings - generated in setState
            if (outputTexts != null) {
               int x = 70;
               for(String s :outputTexts) {  //foreach item
                   x += 17;
                   g.drawString(s,offsetX,x);
               }
            }
            Font font = new Font("Arial", Font.PLAIN, 10);
            g.setFont(font);
            g.drawString("Canvas redrawing: "+((repaint)?"ON":"OFF"), offsetX, this.getHeight() - 30 );
            g.drawString("-press key R to change", offsetX+2, this.getHeight() - 19 );

            if (!winner.equals("")) { 
                 g.setFont(new Font("Arial",Font.BOLD,12));
                 g.drawString(winner,offsetX,55);
            }
        }
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private java.awt.Canvas canvas1;
    // End of variables declaration//GEN-END:variables
}
