/*
Applet Jaxe - Editeur XML en Java

Copyright (C) 2008 Observatoire de Paris-Meudon

Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier conformément aux dispositions de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation ; version 2 de la licence, ou encore (à votre choix) toute version ultérieure.

Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou D'ADAPTATION A UN OBJET PARTICULIER. Pour plus de détail, voir la Licence Publique Générale GNU .

Vous devez avoir reçu un exemplaire de la Licence Publique Générale GNU en même temps que ce programme ; si ce n'est pas le cas, écrivez à la Free Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, Etats-Unis.
*/
package jaxeapplet;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;

import com.swabunga.spell.engine.SpellDictionaryASpell;

/**
 * Implémentation de dictionnaire pour Jazzy.
 * Comme com.swabunga.spell.engine.SpellDictionaryDichoDisk, mais en mettant le dictionnaire en mémoire
 * dans un seul String. Utilise donc moins de mémoire que SpellDictionaryHashMap.
 *
 * Les fichiers de dictionnaire utilisent le format "code*word" avec des lignes triées par code.
 *
 * @author Damien Guillaume
 * @version 0.1
 */
public class SpellDictionaryDichoMem extends SpellDictionaryASpell {
    
    /** Holds the dictionary file for reading */
    private StringBuffer dico = null;
    private int pos = -1;
    
    /**
     * Dictionary convenience Constructor.
     * @param wordList The file containing the words list for the dictionary
     * @param encoding Uses the character set encoding specified
     * @throws java.io.IOException indicates problems reading the words list
     * file
     */
    public SpellDictionaryDichoMem(final URL wordList, final String encoding) throws IOException {
        super((Reader) null);
        dico = lectureDico(wordList, encoding);
    }
    
    /**
     * Dictionary constructor that uses an aspell phonetic file to
     * build the transformation table.
     * @param wordList The file containing the words list for the dictionary
     * @param phonetic The file to use for phonetic transformation of the 
     * wordlist.
     * @param encoding Uses the character set encoding specified
     * @throws java.io.IOException indicates problems reading the words list
     * file
     */
    public SpellDictionaryDichoMem(final URL wordList, final URL phonetic, final String encoding) throws IOException {
        super(new BufferedReader(new InputStreamReader(phonetic.openConnection().getInputStream(), encoding)));
        dico = lectureDico(wordList, encoding);
    }
    
    private StringBuffer lectureDico(final URL u, final String encoding) throws IOException {
        final URLConnection conn = u.openConnection();
        final int clength = conn.getContentLength();
        StringBuffer sbuff;
        if (clength != -1)
            sbuff = new StringBuffer(clength);
        else
            sbuff = new StringBuffer();
        final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), encoding));
        final char[] cbuf = new char[1024];
        int l;
        while ((l = reader.read(cbuf)) != -1)
            sbuff.append(cbuf, 0, l);
        reader.close();
        if (clength == -1)
            sbuff.trimToSize();
        return(sbuff);
    }
    
    private void setPos(final int offset) {
        pos = offset;
    }
    
    private int getPos() {
        return(pos);
    }
    
    private String getLine() throws IOException {
        final int dicolength = dico.length();
        if (dico == null || dicolength <= pos)
            throw new IOException("getLine(): unable to read line");
        char c;
        int p = pos;
        boolean lend = false;
        while (p < dicolength && !lend) {
            c = dico.charAt(p);
            p++;
            lend = (c == '\n' || c == '\r');
        }
        final String line = dico.substring(pos, lend ? p-1 : p);
        if (p < dicolength && (dico.charAt(p) == '\n' || dico.charAt(p) == '\r'))
            p++;
        pos = p;
        return(line);
    }
    
    /**
     * Add a word permanently to the dictionary (and the dictionary file).
     * <i>not implemented !</i>
     * @param word The word to add.
     */
    public void addWord(String word) {
        System.err.println("error: addWord is not implemented for SpellDictionaryDichoMem");
    }
    
    /**
     * Search the dictionary file for the words corresponding to the code
     * within positions p1 - p2
     */
    private LinkedList<String> dichoFind(String code, int p1, int p2) throws IOException {
        //System.out.println("dichoFind " + code + " " + p1 + " " + p2);
        int pm = (p1 + p2) / 2;
        setPos(pm);
        String l = getLine();
        if (pos == dico.length())
            return(new LinkedList<String>());
        //System.out.println("l " + pm + ": " + l);
        pm = getPos();
        l = getLine();
        //System.out.println("l " + pm + ": " + l);
        final int pm2 = getPos();
        if (pm2 >= p2)
            return(seqFind(code, p1, p2));
        final int istar = l.indexOf('*');
        if (istar == -1)
            throw new IOException("bad format: no * !");
        final String testcode = l.substring(0, istar);
        final int comp = code.compareTo(testcode);
        if (comp < 0)
            return(dichoFind(code, p1, pm-1));
        else if (comp > 0)
            return(dichoFind(code, pm2, p2));
        else {
            LinkedList<String> l1 = dichoFind(code, p1, pm-1);
            LinkedList<String> l2 = dichoFind(code, pm2, p2);
            final String word = l.substring(istar+1);
            l1.add(word);
            l1.addAll(l2);
            //System.out.println("dichoFind: return " + l1);
            return(l1);
        }
    }
    
    private LinkedList<String> seqFind(String code, int p1, int p2) throws IOException {
        //System.out.println("seqFind("+code+","+p1+","+p2+")");
        final LinkedList<String> list = new LinkedList<String>();
        setPos(p1);
        while (getPos() < p2) {
            final String l = getLine();
            final int istar = l.indexOf('*');
            if (istar == -1)
                throw new IOException("bad format: no * !");
            final String testcode = l.substring(0, istar);
            if (testcode.equals(code))
                list.add(l.substring(istar+1));
        }
        //System.out.println("seqFind: return " + list);
        return(list);
    }
    
    /**
     * Returns a list of strings (words) for the code.
     * @param code The phonetic code common to the list of words
     * @return A list of words having the same phonetic code
     */
    public List<String> getWords(String code) {
        //System.out.println("getWords " + code);
        LinkedList<String> list;
        try {
            list = dichoFind(code, 0, dico.length()-1);
        } catch (IOException ex) {
            System.err.println("IOException: " + ex.getMessage());
            list = new LinkedList<String>();
        }
        //System.out.println("return " + list);
        return list;
    }
    
}

