
import java.awt.*;  // pour la classe Point
import java.io.*;

// pour StringReader

/** Cette classe permet de gerer les references relatives #(+6+7) et absolues #(B4)
 * On peut manipuler des références absolues du type:
 * "#(E7)" pour la cellule (4,7)
 * On peut manipuler des références relatives du type:
 * "#(+0-3)" par rapport à "#(F5)" pour la case (5,1)  */

public class Coord {

    /** Charactere pour le marqueur des références */
    static final char Id_Marqueur = '#';
    /** Charactere pour les parenthèses ouvrantes */
    static final char Id_ParentheseO = '(';
    /** Charactere pour les parenthèses fermantes */
    static final char Id_ParentheseF = ')';
    /** Charactere positif pour les references relatives */
    static final char Id_Positif = '+';
    /** Charactere négatif  pour les references relatives*/
    static final char Id_Negatif = '-';

    /** Pour les références invalides */
    public static final int Coord_Invalide = 0;
    /** Pour les références absolues */
    public static final int Coord_Absolue = 1;
    /** Pour les référence relatives */
    public static final int Coord_Relative = 2;

    int colone;
    int ligne;
    int type;  // type de refernce (Absolue ou relative)

// Multi-constructeurs :

    /** Constructeur: recopie la référence. */
    public Coord(Coord r) {
        colone = r.colone;
        ligne = r.ligne;
        type = r.type;
    }

    /** Constructeur: Creer une coordonnée à partir de 2 entiers et d'un type de référence (Relatif ou Absolue) */
    public Coord(int colone, int ligne, int type) {
        if ((type == Coord_Absolue) || (type == Coord_Relative))
            init(colone, ligne, type);
        else
            this.type = Coord_Invalide;
    }

    /** Constructeur: Creer une coordonnée absolue à partir de 2 entiers */
    public Coord(int colone, int ligne) {
        init(colone, ligne, Coord_Absolue);
    }

    /** Retourne le type de la référence. ( Coord_Absolue, Coord_Relative ou Coord_Invalide) */
    public int avoirType() {
        return type;
    }

    /** Retourne le nom commun de la Coordonnée ("A1", "G5" ...) */
    public String avoirNom() {
        if (type == Coord_Invalide)
            return "ref invalide";
        String str = "#(";
        if (type == Coord_Absolue)
            str += "" + (char) (colone + 'A') + (ligne + 1);
        if (type == Coord_Relative) {
            if (colone >= 0)
                str += "" + '+' + colone;
            else
                str += "" + colone;
            if (ligne >= 0)
                str += "" + '+' + ligne;
            else
                str += "" + ligne;
        }
        str += ")";
        return str;
    }

    /** Retourne les coordonnées absolues dans un point  */
    public Point avoirAbsoluePoint() throws ExceptionCoordInvalide {
        if (type != Coord_Absolue)
            throw new ExceptionCoordInvalide();
        return new Point(colone, ligne);
    }

    /** Retourne les coordonnées relative dans un point par rapport à la Coord passée en argument  */
    public Point avoirRelativePoint(Coord ref) throws ExceptionCoordInvalide {
        if (type == Coord_Invalide) throw new ExceptionCoordInvalide();
        if ((type != Coord_Relative) || (ref.type != Coord_Absolue))
            throw new ExceptionCoordInvalide();
        Point adr = new Point();
        adr.x = ref.colone + colone;
        adr.y = ref.ligne + ligne;
        return adr;
    }

    /** retourne type de coordonnée (Coord_Absolue, Coord_Relative ou Coord_Invalide) */
    static public int avoirType(String str) {
        int type = Coord_Invalide;
        if (!estCorrect(str)) return type;
        int len = str.length();
        char c = premiereLettre(str.substring(2));
        switch (c) {
            case Id_Positif:
            case Id_Negatif:
                type = Coord_Relative;
                break;
            case '\0':
                type = Coord_Invalide;
                break;
            default:
                type = Coord_Absolue;
        }
        // on vérifie la cohérence du type de référence
        if (type == Coord_Absolue) { // nécessairement un entier suit
            if (-1 == premierEntier(str.substring(3)))
                return Coord_Invalide;
        }
        if (type == Coord_Relative) {
            StringReader reader = new StringReader(str.substring(3, (len - 1)));
            int lu = dernierEntier(reader);
            if ((lu != Id_Positif) && (lu != Id_Negatif))
                return Coord_Invalide;
            lu = dernierEntier(reader);
            if (lu != -1)
                return Coord_Invalide;
        }
        return type;
    }

    /** retourne un object Coord en fonction d'une coordonnée sous forme de String  */
    static public Coord avoirCoord(String str) {
        Point p;
        switch (avoirType(str)) {
            case Coord_Absolue:
                p = avoirValeurAbsolue(str);
                return new Coord(p.x, p.y, Coord_Absolue);
            case Coord_Relative:
                p = avoirValeurRelative(str);
                return new Coord(p.x, p.y, Coord_Relative);
            default:
                break;
        }
        return new Coord(0, 0, Coord_Invalide);
    }

    /** initialise l'object Coord  */
    void init(int colone, int ligne, int type) {
        this.colone = colone;
        this.ligne = ligne;
        this.type = type;
    }

    /** retourne la valeur d'une Coordonnée absolue sous forme de string*/
    static Point avoirValeurAbsolue(String str) {
        Point p = new Point(-1, -1);
        char c = str.charAt(2);
        if ((c >= 'A') && (c <= 'Z')) p.x = (int) (c - 'A');
        if ((c >= 'a') && (c <= 'z')) p.x = (int) (c - 'a');
        if (p.x == -1) return p;
        try {
            p.y = Integer.parseInt(str.substring(3, str.length() - 1));
            p.y -= 1; // transforme le numéro de ligne en indice!
        } catch (Exception e) {
            p.x = p.y = -1;
            return p;
        }
        if (p.y < 0) {
            p.x = p.y = -1;
            return p;
        }
        return p;
    }

    /** retourne la valeur d'une Coordonnée relative sous forme de string*/
    static Point avoirValeurRelative(String str) {
        Point p = new Point(-1, -1);
        int l = str.length();
        int i;
        char sign[] = new char[2];
        int nbSign;
        for (
                i = 2, nbSign = 0;
                (i < l) && (nbSign < 2);
                i++
                ) {
            switch (str.charAt(i)) {
                case Id_Positif:
                case Id_Negatif:
                    sign[nbSign++] = str.charAt(i);
                    break;
                default:
                    break;
            }
        }
        if ((i >= l - 1) || (nbSign != 2)) {
            p.x = p.y = -1;
            return p;
        }
        try {
            p.x = Integer.parseInt(str.substring(3, i - 1));
            p.y = Integer.parseInt(str.substring(i, l - 1));
        } catch (Exception e) {
            p.x = -1;
            p.y = -1;
            return p;
        }
        if (sign[0] == Id_Negatif) p.x = -p.x;
        if (sign[1] == Id_Negatif) p.y = -p.y;
        return p;
    }

    /** retourne l'entier situé au début du string */
    static int premierEntier(String str) {
        if (str == null) return -1;
        int lu;
        // trouve la fin de ligne'entier dans str
        for (lu = 0; lu < str.length(); lu++) {
            char c = str.charAt(lu);
            if ((c < '0') || (c > '9'))
                break;
        }
        int i;
        try {
            i = Integer.parseInt(str.substring(0, lu));
        } catch (Exception e) {
            i = -1;
        }
        return i;
    }

    /** retourne la lettre (en majuscule) au début de str */
    static char premiereLettre(String str) {
        if (str == null) return '\0';
        char c = str.charAt(0);
        if ((c >= 'a') && (c <= 'z')) return (char) (c + 'A' - 'a');
        if ((c >= 'A') && (c <= 'Z')) return c;
        if ((c == Id_Positif) || (c == Id_Negatif)) return c;
        return '\0';
    }

    /** retourne le dernier char lu (en enlevant lentier)*/
    static int dernierEntier(StringReader reader) {
        int lu;
        if (reader == null) return -1;
        try {
            lu = reader.read();
            while ((lu != -1) && (lu >= (int) '0') && (lu <= (int) '9')) {
                lu = reader.read();
            }
        } catch (IOException e) {
            return -1;
        }
        return lu;
    }

    /** retourne si oui ou non le string contient une coordonnée valide */
    static boolean estCorrect(String str) {
        if (str == null) return false;
        int l = str.length();
        // on vérifie la taille minimale de str (qui est de 5 car:
        // "#(A1)" comprend 5 caractères...
        if (l < 5) return false;
        // vérification des éléments syntaxiques de bases
        if (
                (str.charAt(0) != Id_Marqueur)
                ||
                (str.charAt(1) != Id_ParentheseO)
                ||
                (str.charAt(l - 1) != Id_ParentheseF)
        )
            return false;
        return true;
    }

}
