/*
 * Board.java - 30 May 2003 - Version 1.0
 */

import OTN170108StringTools.StringTools;

import java.awt.*;
import java.applet.*;
import java.net.*;
import java.util.*;
import java.io.*;

//An applet class to display text in a fashion similar to
//computer terminals that we see only in the movies :-)
public class Board extends Applet implements Runnable
{
    final String MOREMESSAGE = "Más datos...";

    Thread kicker;
    Vector script = new Vector();
    Image image;
    boolean threadSuspended;
    boolean isRunning;

    //Configurable stuff...
    int width;
    int height;

    Color bgcolor;
    Color fgcolor;

    AudioClip sound;

    int cpause;
    int lpause;
    int spause;
    int lspace;
    int fontsize;
    int cursor;
    int indent;
    int initx;
    int inity;
    int maxy;

    boolean loop;

    Image bgimage;

    MediaTracker tracker;

    String sCadena;

    public String[][] getParameterInfo()
    {
        String[][] info = {
            {"width", "int", "width of the applet, in pixels"},
            {"height", "int", "height of the applet, in pixels"},
            {"bgcolor", "String", "RGB hex triplet for 'screen' background <black>"},
            {"fgcolor", "String", "RGB hex triplet for 'screen' foreground <green>"},
            {"sound", "String", "Sound clip file, relative to codebase <none>"},
            {"cpause", "int", "Delay between characters <25ms>"},
            {"lpause", "int", "Delay between lines <250ms>"},
            {"spause", "int", "Delay between repeats <500ms>"},
            {"loop", "int", "Non-zero causes applet to loop <1>"},
            {"script", "String", "Script file to display, relative to codebase <none>"},
            {"lspace", "int", "Line spacing <15>"},
            {"fontsize", "int", "Font size <12>"},
            {"cursor", "int", "Width of \"Cursor\" <3>"},
            {"indent", "int", "Indent value for text display in pixels <15>"},
            {"initx","int", "X-coord of \"Screen\" origin in applet panel <0>"},
            {"inity","int", "Y-coord of \"Screen\" origin in applet panel <0>"},
            {"bgimage", "String", "Background image file, relative to codebase <none>"},
            {"maxy","int","Lower limit of text display, zero uses applet height <0>"}
        };
        return info;
    }

    public String getAppletInfo()
    {
        return("Board Applet.");
    }


    public void init()
    {

        super.init();

        width = getSize().width;
        height = getSize().height;

        String param = getParameter("bgcolor");
        bgcolor = parseColorString(param, Color.black);

        param = getParameter("fgcolor");
        fgcolor = parseColorString(param, Color.green);

        param = getParameter("sound");
        sound = getClip(param);

        param = getParameter("cpause");
        cpause = getInt(param,25);

        param = getParameter("lpause");
        lpause = getInt(param,250);

        param = getParameter("spause");
        spause = getInt(param,500);

        param = getParameter("loop");
        loop = (getInt(param,1) > 0)? true : false;

        param = getParameter("lspace");
        lspace = getInt(param,15);

        param = getParameter("fontsize");
        fontsize = getInt(param,12);

        param = getParameter("cursor");
        cursor = getInt(param,3);

        param = getParameter("indent");
        indent = getInt(param,0);

        param = getParameter("initx");
        initx = getInt(param,0);

        indent += initx;

        param = getParameter("inity");
        inity = getInt(param,0);

        param = getParameter("maxy");
        maxy = getInt(param,height);


        param = getParameter("bgimage");
        if(null != param){
            try{
                bgimage = getImage(getCodeBase(),param);
                tracker = new MediaTracker(this);
                tracker.addImage(bgimage,0);
            }catch(Exception e){
                bgimage = null;
            }
        }

        sCadena = getParameter("script");
        if(sCadena == null)
        {
            System.out.println("Board: Sin datos.");
            return;
        }

    }

    public void start()
    {
        if(null == kicker){
            kicker=new Thread(this);
            kicker.start();
        }
    }

    public void stop()
    {
        if(null != kicker){
            kicker.interrupt();
            //kicker.stop();
            kicker=null;
        }
    }

    private AudioClip getClip(String s)
    {
        AudioClip clip = null;
        try{
            URL url = new URL(getCodeBase(),s);
            clip = getAudioClip(url);
        }catch(Exception e){
            System.out.println(e);
            clip=null;
        }
        return clip;
    }

    private Color parseColorString(String colorString, Color dflt)
    {
        Color color;

        try{
            colorString = colorString.replace('#',' ').trim();
            int R = Integer.valueOf(colorString.substring(0,2),16).intValue();
            int G = Integer.valueOf(colorString.substring(2,4),16).intValue();
            int B = Integer.valueOf(colorString.substring(4,6),16).intValue();
            color = new Color(R,G,B);
        }catch(Exception e){
            color = dflt;
        }

        return color;
    }

    private int getInt(String s, int dflt)
    {
        int val;

        try{
            val = Integer.parseInt(s);
        }catch(Exception e){
            val = dflt;
        }
        return val;
    }

    private void getScript()
    {
        String textline;
        int iMaxPos = 60;

        try
        {
            int iIt = 0;
            StringTools ostr = new StringTools();
            String[] strencat = ostr.splitStr(sCadena, "|");
            while (iIt < strencat.length)
            {
                String sAux = strencat[iIt];
                if (sAux.length() > iMaxPos)
                {
                    int i = 0;
                    while (i < sAux.length())
                    {
                        script.addElement(sAux.substring(i, iMaxPos));
                        i = i + iMaxPos;
                    }
                }
                else
                    script.addElement(sAux);

                script.addElement("<newline>");
                iIt++;
            }

//            int iIt = 0;
//            while (iIt < file.length())
//            {
//                script.addElement(file.substring(iIt, 20));
//                iIt = iIt + 20;
//            }


//            URLConnection urlConnection = new URL(getCodeBase(),file).openConnection();
//
//             //In JDK 1.1, this should be replaced with BufferedReader.readLine, however
//             //the following works with both 1.02 and 1.1...
//             DataInputStream in = new DataInputStream(urlConnection.getInputStream());
//
//             while(null != (textline = in.readLine())){
//                script.addElement(textline.trim());
//             }

        }catch (Exception e){
            script.removeAllElements();
            script.addElement("No se han podido leer los datos.");
        }
    }


    private void repaintScreen(Graphics g)
    {
        //We call this to refresh our buffered image
        //prior to typing text onto it...
        if(null == bgimage){
            g.setColor(bgcolor);
            g.fillRect(0, 0, width, height);
        }
        else{
            g.drawImage(bgimage,0,0,this);
        }
    }

    private int displayString(Graphics g, String string, int y, int w, int ascent)
    {
        //Step through the string, display each character with the sound effect
        //Paint a cursor as we do, and sleep for the character pause interval
        //in between each character.
        //
        //Return the Y coord for the next line of text.
        for(int j = 0; j < string.length(); j++){
            if(null != sound)
                sound.play();
            g.setColor(fgcolor);
            g.drawString(string.substring(j,j+1),indent + (j)*w, y);
            g.fillRect(indent + (j+1)*w, y-ascent, cursor, fontsize);
            try{
                kicker.sleep(cpause);
            }catch(InterruptedException e){}
            g.setColor(bgcolor);
            g.fillRect(indent + (j+1)*w, y-ascent, cursor, fontsize);
            repaint();
        }
        return y+lspace;

    }

    public void run()
    {
        //Wait for a background image, if any...
        if(null != bgimage){
            try{
                tracker.waitForAll();
            }catch(Exception e){
                bgimage = null;
            }
        }

        //Create our buffered image
        image= createImage(width, height);

        //Get the graphics context for the buffered image
        Graphics g = image.getGraphics();

        //Put it up...
        repaint();

        //Now, get the script (if necessary.)
        if(script.isEmpty())
            getScript();

        //Set a teletype font, and get some font metrics
        g.setFont(new Font("Courier", Font.PLAIN, fontsize));
        FontMetrics fm = g.getFontMetrics();
        int w = fm.stringWidth("A");
        int ascent = fm.getMaxAscent();

        //We've got our image and file, so we can now set isRunning to true.
        //This is used to see if we are ready to respond to mouseDown events
        //(see below)
        isRunning=true;

        try
        {
            while(kicker != null){

                repaintScreen(g);

                int Y = inity + lspace;

                for(int i = 0; i < script.size(); i++){

                    String string = (String)script.elementAt(i);

                    //Handle a NEWLINE "tag"
                    if(string.toLowerCase().equals("<newline>")){
                        if(Y != inity + lspace)
                            Y += lspace;
                    }

                    //Handle a NEWPAGE "tag"
                    else if(string.toLowerCase().equals("<newpage>")){
                        repaintScreen(g);
                        Y = inity + lspace;
                    }

                    //Handle a PAUSE "tag" (parse out the value) and sleep
                    //for the requested duration
                    else if(string.toLowerCase().startsWith("<pause")){
                        int pause;
                        int start=string.indexOf(" ");
                        int end = string.indexOf(">");
                        start++;
                        try{
                            pause=Integer.parseInt(string.substring(start,end));

                            if(pause == 0){
                                threadSuspended=true;
                                //kicker.suspend();
                                kicker.wait();
                            }
                            else{
                                kicker.sleep(pause);
                            }
                        }catch(Exception e){}
                    }

                    //Otherwise, handle a message line.
                    else{

                        if(string.toLowerCase().equals("<more>")){
                            string = MOREMESSAGE;
                        }

                        Y = displayString(g, string, Y, w, ascent);

                        //Are we near enough to the bottom of the display to
                        //show a "More..." (and is it appropriate, i.e. is
                        //there more text to display?)
                        if((Y + 2*lspace) > maxy && i+1 < script.size()){
                            Y+=lspace;
                            string=MOREMESSAGE;
                            displayString(g, string, Y, w, ascent);
                        }

                        //Did we just display "More..."?
                        if(string.equals(MOREMESSAGE)){
                            //This will force a new page after
                            //the thread resumes
                            Y += maxy;
                            threadSuspended=true;
                            //kicker.suspend();
                            kicker.wait();
                        }

                        //Otherwise, if we simply displayed a text line,
                        //sleep for the line pause interval.
                        else{
                            try{
                                kicker.sleep(lpause);
                            }catch(InterruptedException e){}
                        }
                    }
                    //Do we need to start a new "Page?"
                    if(Y > maxy){
                        repaintScreen(g);
                        Y = inity + lspace;
                    }
                }

                //Is unattended looping enabled? If it is, sleep for
                //a screen pause interval.
                if(loop){
                    try{
                       kicker.sleep(spause);
                    }catch(InterruptedException e){}
                }

                //Otherwise, suspend until we get a mouseDown event
                else{
                    threadSuspended=true;
                    //kicker.suspend();
                    kicker.wait();
                }
            }
        }
        catch(InterruptedException e){}
    }

    public synchronized void processMouseEvent(java.awt.event.MouseEvent e)
    {
        //Only respond to mouseDown if the applet has its image and file
        //and is running...
        try
        {
            if(isRunning){
                if (threadSuspended) {
                    kicker.notify();
                    //kicker.resume();
                }
                else {
                    kicker.wait();
                    //kicker.suspend();
                }
                threadSuspended = !threadSuspended;
            }
        }
        catch (InterruptedException ex)
        {}
    }


    public void paint(Graphics g)
    {
        //If we've got an image, draw it...
        if(null != image)
            g.drawImage(image,0,0,this);

        //Otherwise, paint the applet panel the specified
        //background color...
        else{
            g.setColor(bgcolor);
            g.fillRect(0,0,getSize().width,getSize().height);
        }
    }

    public void update(Graphics g)
    {
        //Override update to call our paint method
        //to eliminate flickering.
        paint(g);
    }

}
