/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *   2008 C.Marchand - christophe.marchand@axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.cp.xemelios.ui.ia.core;

import fr.gouv.finances.dgfip.xemelios.common.Constants;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;

/**
 *
 * @author  christophe.marchand
 */
public class DlgCore extends javax.swing.JDialog {
    private Action actNew, actClose;
//    private Icon iconNew = IhmFactory.getIconFromResource(ImageResources.NEW);
//    private Icon iconClose = IhmFactory.getIconFromResource(ImageResources.CLOSE);
    private SGrid grid;
    
    /** Creates new form DlgCore */
    public DlgCore(java.awt.Frame parent) {
        super(parent, false);
        initActions();
        newGame();
        initComponents();
    }
    protected void initActions() {
        actNew = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                newGame();
            }
        };
        actClose = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                setVisible(false);
            }
        };
    }
    protected void newGame(){
        Builder bd = new Builder();
        grid = new SGrid(new StateModel(bd.buildOne(TemplateFactory.getTemplate(3,3))));
    };
    
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                DlgCore dialog = new DlgCore(new javax.swing.JFrame());
                dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                    @Override
                    public void windowClosing(java.awt.event.WindowEvent e) {
                        System.exit(0);
                    }
                });
                dialog.setVisible(true);
            }
        });
    }
    
    private static boolean isInSameSquare(int i, int j, int row, int col) {
        return Math.floor(i/3.0f)==Math.floor(row/3.0f) && Math.floor(j/3.0f)==Math.floor(col/3.0f);
    }
    private void initComponents() {


        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        StringBuffer sb = new StringBuffer();
        sb.append(Constants.NOM_APP).append(" S").append("u").append("d").append("o").append("k").append("u");
        setTitle(sb.toString());


        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(grid, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(grid, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 320, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }
    
    /**
     * @param args the command line arguments
     */
    // Variables declaration - do not modify
    private class SGrid extends JPanel {
        SCase grid[][];
        private StateModel sm;
        public SGrid(StateModel sm) {
            super();
            this.sm=sm;
            initComponents();
        }

        @Override
        public int getHeight() {
            return super.getWidth();
        }
        protected void initComponents() {
            setLayout(new GridLayout(9,9,0,0));
            grid = new SCase[9][9];
            for(int i=0;i<9;i++) {
                for(int j=0;j<9;j++) {
                    grid[i][j]=new SCase(sm,this,i,j);
                    add(grid[i][j]);
                }
            }
        }
        public void notifyFocusGained(int row, int col) {
            for(int i=0;i<9;i++) {
                for(int j=0;j<9;j++) {
                    grid[i][j].setSelected(i==row || j==col || isInSameSquare(i,j,row,col));
                    grid[i][j].setHadFocus(row==i && col==j);
                }
            }
            paintImmediately(new Rectangle(0,0,getWidth(),getHeight()));
        }
        public void checkGridFinished() {
            boolean gridFilled = true;
            for(int i=0; i<9; i++) {
                if(!gridFilled) break;
                for(int j=0; j<9; j++) {
                    String s = sm.getValueAt(i,j);
                    if(s==null) {
                        gridFilled = false;
                        break;
                    }
                }
            }
            if(gridFilled) {
                JOptionPane.showMessageDialog(getParent(), "Félicitations !");
            }
        }
        public void highLight(String s) {
            for(int i=0; i<9; i++) {
                for(int j=0;j<9;j++) {
                    grid[i][j].setSelected(sm.getValueAt(i,j) == null);
                }
            }
            for(int i=0; i<9; i++) {
                for(int j=0;j<9;j++) {
                    if(s.equals(sm.getValueAt(i, j))) {
                        for(int k=0;k<9;k++) {
                            grid[i][k].setSelected(false);
                            grid[k][j].setSelected(false);
                        }
                        int xStart = (int)Math.floor(i/3.0f)*3;
                        int yStart = (int)Math.floor(j/3.0f)*3;
                        int xEnd = xStart + 3;
                        int yEnd = yStart + 3;
                        for(int i1=xStart;i1<xEnd;i1++) {
                            for(int j1=yStart;j1<yEnd;j1++) {
                                grid[i1][j1].setSelected(false);
                            }
                        }
                    }
                }
            }
            paintImmediately(new Rectangle(0,0,getWidth(),getHeight()));
        }
    }
    private class SCase extends JComponent {
        private StateModel sm;
        private int row, col;
        private boolean selected = false;
        private boolean hadFocus = false;
        private SGrid grid;
        public SCase(final StateModel sm,final SGrid grid, final int row, final int col) {
            super();
            this.sm=sm;
            this.grid=grid;
            this.row=row;
            this.col=col;
            addKeyListener(new KeyAdapter() {
                @Override
                public void keyTyped(KeyEvent e) {
                    if((e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) > 0) {
                        char c = e.getKeyChar();
                        if('1' <= c && c <= '9') {
                            grid.highLight(Character.toString(c));
                        }
                        
                    } else if(sm.isWritable(row, col)) {
                        if(sm.isValueAllowed(row, col, Character.toString(e.getKeyChar()))) {
                            sm.setValueAt(row, col, Character.toString(e.getKeyChar()));
                            e.consume();
                            repaint();
                            grid.checkGridFinished();
                        } else if(e.getKeyChar()==' ' || (int)e.getKeyChar()==127 || (int)e.getKeyChar()==8) {
                            sm.setValueAt(row, col, " ");
                            e.consume();
                            repaint();
                        }
                    }
                }

                @Override
                public void keyPressed(KeyEvent e) {
                    if(e.getKeyCode()==KeyEvent.VK_LEFT) {
                        if(col>0) {
                            grid.grid[row][col-1].grabFocus();
                            e.consume();
                        }
                    } else if(e.getKeyCode()==KeyEvent.VK_RIGHT) {
                        if(col<8) {
                            grid.grid[row][col+1].grabFocus();
                            e.consume();
                        }
                    } else if(e.getKeyCode()==KeyEvent.VK_UP) {
                        if(row>0) {
                            grid.grid[row-1][col].grabFocus();
                            e.consume();
                        }
                    } else if(e.getKeyCode()==KeyEvent.VK_DOWN) {
                        if(row<8) {
                            grid.grid[row+1][col].grabFocus();
                            e.consume();
                        }
                    }
                }
            });
            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    requestFocus();
                }
            });
            addFocusListener(new FocusAdapter() {
                @Override
                public void focusGained(FocusEvent e) {
                    grid.notifyFocusGained(row,col);
                }
            });
        }
        @Override
        public void paint(Graphics g) {
            if(selected) g.setColor(UIManager.getColor("TextField.selectionBackground")); // 
            else g.setColor(UIManager.getColor("TextField.background")); // UIManager.getColor("background")
            g.fillRect(1,1,getWidth()-1,getHeight()-1);
            if(hasFocus() | isTopComponentFocused()) g.setColor(Color.GRAY);
            else g.setColor(Color.BLACK);
            // cadre supérieur
            g.drawLine(0,0, getWidth()-1,0);
            if(hasFocus() | isLeftComponentFocused()) g.setColor(Color.GRAY);
            else g.setColor(Color.BLACK);
            // cadre gauche
            g.drawLine(0,0,0, getHeight()-1);
            if(row % 3 == 2) {
                if(isRightComponentFocused())
                    g.setColor(Color.GRAY);
                else
                    g.setColor(Color.BLACK);
                g.drawLine(0,getHeight()-1,getWidth()-1,getHeight()-1);
            }
            if(col % 3 == 2) {
                if(isBottomComponentFocused())
                    g.setColor(Color.GRAY);
                else
                    g.setColor(Color.BLACK);
                g.drawLine(getWidth()-1,0,getWidth()-1,getHeight()-1);
            }
            if(sm.getValueAt(row, col)!=null) {
                Font originalF = g.getFont();
                g.setColor(Color.BLACK);
                Font ft = g.getFont();
                Font nf = ft.deriveFont(getHeight()*0.5f);
                if(sm.isWritable(row, col)) {
                    nf = nf.deriveFont(Font.ITALIC);
                }
                g.setFont(nf);
                String s = sm.getValueAt(row, col);
                Rectangle2D rect = g.getFontMetrics().getStringBounds(s, g);
                g.drawString(s,
                        (int)((getWidth()-rect.getWidth())/2),
                        (int)((getHeight()-rect.getHeight()/2)));
                g.setFont(originalF);
            }
        }
       public void setSelected(boolean selected) {
           this.selected=selected;
       }
       public void setHadFocus(boolean hadFocus) {
           this.hadFocus=hadFocus;
       }
       public boolean isBottomComponentFocused() {
           if(row==8) return false;
           return grid.grid[row+1][col].hasFocus();
       }
       public boolean isRightComponentFocused() {
           if(col==8) return false;
           return grid.grid[row][col+1].hasFocus();
       }
       public boolean isLeftComponentFocused() {
           if(col==0) return false;
           return grid.grid[row][col-1].hasFocus();
       }
       public boolean isTopComponentFocused() {
           if(row==0) return false;
           return grid.grid[row-1][col].hasFocus();
       }
        @Override
        public Dimension getMinimumSize() {
            return new Dimension(20,20);
        }
       
    }
}
