import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
//import coco.*;

/*coco client program that enables the user to download a coco page
  and then watch it as other watchers change it; allow the user to
  submit changes to that page via pressing one of the submit buttons
  on the page*/
class CocoClient extends JFrame{
  /*the listener that will submit local user's requests to the page
    server whenever one of the submitters of the downloaded page
    activated*/
  class CocoSubmitListener implements ActionListener{
    public void actionPerformed(ActionEvent e){
      /*get contents of the form as command string*/
      CocoSubmitter sr=(CocoSubmitter)e.getSource();
      String cs=sr.getParams();

      /*if no form, the awt command string is used*/
      if(cs==null)
	cs=e.getActionCommand();

      /*send command string to the coco server*/
      String rs="REFRESH "+cs;
      System.out.println("sending message: "+rs);
      socOut.println(rs);
      System.out.println("message sent");
    }
  }

  /*listen to the user typing a new address of a page to download*/
  class CocoWatchListener implements ActionListener{
    public void actionPerformed(ActionEvent e){
      load();
    }
  }

  /*receive packets coming from the server; uses socIn, socOut and soc
    fields, but does _not_ open or close them*/
  class NetListen implements Runnable{
    CocoPage page;
  
    public NetListen(CocoPage dedicated){
      super();
      
      page=dedicated;
    }

    public void run(){
      /*request string*/
      String rs=null;
	  
      /*debug message*/
      System.out.println("starting refresh loop");

      refreshloop:
      while(true){
        /*read line from the stream socket*/
        try{
	  rs=socIn.readLine();
	}catch(SocketException ex){
          /*debug message*/
          System.out.println("stopping refresh loop, connection unexpectedly closed");
	  break refreshloop;
	}catch(IOException ex){
          /*error message*/
          System.out.println("error receiving message "+ex.toString());
	}
	
	/*stop refresh loop if closed*/
        if(rs==null)break refreshloop;

        /*debug message*/
        System.out.println("message received: "+rs);

	/*calculate beginning of the keyword (first word of the packet)*/
	int keywordbeg=0;
	while(
	  keywordbeg<rs.length()&&
	  Character.isWhitespace(rs.charAt(keywordbeg))
	)keywordbeg++;

	//walking through the first word until reaching first space
	int keywordend=keywordbeg;
	while(
	  keywordend<rs.length()&&
	  !Character.isWhitespace(rs.charAt(keywordend))
	)keywordend++; 

	/*the keyword*/
	String kw=rs.substring(keywordbeg,keywordend);

	/*the rest*/
	String cs=rs.substring(keywordend);

	/*process a refresh message*/
	if(kw.equals("REFRESH")){
	  /*find the colon separating the addressee from the command*/
	  int coloni=cs.indexOf(':');
          if(coloni<0){
	    /*error message*/
            System.out.println("received refresh message is corrupted");

	    continue refreshloop;
          }else{
	    /*separate the command string*/
	    String id=cs.substring(0,coloni).trim();
	    String request=cs.substring(coloni+1);

	    /*send the request*/
	    page.requestOn(id,request);
	  }
	}
	
	else{
	  /*error message*/
	  System.out.println("unknown message received");

	  continue refreshloop;
	}
      }//end of refresh loop

      /*debug message*/
      System.out.println("refresh loop normal exit");
    }//end of method run
  }//end of subclass NetListen

  /*listener instances*/
  CocoSubmitListener csl=new CocoSubmitListener();
  CocoWatchListener cwl=new CocoWatchListener();

  /*the address bar ensures input for an address
    where to download a page from*/
  TextField address=new TextField("http://localhost/index.cjd");
  
  /*URL object that decodes address*/
  URL urlAddress;

  /*the downloaded page*/
  CocoPage page;

  /*thread that will receive messages coming from the page server*/
  Thread netListen;

  /*the stream socket to communicate on with the page server*/
  Socket soc=null;
  
  /*the stream socket input to listen on*/
  BufferedReader socIn=null;

  /*the stream socket output for sending messages to the page server*/
  PrintWriter socOut=null;

  /*stashed content pane of the frame*/
  Container cp;

  /*program start method*/
  public static void main(String[] arg){
    /*first argument is the page to download first*/
    if(arg.length>0)new CocoClient(arg[0]);

    /*or nothing*/
    else new CocoClient();
  }

  /*constructor without arguments*/
  public CocoClient(){
    /*window general settings*/
    super("Coco Client");
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    /*stash the content pane*/
    cp=getContentPane();

    /*set up the address bar*/
    cp.add(address,"North");
    address.addActionListener(cwl);

    /*show window*/
    pack();
    show();
  }

  /*constructor to begin with downloading a given page*/
  public CocoClient(String s){
    this();

    /*page address will be shown by and forwarded through
      the address bar*/
    address.setText(s);

    /*do load the page from the address shown by the address bar*/
    load();
  }
  
  /*load the page from the address shown by the address bar*/
  private void load(){
    /*get that address*/
    String as=address.getText();
    
    /*create the conforming url object*/
    try{
      urlAddress=new URL(as);
    }catch(MalformedURLException ex){
      JOptionPane.showMessageDialog(
	this, //parentComponent
        "A beírt szöveg nem érvényes URL!", //message
	"Címsor átírva", //title
	JOptionPane.ERROR_MESSAGE //messageType
      );
      return;
    }

    /*clear old page, if any, and then display wait message instead*/
    if(page!=null)cp.remove(page);
    page=null;
    Label wait=new Label(
      "Letöltjük a kért oldalt. (Ezt: "+
      urlAddress.getPath()+
      " innen: "+
      urlAddress.getHost()+
      ".)"
    );
    cp.add(wait,"Center");
    pack();

    /*clear old downloaded file to avoid mixing it up with the new one*/
    new File("justdown.cjd").delete();

    /*interrupt refresh thread, close old socket*/
    try{
      if(socOut!=null&&socIn!=null&&soc!=null){
        socOut.println("LEAVE"); //asks server to close connection
	
	/*in jdk1.3, closing socket will block program execution until
	  refresh thread wakes up from listening; this will happen when
	  server closes connection; then, refresh thread exits normally*/
        socOut.close();
        socIn.close();
        soc.close(); 
      }
    }catch(IOException ex){
      /*error message*/
      System.out.println("error closing socket: "+ex.toString());
    }

    /*open new socket*/
    try{
      soc=new Socket(urlAddress.getHost(),CocoConstants.SERVER_IN_PORT);
      socOut=new PrintWriter(soc.getOutputStream(),true);
      socIn=new BufferedReader(new InputStreamReader(soc.getInputStream()));
    }catch(UnknownHostException ex){
      /*error message*/
      System.out.println("unknown host: "+ex.toString());
      
      System.exit(1);
    }catch(IOException ex){
      /*error message*/
      System.out.println("socket opening error: "+ex.toString());
      
      System.exit(1);
    }
    
    String rs=null; //string read in from afar
    
    /*receive hello message*/
    try{
      rs=socIn.readLine();
    }catch(IOException ex){
      /*error message*/
      System.out.println("server did not say hello "+ex.toString());
      System.exit(1);
    }
    
    /*check if it was a hello*/
    if(!rs.startsWith("HELLO")){
      System.out.println("instead of hello, server said "+rs);
    }

    /*download contents from the address to the file "justdown.cjd"*/
    socOut.println("DOWNLOAD "+urlAddress.getPath());
    int fl=0; //expected file length
    
    /*receive a line from the server*/
    try{
      rs=socIn.readLine();
    }catch(IOException ex){
      /*error message*/
      System.out.println("error downloading header "+ex.toString());
    }
    
    /*stop if closed*/
    if(rs==null){
      System.out.println("socket closed while downloading header");
    }else{
      /*calculate beginning of the keyword (first word of the packet)*/
      int keywordbeg=0;
      while(
        keywordbeg<rs.length()&&
        Character.isWhitespace(rs.charAt(keywordbeg))
      )keywordbeg++;

      //walking through the first word until reaching first space
      int keywordend=keywordbeg;
      while(
        keywordend<rs.length()&&
        !Character.isWhitespace(rs.charAt(keywordend))
      )keywordend++; 

      /*the keyword*/
      String kw=rs.substring(keywordbeg,keywordend);

      /*the rest*/
      String cs=rs.substring(keywordend);
      
      /*check if line was a file header*/
      if(kw.equals("FILE")){
        /*if so, the file length is expected to be specified in decimal*/
        try{
          fl=Integer.parseInt(cs.trim());
	}catch(NumberFormatException ex){
          System.out.println("file header corrupted "+ex.toString());
	}
      }else{
        System.out.println("not a file: "+rs);
      }
    }
    
    System.out.println("downloading file of length "+Integer.toString(fl,10));
    
    /*now write data coming from the server to a file*/
    try{
      FileOutputStream fos=new FileOutputStream("justdown.cjd");
      BufferedOutputStream bos=new BufferedOutputStream(fos);
      BufferedInputStream bis=new BufferedInputStream(soc.getInputStream());
    
      int cp=0; //current position
      int rc; //char just read in
      
      /*write until file length reached*/
      while(cp<fl){
        bos.write(bis.read());
	cp++;
      }

      bos.flush();
      fos.close();
    }catch(IOException ex){
      /*error message*/
      System.out.println("error downloading file "+ex.toString());
    }

    /*incarnation of downloaded contents in a CocoPage object*/
    try{
      FileInputStream fis=new FileInputStream("justdown.cjd");
      ObjectInputStream ois=new ObjectInputStream(fis);

      page=(CocoPage)ois.readObject();
      fis.close();
    }catch(StreamCorruptedException x){
      JOptionPane.showMessageDialog(
	this, //parentComponent
        "A letöltött file nem coco/javadump oldal!\n"+
	  "Ez a program nem tud más tartalmat megjeleníteni.", //message
	"Letöltött oldal megjelenítése", //title
	JOptionPane.ERROR_MESSAGE //messageType
      );
    }catch(Exception x){
      JOptionPane.showMessageDialog(
	this, //parentComponent
        x.toString(), //message
	"Letöltött oldal megjelenítése", //title
	JOptionPane.ERROR_MESSAGE //messageType
      );
    }

    /*remove wait message, display the page just read in instead*/
    cp.remove(wait);
    if(page!=null){
      cp.add(page,"Center");
      page.setCocoSubmitListener(csl);
    
      /*start refreshing thread*/
      netListen=new Thread(new NetListen(page));
      netListen.start();
      socOut.println("WATCH "+urlAddress.getPath());
    }
    pack();
  }
}
