/***************************************************************************
 * File	    : TOEFL Section2.java (C) Copyright 1999                       *
 * Date     : 1999.7.14                                                    *
 * Author   : Ryo UMETSU                                                   *
 * Email    : u-ryo@sipeb.aoyama.ac.jp                                     *
 ***************************************************************************/

import java.awt.*;
import java.applet.*;
import java.util.*;
import java.io.*;
import java.net.*;
import java.text.*;

public class Section2 extends Applet {
    private Font f;
    private Selection selection;
    private Lesson lesson;
    private Panel panel = new Panel();
    private CardLayout card = new CardLayout();

    public void init() {
        setLayout(card);
        add("selection", panel);
        panel.setLayout(new BorderLayout());
        panel.add("Center", selection = new Selection(this));
        String param = getParameter("fontSize");
        f = new Font("TimesRoman", Font.PLAIN,
                     (param == null ? 20 : Integer.parseInt(param)));
 
        for (int i=0;;i++) {
            param = getParameter("lesson" +i);
            if (param != null)
                selection.addItem(param);
            else
                break;
        }
        card.show(this, "selection");
    }
    public void selectLesson(String fileName) {
        lesson = new Lesson(f, fileName, getCodeBase().toString(), this);
        Panel pan = new Panel();
        pan.setLayout(new BorderLayout());
        pan.add("Center", lesson);
        add("lesson", pan);
        card.show(this, "lesson");
    }
    public void back() {
        if (lesson != null) {
            remove(lesson);
            lesson = null;
            card.show(this, "selection");
        }
    }
}

class Selection extends Panel {
    private Choice lessonChoice = new Choice();
    private Section2 section2;

    public Selection(Section2 sec) {
        section2 = sec;
        setLayout(new GridLayout(7, 1));
        Panel pan[] = new Panel[4];
        for (int i=0; i<pan.length; i++) {
            pan[i] = new Panel();
            add(pan[i]);
        }
        Color bg = new Color(240, 246, 255);
        setBackground(bg);

        setFont(new Font("TimesRoman", Font.BOLD, 24));
        Label toefl, section;
        pan[0].add("South", toefl = new Label("TOEFL"));
        pan[1].add("Center", section = new Label("SECTION 2"));
        toefl.setForeground(new Color(255, 100, 100));
        section.setForeground(new Color(100, 255, 100));

        pan[2].add(new Label("Select the LESSON No.:"));
        pan[2].add(lessonChoice);
        lessonChoice.setBackground(bg);

        add("Center", pan[3]);
        pan[3].add(new Button("Start!!"));
    }
    public void addItem(String param) {
        lessonChoice.addItem(param);
    }
    public boolean action(Event evt, Object obj) {
        if (evt.target instanceof Button)
            section2.selectLesson(lessonChoice.getSelectedItem());
        return true;
    }
}

class Lesson extends Panel implements Runnable {
    private CardLayout card, sectionCard;
    private Panel pan[] = new Panel[8];
    private TextField dispLimit;
    private Label dispTime, result;
    private Button stopButton, answerButton, nextButton;
    private CheckboxGroup questionGroups[] = new CheckboxGroup[2];
    private Font f;
    private URL theURL;
    private String data[][] = new String[2][];
    private Questions questions[][] = new Questions[2][];
    private Checkbox questionGroup[][] = new Checkbox[2][];
    private Thread runner;
    private Date startTime;
    private int limitTime = 25;
    private Comments commentsDialog;
    private Section2 section2;

    public Lesson(Font ff, String fileName, String codeBase,
                  Section2 sec) {
        f = ff;
        section2 = sec;
        setFont(f);
        data[0] = 
            readFile(codeBase + "structure" + fileName + ".dat");
        data[1] =
            readFile(codeBase + "written" + fileName + ".dat");
        setLayout(new BorderLayout());
        for (int i=0; i<pan.length; i++)
            pan[i] = new Panel();
        makeCtlPanel();
        makeCardPanel();
        makeCheckboxPanel();
        makeCheckbox("Structure");
        makeCheckbox("Written");
        sectionCard.show(pan[3], "Structure");
        card.show(pan[7], questionGroups[0].getCurrent().getLabel());

        startTime = new Date();
        if (runner == null); {
            runner = new Thread(this);
            runner.start();
        }
    }
    private void makeCtlPanel() {
        add("North", pan[0]);
        pan[0].setLayout(new GridLayout(2, 1));
        pan[0].add(pan[1]);
        Color bg = new Color(100, 200, 232);
        pan[1].setBackground(bg);
        pan[1].add(new Label("Limit Time", Label.RIGHT));
        pan[1].add(dispLimit = new TextField("25", 2));
        pan[1].add(dispTime = new Label("25:00"));
        dispTime.setForeground(Color.red);
        pan[1].add(stopButton = new Button("Stop Timer"));
        pan[1].add(answerButton = new Button("Answer"));
        pan[1].add(result = new Label("Score 00(00/40)"));
        result.setForeground(Color.red);
        pan[1].add(new Button("QUIT!"));
    }
    private void makeCardPanel() {
        pan[7].setBackground(Color.white);
        add("Center", pan[7]);
        pan[7].setLayout(card = new CardLayout());
    }
    private void makeCheckboxPanel() {
        pan[0].add("Center", pan[2]);
        pan[2].add("Center", pan[3]);
        pan[2].add("East", pan[6]);
        pan[6].add(nextButton = new Button("to Next"));
        pan[3].setLayout(sectionCard = new CardLayout());
    }
    private void makeCheckbox(String section) {
        boolean s;
        if (section.equals("Structure"))
            s = true;
        else
            s = false;
        Color pbg = new Color(238, 255, 242);
        pan[3].add(section, pan[s ? 4 : 5]);
        pan[s ? 4 : 5].add(new Label(s ? "Structure:" :
                                     "Written Expr:", Label.CENTER));
        pan[s ? 4 : 5].setBackground(pbg);
        pan[2].setBackground(pbg);
        pan[3].setBackground(pbg);
        pan[6].setBackground(pbg);
        int perQ = (s ? 3 : 5); // num per Question
        int c = (s ? 0 : 1);
        int from = (s ? 0 : 15); // pre perQ
        int num = (s ? (int)(15/perQ) : (int)(25/perQ));
        questionGroups[c] = new CheckboxGroup();
        questionGroup[c] = new Checkbox[num];
        questions[c] = new Questions[num];
        for (int i=0; i<num; i++) {
            String temp = Integer.toString(i*perQ+1+from);
            temp += "-";
            temp += Integer.toString((i+1)*perQ+from);
            pan[7].add(temp, questions[c][i] =
                       new Questions(data[c], i*perQ+1+from,
                                     (i+1)*perQ+from, f));
            questionGroup[c][i] =
                new Checkbox(temp, questionGroups[c],
                             (i==0? true : false));
            pan[s ? 4 : 5].add(questionGroup[c][i]);
            questionGroup[c][i].setBackground(pbg);
        }
    }
    private String[] readFile(String fileName) {
        try {
            theURL = new URL(fileName);
        } catch (Exception e) {
            System.out.println("Unable to open URL");
        }
        DataInputStream ds = null;
        InputStream conn = null;
        try {
            conn = theURL.openStream();
            ds = new DataInputStream(new BufferedInputStream(conn));
        } catch (IOException e) {
            System.out.println(e);
        }
        String s;
        Vector vect = new Vector();
        try {
            while ((s = ds.readLine()) != null)
                vect.addElement(new String(s));
        } catch (IOException e) {
            System.out.println("Exception caught reading file.");
        }
        String data[] = new String[vect.size()];
        for (int i=0; i<vect.size(); i++)
            data[i] = String.valueOf(vect.elementAt(i));
        return data;
    }
    public boolean action(Event evt, Object obj) {
        if (evt.target instanceof Checkbox) {
            int i =
                (nextButton.getLabel().equals("to Next") ? 0 : 1);
            card.show(pan[7], questionGroups[i].getCurrent().getLabel());
            if (commentsDialog != null)
                commentsDialog.changeComments(getComments());
        } else if (evt.target instanceof Button) {
            if (obj.equals("Stop Timer")) {
                if (commentsDialog == null) {
                    stop();
                    //answerButton.setEnabled(true);
                    stopButton.setLabel("Restart");
                }
            } else if (obj.equals("Restart")) {
                if (commentsDialog == null) {
                    stopButton.setLabel("Stop Timer");
                    runner = new Thread(this);
                    runner.start();
                    startTime = new Date();
                    //answerButton.setEnabled(false);
                }
            } else if (obj.equals("Answer")) {
                if (commentsDialog == null) {
                    if (runner == null) {
                        calcScore();
                        //stopButton.setEnabled(false);
                    } else {
                        Warning warn;
                        warn = new Warning();
                    }
                } else
                    if (!commentsDialog.isShowing())
                        commentsDialog.show();
            } else if (obj.equals("QUIT!")) {
                stop();
                if (commentsDialog != null) {
                    commentsDialog.dispose();
                    commentsDialog = null;
                }
                section2.back();
            } else if (evt.target.equals(nextButton)) {
                if (obj.equals("to Next")) {
                    sectionCard.next(pan[3]);
                    nextButton.setLabel("Previous");
                    card.show(pan[7],
                              questionGroups[1].getCurrent().getLabel());
                } else if (obj.equals("Previous")) {
                    sectionCard.previous(pan[3]);
                    nextButton.setLabel("to Next");
                    card.show(pan[7],
                              questionGroups[0].getCurrent().getLabel());
                }
                if (commentsDialog != null)
                    commentsDialog.changeComments(getComments());
                //repaint();
            }
        }
        return true;
    }
    private void calcScore() {
        int score = 0;
        for (int i=0; i<2; i++)
            for (int j=0; j<questions[i].length; j++) {
                questions[i][j].stop();
                for (int k=0; k<questions[i][j].getResult().length; k++)
                    if (questions[i][j].getResult()[k])
                        score++;
            }
        int conversion[] =
        { 19, 20, 22, 24, 25, 26, 28, 29, 30, 32, 33, 34, 35, 36, 37,
          38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
          53, 54, 55, 57, 58, 59, 61, 63, 64, 66, 67 };
        result.setText("Score" + conversion[score] + "(" + score + "/40)");
        commentsDialog = new Comments("Your score is " + score + "/40",
                                      getComments());
    }
    public void stop() {
        if (runner != null) {
            //stopButton.setEnabled(false);
            runner.stop();
            runner = null;
        }
    }
    public void run() {
        while(true){
            Date nowTime = new Date();
            try {
                limitTime = Integer.parseInt(dispLimit.getText());
            } catch (Exception e) { }
            int time = (int)(startTime.getTime() - nowTime.getTime()) / 1000
                + limitTime * 60;
            DecimalFormat df = new DecimalFormat("00");
            dispTime.setText(df.format(time/60) + ":" + df.format(time%60));
            if (nowTime.getTime() >= startTime.getTime()+limitTime*60000) {
                calcScore();
                stop();
            }
            try { Thread.sleep(1000); }
            catch (InterruptedException e) { }
        }
    }
    public String getComments() {
        int i =
            (nextButton.getLabel().equals("to Next") ? 0 : 1);
        for (int j=0; j<questions[i].length; j++)
            if (questionGroups[i].getCurrent() == questionGroup[i][j])
                return questions[i][j].getComments();
        return null;
    }
}
class Questions extends Panel {
    private CheckboxGroup qs[];
    private Checkbox alt[][];
    private String questions[];
    private String answers[][];//0=Num, 1=Right, 2=Answered
    private Font f;
    private boolean stop = false;
    private String comments[];
    private double yy = 1.0;

    public Questions(String data[], int from, int to, Font ff) {
        setLayout(null);
        setFont(ff);
        f = ff;
        qs = new CheckboxGroup[to - from + 1];
        alt = new Checkbox[qs.length][4];
        String string[] = { "A", "B", "C", "D" };
        for (int i=0; i<=(to-from); i++) {
            qs[i] = new CheckboxGroup();
            for (int j=0; j<string.length; j++) {
                alt[i][j] =
                    new Checkbox(string[j], qs[i], false);
                add(alt[i][j]);
                alt[i][j].setBackground(Color.white);
            }
        }
        analysis(data, from, to);
        setBackground(Color.white);
    }
    private void analysis(String data[], int from, int to) {
        int fromto[] = { 0, 0, 0, 0, 0, 0 };
        // dataFrom, dataTo, answerFrom, answerTo, commentFrom, commentTo
        String t = Integer.toString(from);
        String tt = Integer.toString(to + 1);
        int flag = 0;
        for (int i=0; i<data.length-1; i++) {
            if (data[i].length() > 4 &&
                data[i].substring(0, 4).equals("----"))
                flag++;
            for (int j=0; j<fromto.length/2; j++) {
                if (flag == j &&
                    checkHead(data[i], true) &&
                    data[i].substring(0, t.length()).equals(t) &&
                    data[i].charAt(t.length()) == '.')
                    fromto[j * 2] = i;
                if (flag == j &&
                    ((checkHead(data[i], true) &&
                      data[i].substring(0, tt.length()).equals(tt) &&
                      data[i].charAt(tt.length()) == '.') ||
                     ((to == 15 || to == 40)
                      && data[i+1].length() > 4 &&
                      data[i+1].substring(0, 4).equals("----"))))
                    fromto[j * 2 + 1] = i;
                }
        }
        questions = new String[fromto[1] - fromto[0]];
        answers = new String[fromto[3] - fromto[2]][3];
        comments = new String[fromto[5] - fromto[4]];
        for (int i=fromto[0]; i<fromto[1]; i++)
            questions[i-fromto[0]] = data[i];
        for (int i=fromto[2]; i<fromto[3]; i++) {
            answers[i-fromto[2]][0] = Integer.toString(from++);
            answers[i-fromto[2]][1] =
                String.valueOf(data[i].charAt(data[i].length() - 1));
        }
        for (int i=fromto[4]; i<fromto[5]; i++)
            comments[i-fromto[4]] = data[i];
    }
    static boolean checkHead(String data, boolean isNum) {
        if (isNum) {
            if (data.length() >= 2) {
                int temp = data.indexOf(".");
                if (temp == 1 || temp == 2) {
                    try {
                        Integer.parseInt(data.substring(0, temp));
                    } catch (Exception e) {
                        return false;
                    }
                    temp++;
                    if (temp == data.length() ||
                        data.charAt(temp) == ' ')
                        return true;
                } else
                    return false;
            }
        } else
            if (data.length() >= 3)
                if (data.charAt(0) == '(' &&
                    data.charAt(2) == ')' &&
                    (data.charAt(1) == 'A' ||
                     data.charAt(1) == 'B' ||
                     data.charAt(1) == 'C' ||
                     data.charAt(1) == 'D'))
                    return true;
                else
                    return false;
        return false;
    }
    public void stop() {
        stop = true;
        for (int i=0; i<qs.length; i++)
            if (qs[i].getCurrent() != null)
                answers[i][2] = qs[i].getCurrent().getLabel();
            else
                answers[i][2] = "";
        repaint();
    }
    public boolean[] getResult() {
        boolean result[] = new boolean[qs.length];
        for (int i=0; i<qs.length; i++)
            if (answers[i][1].equals(answers[i][2]))
                result[i] = true;
            else
                result[i] = false;
        return result;
    }
    public void paint(Graphics g) {
        //setFont(f);
        FontMetrics fm = getFontMetrics(f);
        int x = 10;
        int y = -(fm.getHeight() * 2) / 3;
        int count[] = { 0, 0 };
        double temp = yy;
        try {
            if (Integer.parseInt(questions[0].
                                 substring(0, questions[0].indexOf(".")))
                > 15) {
                yy = drawWritten(x, y, yy, count, g, fm);
                if (yy < temp)
                    repaint();
            } else
                yy = drawStructure(x, y, yy, count, g, fm);
                if (yy < temp)
                    repaint();
        } catch (Exception e) {
            System.out.println(e);
        }
        for (int i=0; i<qs.length; i++) {
            Checkbox current = qs[i].getCurrent();
            for (int j=0; j<alt[i].length; j++)
                qs[i].setCurrent(alt[i][j]);
            qs[i].setCurrent(current);
        }
    }
    private double drawStructure(int x, int y, double yy, int count[],
                               Graphics g, FontMetrics fm) {
        for (int i=0; i<questions.length; i++) {
            StringTokenizer tokens =
                new StringTokenizer(questions[i]);
            while (tokens.hasMoreTokens()) {
                String s = tokens.nextToken();
                if (s.length() >= 3 && s.substring(0, 3).equals("___"))
                    s = "____" + s;
                if (x + fm.stringWidth(s) > this.size().width ||
                    checkHead(s, false) || checkHead(s, true)) {
                    if (checkHead(s, true)) {
                        y += (int)(fm.getHeight() * yy);
                        x = 10;
                    } else
                        x = 30;
                    y += (int)(fm.getHeight() * yy);
                }
                if (checkHead(s, false)) {
                    if (!stop) {
                        Dimension d =
                            alt[count[0]][count[1]].preferredSize();
                        int h = d.height;
                        if (yy < 1.0)
                            h *= yy;
                        alt[count[0]][count[1]].reshape(10,
                                                        y - fm.getHeight(),
                                                        d.width, h);
                    } else if (stop) {
                        if (alt[count[0]][count[1]] != null)
                            remove(alt[count[0]][count[1]]);
                        if (answers[count[0]][1].equals(s.substring(1, 2))) {
                            g.setColor(Color.red);
                            if (answers[count[0]][1].equals(answers[count[0]][2]))
                                g.drawOval(20, y-fm.getHeight(),
                                           fm.stringWidth("OO"),
                                           fm.getHeight()+10);
                        }
                        if (answers[count[0]][2].equals(s.substring(1, 2)))
                            g.setColor(Color.blue);
                        g.drawString(s.substring(1, 2), 30, y);
                        g.setColor(Color.black);
                    }
                    count = countUp(count);
                } else {
                    g.drawString(s, x, y);
                }
                x += fm.stringWidth(s + " ");
            }
        }
        if ((y + fm.getHeight()/2)> this.size().height)
            return yy * 0.95;
        else
            return yy;
    }
    private double drawWritten(int x, int y, double yy, int count[],
                               Graphics g, FontMetrics fm) {
        int isQst[] = { 0, 0 };
        String h = "dummy";
        for (int i=0; i<questions.length; i++) {
            StringTokenizer tokens =
                new StringTokenizer(questions[i]);
            while (tokens.hasMoreTokens()) {
                String s = tokens.nextToken();
                if (x + fm.stringWidth(s) > this.size().width ||
                    checkHead(s, true)) {
                    if (checkHead(s, true))
                        x = 10;
                    else
                        x = 30;
                    y += (int)(fm.getHeight() * yy) * 2;
                }
                if (checkHead(s, false)) {
                    h = s.substring(1, 2);
                    s = s.substring(4);
                    isQst[0]++;
                }
                if (s.indexOf(']') > 0) {
                    int m = 0;
                    s = s.substring(0, s.indexOf(']')) +
                        s.substring(s.indexOf(']') + 1);
                    if (isQst[0] > 1)
                        m = fm.stringWidth("  ");
                    g.drawLine(x - m, y+3, x+fm.stringWidth(s), y+3);
                    m = 0;
                    if (isQst[0] > 1 &&
                        x - (isQst[1] + fm.stringWidth("  ") * (isQst[0] - 1))
                        >= 30)
                        m -= (isQst[1] +
                              fm.stringWidth("  ") * (isQst[0] - 1)) / 2;
                    if (!stop) {
                        Dimension d = alt[count[0]][count[1]].preferredSize();
                        int n = (fm.stringWidth(s) - d.width) / 2 + m;
                        alt[count[0]][count[1]].
                            reshape(x + n, y + 5,
                                    d.width, fm.getHeight());
                    } else if (stop) {
                        int n = (fm.stringWidth(s) -
                                 fm.stringWidth("A")) / 2 + m;
                        if (alt[count[0]][count[1]] != null)
                            remove(alt[count[0]][count[1]]);
                        if (answers[count[0]][1].equals(h)) {
                            g.setColor(Color.red);
                            if (answers[count[0]][1].equals(answers[count[0]][2]))
                                g.drawOval(x+n-8, y,
                                           fm.stringWidth("OO"),
                                           fm.getHeight()+10);
                        }
                        if (answers[count[0]][2].equals(h))
                            g.setColor(Color.blue);
                        g.drawString(h, x + n, y + fm.getHeight());
                        g.setColor(Color.black);
                    }
                    count = countUp(count);
                    isQst[0] = 0;
                    isQst[1] = 0;
                }
                if (isQst[0] > 0) {
                    int m = 0;
                    if (isQst[0] > 1)
                        m = fm.stringWidth("  ");
                    g.drawLine(x - m, y+3, x+fm.stringWidth(s), y+3);
                    isQst[0]++;
                    isQst[1] += fm.stringWidth(s);
                }
                g.drawString(s, x, y);
                x += fm.stringWidth(s + "  ");
           }
        }
        if ((y + fm.getHeight()/2)> this.size().height)
            return yy * 0.98;
        else
            return yy;
    }
    private int[] countUp(int count[]) {
        if (count[1] >= 3) {
            count[1] = 0;
            count[0]++;
        } else
            count[1]++;
        return count;
    }
    public String getComments() {
        String string = "";
        for (int i=0; i<comments.length; i++) {
            string += comments[i] + "\n";
            System.out.println(comments[i]);
        }
        System.out.println("=============================================");
        byte by[] = string.getBytes();
        try {
            String result = new String(by, "JIS");
            return result;
        } catch (Exception e) {
            System.out.println(e);
        }
    return null;
    }
}
class Comments extends Frame {
    private Panel pan[] = new Panel[3];
    private TextArea textArea;

    public Comments(String score, String comments) {
        super(score);
        setFont(new Font("Courier", Font.PLAIN, 18));
        setLayout(new BorderLayout());
        for (int i=0; i<pan.length; i++) {
            pan[i] = new Panel();
        }
        add("Center", textArea =
                   new TextArea(comments));
        textArea.setEditable(false);
        add("South", pan[0]);
        pan[0].setLayout(new GridLayout(2, 1));
        pan[0].add(new Label("(If you can't read the comments, see the java console)", Label.CENTER));
        pan[0].add(pan[2]);
        pan[2].add(new Button("OK"));
        resize(650, 350);
        show();
    }
    public void changeComments(String comments) {
        textArea.setText("");
        textArea.setText(comments);
    }
    public boolean action(Event evt, Object arg) {
        if (evt.id == Event.WINDOW_DESTROY)
            dispose();
        else if (evt.target instanceof Button)
            dispose();
        return true;
    }
}
class Warning extends Frame {
    private Panel pan[] = new Panel[2];

    public Warning() {
        super("Warning");
        setFont(new Font("Courier", Font.PLAIN, 18));
        setLayout(new GridLayout(2, 1));
        for (int i=0; i<pan.length; i++) {
            pan[i] = new Panel();
        }
        add(pan[0]);
        pan[0].add(new Label("Please stop the timer, first!",
                             Label.CENTER));
        add(pan[1]);
        pan[1].add(new Button("OK"));
        resize(350, 150);
        show();
    }
    public boolean action(Event evt, Object arg) {
        if (evt.id ==Event.WINDOW_DESTROY)
            dispose();
        else if (evt.target instanceof Button)
            dispose();
        return true;
    }
}
