// Projet de Java :  "Ecriture d'un mini tableur"
// Auteur : Baccam Olivier & Roulin Clément
// Responsable : Claudine Moinard


import java.awt.*;
import java.awt.event.KeyEvent;

/** Classe principale :
 * Affiche le tableur (grace à un GridBagLayout) et
 * Détermine les actions à réaliser en fonction de chaque évenement (grâce aux 4 adaptateurs)
 */
public class TableurJava extends java.applet.Applet {

    //variables utilisé pour ligne'affichage de ligne'applet
    Label CR;                    // pour afficher de "copyright"
    Label affichage;             // pour afficher tous les messages
    TextField formule;               // pour entrer les formules
    Cellule lacase;                // pour représenter la case en cours d'utilisation
    Cellule tab[][];               // tableau de cases [lignes][colones]
    TextField largeur;               // pour redimensionner [colone]
    TextField hauteur;               // pour redimensionner [lignes]
    java.awt.Button redimensionner;  // pour redimensionner le tableur
    Dimension dimension_applet;      // dimension de ligne'applet
    Dimension dimension_tableau;      // dimension du tableau
    Font font1;                 // font du texte 1
    Font font2;                 // font du texte 2

    //Adaptateurs
    java.awt.event.FocusListener fl;
    java.awt.event.ActionListener al;
    java.awt.event.KeyListener kl;
    java.awt.event.MouseListener ml;

    /** Traite ligne'évenement :
     * "L'utisateur selectionne (clique) une case du tableau ou le textfield formule"
     */
    class MyFocusAdapter extends java.awt.event.FocusAdapter implements java.awt.event.FocusListener {
        public void focusGained(java.awt.event.FocusEvent fe) {
            if (formule == fe.getComponent()) { // événement en provenance du textfield formule
                if (lacase != null)
                    affichage.setText("Edition de la case de réference : " + lacase.avoirNom());
                formule.selectAll();
            } else {  // événement en provenance d'une case
                if (lacase != null) {
                    lacase.offBackground();
                }
                lacase = (Cellule) fe.getSource();
                lacase.onBackground();
                affichage.setText("Appuyer sur Entrée pour editer la case " + lacase.avoirNom());
                formule.setText(lacase.avoirFormule());
            }
        }
    }

    /** Traite ligne'évenement :
     * "L'utisateur appuie sur la touche entrée à ligne'intérieur d'une case ou appuie sur le bouton."
     */
    class MyActionAdapter implements java.awt.event.ActionListener {
        public void actionPerformed(java.awt.event.ActionEvent ae) {
            if (ae.getActionCommand() == "TAILLE") { // lorsque lutisateur appuie sur le bouton de redimensionnement
                int w,h;
                w = Integer.parseInt(largeur.getText());
                h = Integer.parseInt(hauteur.getText());
                redimensionne(w, h);
                return;
            }
            if (formule == ae.getSource()) {  // lorsque lutisateur appuie sur entrée dans lengthtextfiel formule
                if (lacase != null) {
                    lacase.changerFormule(formule.getText(), tab);
                    lacase.requestFocus();
                }
            } else { // lorsque lutisateur appuie sur entrée dans une case du tableau
                formule.requestFocus();
            }
        }

    }


    /** Traite ligne'évenement :
     * "L'utisateur navigue  avec les flèches du clavier entre les cases du tableau et le textfield formule."
     */
    class MyKeyAdapter extends java.awt.event.KeyAdapter implements java.awt.event.KeyListener {
        public void keyPressed(KeyEvent ke) {
            if (lacase == null) return;
            if (ke.getSource() == formule) { // ligne'événement provient de formule
                switch (ke.getKeyCode()) {
                    case KeyEvent.VK_ESCAPE:
                        lacase.requestFocus();
                        break;
                }
            } else {  // lorsque ligne'utilisateur change de case on garde les coordonnées
                java.awt.Point p = null;
                try {
                    p = lacase.avoirPosition();
                } catch (ExceptionCoordInvalide ir) {
                    return;
                }
                switch (ke.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        p.y -= 1;
                        if (p.y < 0) p.y = dimension_tableau.height - 1;
                        break;
                    case KeyEvent.VK_DOWN:
                        p.y += 1;
                        if (p.y >= dimension_tableau.height) p.y = 0;
                        break;
                    case KeyEvent.VK_LEFT:
                        p.x -= 1;
                        if (p.x < 0) p.x = dimension_tableau.width - 1;
                        break;
                    case KeyEvent.VK_RIGHT:
                        p.x += 1;
                        if (p.x >= dimension_tableau.width) p.x = 0;
                        break;
                }
                tab[p.y][p.x].requestFocus();
            }
        }
    }


    /** Traite ligne'évenement :
     * "L'utisateur se déplace sur le tableau à laide de la sourie mais ne clique pas."
     */
    class MyMouseListener extends java.awt.event.MouseAdapter {
        public void mouseExited(java.awt.event.MouseEvent me) {
            Cellule c = null;
            try {
                c = (Cellule) me.getComponent();
            } catch (Exception e) {
                return;
            }
            if ((c != null) && (c != lacase)) {
                affichage.setText("Cliquez sur la case de votre choix");
            }
        }

    }


    /** Intitialise les variables utilisées par le Gui
     * Fait appel à la méthode Composer
     */
    public synchronized void init() {
        String temp;
        Label l;
        char col;
        setBackground(Color.orange);                     // couleur background de ligne'applet
        dimension_tableau = new Dimension(10, 10);        // dimensions du tableau par défault
        dimension_applet = new Dimension();
        temp = getParameter("width");
        dimension_applet.width = Integer.parseInt(temp);
        temp = getParameter("height");
        dimension_applet.height = Integer.parseInt(temp);

        // création des instances pour chaque événement
        fl = new TableurJava.MyFocusAdapter();
        al = new TableurJava.MyActionAdapter();
        kl = new TableurJava.MyKeyAdapter();
        addKeyListener(kl);
        ml = new TableurJava.MyMouseListener();

        lacase = null;
        font1 = new Font("Arial", Font.PLAIN, 12);
        font2 = new Font("Arial", Font.BOLD, 14);
        CR = new Label();

        affichage = new Label("");
        formule = new TextField(12);
        formule.addFocusListener(fl);
        formule.addActionListener(al);
        formule.addKeyListener(kl);

        // initialisation des cases du tableau
        tab = new Cellule[dimension_tableau.height][dimension_tableau.width];
        for (int i = 0; i < dimension_tableau.height; i++) {
            for (int j = 0; j < dimension_tableau.width; j++) {
                tab[i][j] = new Cellule(j, i);
                tab[i][j].addFocusListener(fl);
                tab[i][j].addActionListener(al);
                tab[i][j].addKeyListener(kl);
                tab[i][j].addMouseListener(ml);
                tab[i][j].setBackground(Color.orange);
            }
        }

        // initialisation du redimensionneur (TexteField & Button)
        largeur = new TextField(2);
        largeur.setText("" + dimension_tableau.width);
        hauteur = new TextField(2);
        hauteur.setText("" + dimension_tableau.height);
        redimensionner = new java.awt.Button("Redimensionner");
        redimensionner.setActionCommand("TAILLE");
        redimensionner.addActionListener(al);

        // initialisation de ligne'interface graphique
        Composer();
    }

    /** Dispose les différents éléments de ligne'applet initial selon un layout (GridBagLayout)
     */
    public void Composer() {

        java.awt.GridBagLayout gridbag = new java.awt.GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        setFont(font1);
        setLayout(gridbag);
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.gridheight = 1;

        /// Affiche le copyright
        CR.setText("- TableurJava Java Par Baccam Olivier & Roulin Clément -");
        c.gridwidth = GridBagConstraints.REMAINDER;
        CR.setFont(font2);
        CR.setForeground(Color.black);
        CR.setAlignment(Label.CENTER);
        gridbag.setConstraints(CR, c);
        add(CR);

        /// Affiche le textfield et sa légende
        Label l2;
        c.gridheight = 1;
        c.weightx = 1.0;
        c.gridwidth = 1 + dimension_tableau.width / 2;
        affichage.setAlignment(Label.RIGHT);
        affichage.setBackground(Color.black);
        affichage.setForeground(Color.orange);
        gridbag.setConstraints(affichage, c);
        add(affichage); // Affiche la légende
        c.gridwidth = dimension_tableau.width / 2 + dimension_tableau.width % 2;
        c.weightx = 1.0;
        gridbag.setConstraints(formule, c);
        add(formule);// Affiche le textfied.
        c.weightx = 0.0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        l2 = new Label("");
        gridbag.setConstraints(l2, c);
        add(l2); //affiche un espace (saut de ligne)

        /// Affiche les lettres du tableau (colones)
        Label l10;
        c.gridwidth = 1;
        c.weightx = 0.0;
        l10 = new Label("");
        gridbag.setConstraints(l10, c);
        add(l10); // affiche lespace du coin haut-gauche
        c.weightx = 1.0;
        for (int j = 0; j < dimension_tableau.width; j++) {
            l10 = new Label("" + (char) ('A' + j));
            l10.setAlignment(Label.CENTER);
            gridbag.setConstraints(l10, c);
            add(l10); // affiche tous les numéros de colonnes
        }
        l10 = new Label("");
        c.weightx = 0.0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        gridbag.setConstraints(l10, c);
        add(l10); // affiche lespace du coin bas-droit

        /// Affiche toutes les cases du tableau
        Label l4;
        for (int i = 0; i < dimension_tableau.height; i++) {
            c.gridwidth = 1;
            l4 = new Label("" + (i + 1));
            l4.setAlignment(Label.CENTER);
            gridbag.setConstraints(l4, c);
            add(l4); // affiche tous les numéros de lignes (à gauche)
            c.weightx = 1.0;
            for (int j = 0; j < dimension_tableau.width; j++) {
                gridbag.setConstraints(tab[i][j], c);
                add(tab[i][j]); // Affiche toutes les cases de la ligne (i)
            }
            l4 = new Label("" + (i + 1));
            l4.setAlignment(Label.CENTER);
            c.weightx = 0.0;
            c.gridwidth = GridBagConstraints.REMAINDER;
            gridbag.setConstraints(l4, c);
            add(l4); // affiche tous les numéros de lignes (à droite)
        }

        /// Affiche les lettres du tableau (colones)
        Label l11;
        c.gridwidth = 1;
        c.weightx = 0.0;
        l11 = new Label("");
        gridbag.setConstraints(l11, c);
        add(l11); // affiche lespace du coin haut-gauche
        c.weightx = 1.0;
        for (int j = 0; j < dimension_tableau.width; j++) {
            l11 = new Label("" + (char) ('A' + j));
            l11.setAlignment(Label.CENTER);
            gridbag.setConstraints(l11, c);
            add(l11); // affiche tous les numéros de colonnes
        }
        l11 = new Label("");
        c.weightx = 0.0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        gridbag.setConstraints(l11, c);
        add(l11); // affiche lespace du coin bas-droit

        /// affiche un espace (saut de ligne)
        c.gridwidth = GridBagConstraints.REMAINDER;
        Label l12 = new Label("");
        gridbag.setConstraints(l12, c);
        add(l12);

        ///affiche le textfield de redimmensionnement et son bouton
        gridbag.setConstraints(largeur, c);
        add(largeur); // affiche un textfied pour la largeur
        gridbag.setConstraints(hauteur, c);
        add(hauteur);// affiche un textfied pour la hauteur
        gridbag.setConstraints(redimensionner, c);
        add(redimensionner);// affiche le bouton de redimmensionnement
        Dimension dim = gridbag.preferredLayoutSize(this);
        resize(dim);
    }

    /** redimensionne le tableau
     * @param w Nouveau nombre de colonne
     * @param h Nouveau nombre de lignes
     */
    public void redimensionne(int w, int h) {

        Cellule gtmp[][] = new Cellule[h][w];

        //on copie lancien tableau dans le tableau temporaire et on crée les nouvelles cases
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                if ((i >= dimension_tableau.height) || (j >= dimension_tableau.width)) {
                    gtmp[i][j] = new Cellule(j, i);
                    gtmp[i][j].setBackground(Color.orange);
                } else {
                    gtmp[i][j] = tab[i][j];
                }
            }
        }

        // on supprime les cases qui sont en "trop" dans le cas dune reduction de tableau
        for (int i = 0; i < dimension_tableau.height; i++) {
            for (int j = 0; j < dimension_tableau.width; j++) {
                if ((i >= h) || (j >= w)) {
                    tab[i][j].invaliderCellule();
                    tab[i][j].invaliderListeDepdt();
                    tab[i][j].removeFocusListener(fl);
                    tab[i][j].removeActionListener(al);
                    tab[i][j].removeKeyListener(kl);
                    tab[i][j].removeMouseListener(ml);
                }
            }
        }

        //on recopie maintenant le tableau temporaire dans g
        tab = new Cellule[h][w];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                tab[i][j] = gtmp[i][j];
                if ((i >= dimension_tableau.height) || (j >= dimension_tableau.width)) {
                    tab[i][j].addFocusListener(fl);
                    tab[i][j].addActionListener(al);
                    tab[i][j].addKeyListener(kl);
                    tab[i][j].addMouseListener(ml);
                }
            }
        }

        dimension_tableau.width = w;
        dimension_tableau.height = h;
        removeAll();
        Composer();
    }


}