import java.awt.*;
import java.util.*;
import java.io.*;
import java.net.*;

/*
 *
 * Headline	This application get news from a news server and
 * show headline news on user's screen
 */
public class Headline extends Frame implements Runnable
{
	Thread th;

    Panel place;
	Display display;
	Button cset, cconnect, cexit, ccancel;
    private SetupDlg dialog;
	private BodyDlg body;
	final static String OPTIONPANEL = "Panel with Buttons";
    final static String TEXTPANEL = "Panel with TextField";

	Properties appProps;
	File PropFile = new File("headline.ini");
	String hostname;  //news server host name
	Integer dt;		  //time interval between fetching news
	String newsgroup;
	int num_article;	//number of article to show

	Server news;
	
	int num, first, last; //number of articles, first number, last number in this newsgroup
	int num_get;		  //number of articles we seccussful get
	int idx;			  //track which article header we are display now
	long tBegin, tEnd;     //to count time so we know when to reconnect


	Article[] article;	  //article which header we want

    public Headline() {
        place = new Panel();
        place.setLayout(new CardLayout());	   

   
        Panel cmd = new Panel();
        cmd.setLayout(new FlowLayout(FlowLayout.CENTER,20,5));
        cset = new Button("Setup");
        cconnect = new Button("Connect");
        ccancel = new Button("Cancel");
		ccancel.setEnabled(false);
        cexit = new Button("Exit");
		cmd.add(cset);
		cmd.add(cconnect);
		cmd.add(ccancel);
		cmd.add(cexit);

        Panel txt = new Panel();
        display = new Display();
		txt.add(display);

        place.add(OPTIONPANEL, cmd);
        place.add(TEXTPANEL, txt);
        add(place);

		appProps = new Properties();
		try
		{
			FileInputStream appStream = new FileInputStream(PropFile);
			appProps.load(appStream);
			appStream.close();		
		} catch (FileNotFoundException e) {
        } catch (IOException e) {
            System.err.println("Error: " + e);
        }

		hostname =new String(appProps.getProperty("hostname","news.prodigy.net"));
		dt =new Integer (appProps.getProperty("time","15"));
		newsgroup =new String(appProps.getProperty("newsgroup","clari.world.top"));
		num_article= (new Integer(appProps.getProperty("number_of_articles","10"))).intValue();
	}

    public Insets insets() {
                  return new Insets(20,0,0,0);
              }

    public boolean action(Event evt, Object arg) {

        if (evt.target == ccancel) {
            ((CardLayout)place.getLayout()).next(place);
            th.resume();
			return true;
        }
        
        if (evt.target == cset) {
        
			if (dialog == null) {
				dialog = new SetupDlg(this, "HeadLine News Setup");
			}
			dialog.show();
            return true;
        }


        if (evt.target == cconnect) {
		    article = new Article[num_article];
        	connect();
			display.click_r = false;
			display.click_l = false;
            return true;
        }

		if(evt.target == cexit)
		{
			if(news != null)
				news.getOut();
			stop();
		    System.exit(0);
		}
        return false;
    }

    public boolean handleEvent(Event e) {
        if (e.id == Event.WINDOW_DESTROY)
		{
			if (news != null)
				news.getOut();
			stop();
		    System.exit(0);
		}
		return super.handleEvent(e);
    }
  
    public Dimension maximumSize() {
        return new Dimension(450,25);
    }

	public void start()
	{
		if (th == null)
		{
			th = new Thread(this);
			th.start();
		}else
		{
			th.resume();
		}
	}

	public void stop()
	{
		if (th != null)
		{
			th.stop();
			th = null;
		}
	}

	public void run()
	{
		try
		{	
			for(idx=0; idx<num_get; )
			{
				display.st = article[idx].subject;
				for(int i=0; i<8; i++)
			 {
				if(display.click_r)    //check if user click on display
				{
					display.click_r = false;
				    ((CardLayout)place.getLayout()).next(place);
					th.suspend();
				}
				if(display.click_l)
				{
					display.click_l = false;
					getBody();
				}
				display.write(10,30-2*i);
				th.sleep(50);
			 }

			 for(int i=0; i<20; i++)  //we do the loop instead of sleep(1000)
			 {						  //to get a quick respond to mouse click
				if(display.click_r)    //check if user click on display
				{
					display.click_r = false;
				    ((CardLayout)place.getLayout()).next(place);
					th.suspend();
				}
				if(display.click_l)
				{
					display.click_l = false;
					getBody();
				}
				th.sleep(50);
			 }
			  

			 for(int i=8; i<16; i++)
			 {
				if(display.click_r)    //check if user click on display
				{
					display.click_r = false;
				    ((CardLayout)place.getLayout()).next(place);
					th.suspend();
				}
				if(display.click_l)
				{
					display.click_l = false;
					getBody();
				}
				display.write(10,30-2*i);
				th.sleep(50);
			 }
			 idx++;
			 if(idx==num_get)
			 {
				tEnd=System.currentTimeMillis(); //Check time after each round
				if(((tEnd-tBegin)/60000) >=  dt.longValue())
					connect();					//reconnect and get newer news
				idx=0;
			  }
			}
		}
		catch (InterruptedException e)
		{
			th.stop();
		}
	}
	
	public void connect()
	{
		if (news == null)
			news = new Server(hostname);
		
        tBegin=System.currentTimeMillis();			//reset the time
		((CardLayout)place.getLayout()).show(place,TEXTPANEL);
		
		news.sndCmd("group " + newsgroup);
	 
		if(news.status.startsWith("211"))    //response newsgroup select
		{
			getNumber();
		    int art = last;
			for( num_get=0;num_get<num_article && (art >= first) ;art--)
			{;
			   if(getHead(art))
			   {
				  article[num_get] =new Article(news.message);
				  num_get++;
			   }
			}

		}else if(news.status.startsWith("411"))
		{							//no such newsgroup
			display.st="No such newsgroup: "+ newsgroup;
			display.write(10,30);
		}

		news.getOut();					//disconnect from the server
		news = null;
		ccancel.setEnabled(true);
		idx = 0;
		start();						//start display the news
	}

	public void getNumber()		  //get some numbers concerning this
	{							  //this news group from server's status line
		String s = new String(news.status);
		int idx = s.indexOf(' ',4);
		num = (Integer.valueOf(s.substring(4,idx))).intValue();
		int idx2 = s.indexOf(' ',idx+1);
		first = (Integer.valueOf(s.substring(idx+1,idx2))).intValue();
	    idx = s.indexOf(' ',idx2+1);
		last = (Integer.valueOf(s.substring(idx2+1,idx))).intValue();
	}

	public boolean getHead(int art_num)   //get the head information of specify article
	{
		news.sndCmd("head " + String.valueOf(art_num));
		if(news.status.startsWith("221"))
			return true;
		else
		    return false;
	}

	public void getBody()			//get the body information of an article
	{
		if (news == null)
			news = new Server(hostname);		//connect to news server
	
		news.sndCmd("group " + newsgroup);
		news.sndCmd("body" + article[idx].id);
		if(news.status.startsWith("222"))		//success in getting body of article
		{
			body = new BodyDlg(this,"Select Article");
			body.show();
		}
		((CardLayout)place.getLayout()).show(place,TEXTPANEL);
		news.getOut();					//disconnect from the server
		news = null;
		body = null;
	}

	public void saveProps()
	{
		appProps.put("hostname",hostname);
		appProps.put("time",dt.toString());
		appProps.put("newsgroup",newsgroup);
		appProps.put("number_of_articles",String.valueOf(num_article));
		try
		{
			FileOutputStream defaultsOut = new FileOutputStream(PropFile);
			appProps.save(defaultsOut, "---Parameters for HeadLine News Java Application---");
			defaultsOut.close();
		} catch (IOException expt) {
			System.err.println("FileStreamsTest: " + expt);
		}	
	}

    public static void main(String args[]) {
        Headline window = new Headline();

        window.setTitle("HeadLine News");
        window.pack();
        window.show();
    }

}

class SetupDlg extends Dialog {
    TextField srvrname;
    TextField tm;
	TextField nwsgrp;
	TextField num;
    Headline parent;
    Button setBttn, cnclBttn;

    SetupDlg(Frame dw, String title) {
        super(dw, title, true);
        parent = (Headline)dw;

        //Create middle section.
		Panel p1 = new Panel();
		p1.setLayout(new GridLayout(0,2));
        Label label1 = new Label("News Server Name:");
        p1.add(label1);
        srvrname = new TextField(parent.hostname,30);
		p1.add(srvrname);
		Label label2 = new Label("Time interval(min) between fetching news:");
        p1.add(label2);
		tm = new TextField(parent.dt.toString(),10);
		p1.add(tm);
		Label label3 = new Label("Select newsgroup:");
		p1.add(label3);
 		nwsgrp = new TextField(parent.newsgroup,30);
		p1.add(nwsgrp);
		Label label4 = new Label("Number of articles to be shown:");
		p1.add(label4);
 		num = new TextField(String.valueOf(parent.num_article),30);
		p1.add(num);
		add("Center", p1);
		

        //Create bottom row.
        Panel p2 = new Panel();
        p2.setLayout(new FlowLayout(FlowLayout.CENTER,20,5));
        cnclBttn = new Button("Cancel");
        setBttn = new Button("Set");
        p2.add(cnclBttn);
        p2.add(setBttn);
        add("South", p2);

	//Initialize this dialog to its preferred size.
		pack();
    }

    public boolean action(Event event, Object arg) {
        if ( event.target == setBttn) {
        	parent.hostname = srvrname.getText();
			parent.dt = Integer.valueOf(tm.getText());
        	parent.newsgroup = nwsgrp.getText();
			parent.num_article = (Integer.valueOf(num.getText())).intValue();
			parent.saveProps();
			hide();
			}

          if ( event.target == cnclBttn) {
			hide();
			}

        return true;
    }
}

class BodyDlg extends Dialog
{
	TextArea body;
	Button ok;
    Headline parent;

    BodyDlg(Frame dw, String title) {
        super(dw, title, false);
        parent = (Headline)dw;

		Panel p1 = new Panel();
        body = new TextArea("News",20,60);
		body.setEditable(false);
		body.append(parent.news.message);
        p1.add(body);
		
		Panel p2 = new Panel();
		ok = new Button("Ok");
        p2.add(ok);
		add("Center",p1);
		add("South",p2);
		pack();
    }
    
	public boolean action(Event event, Object arg) {

          if ( event.target == ok) {
			hide();
			}
		return true;
    }
}

class Server 
{
	String host;
	Socket echoSocket = null;
    DataOutputStream os = null;
    BufferedReader is = null;
    String status;		//first line status response
	String message;		//text response 

	public Server(String hostname) 
	{
		host = hostname;

        try 
		{
			echoSocket = new Socket(host, 119);
            os = new DataOutputStream(echoSocket.getOutputStream());
            is = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
        } catch (UnknownHostException e) {
            message = new String("Don't know about host: " + host);
		} catch (IOException e) {
            message = new String("Couldn't get I/O for the connection to: " + host);
        }

        if (echoSocket != null && os != null && is != null) 
		{
			message = new String("connected to host: " + host);
                getreply();
		}
	}

	public void sndCmd(String cmd)		//send command to the news server
	{
		message = new String();
		try
		{
			os.writeBytes(cmd+"\n");
			getreply();
		} catch (IOException e) {
			message = new String("Error in sending command");
		}
	}

	public void getOut()
	{
		if((echoSocket != null) && ((os != null) && (is != null))) 
		{
			try
			{
				sndCmd("quit");
				os.close();
				is.close();
				echoSocket.close();
			} catch(IOException e) {
				message = new String("I/O failed on the conection to: " + host);
			}
		}
	}

	public void getreply()
	{
        try
		{
             status = is.readLine();
			 message = new String();
             while(is.ready())
		     {
                message = message + is.readLine() + "\n";
             }
	
		} catch (IOException e) {
            message = new String("I/O failed on the connection to: "+ host);
        }
	}
	
 }

class Article		// data of the header of an article
{
	String group;   //newsgroup this article belong
    String subject = new String("No Subject");
	String author = new String("No Author");
	String date = new String("No Date");
	String id= new String("<0>");      //message id
	String ln_num;  //article length
    

	public Article(String mssg) //construct a Article class use
	{							//message string we get from server
		BufferedReader in;
		String line = new String();
		String s;
		
		try
		{
			in = new BufferedReader(new StringReader(mssg));
			boolean endtxt = false;
			while(in.ready() && !endtxt)	 //read text and paste corresponding information
			{
				line =in.readLine();
				s =line.toLowerCase();
				
				if(s.startsWith(".") && !s.startsWith(".."))  //text end
					endtxt = true;
				
				if(s.startsWith("subject:"))
				{
					subject = line.substring(8);
				}else if(s.startsWith("from:"))
				{
					author = line.substring(5);
				}else if(s.startsWith("newsgroups:"))
				{
					group = line.substring(11);
				}else if(s.startsWith("date:"))
				{
					date = line.substring(5);
				}else if(s.startsWith("message-id:"))
				{
					id = line.substring(11);
				}else if(s.startsWith("lines:"))
				{
					ln_num = line.substring(6);
				}					  
			}  
			
			if(subject=="")
				subject="No Subject";
			if(author=="")
				author="Unknowned Author";
			if(group=="")
				group="No group";
			if(date=="")
				date="Unknowned date";
			if(ln_num=="")
				ln_num="Unknowned length";

			in.close();
	   }catch(IOException e) {
			System.err.println("Error reading the article");
	   }
	}
}

class Display extends Canvas 
{
	String st ;
	int xp, yp;       //position of text

	Dimension offDimension;
	Image offImage;
	Graphics offGraphics;
	boolean click_l;	  //left button click
	boolean click_r;      //right button click

	public Display()
	{
		st = new String();
		click_l = false;
		click_r=false;
	}

	

	public void paint(Graphics g)
	{
		update(g);
	}

    public synchronized void update(Graphics g) {
        
	 Dimension d=size();
	 if ( (offGraphics == null)
  || (d.width != offDimension.width)
  || (d.height != offDimension.height) ) 
  
	 {
		offDimension = d;
		offImage = createImage(d.width, d.height);
		offGraphics = offImage.getGraphics();
		int w = size().width;
        int h = size().height;
		offGraphics.drawRect(0, 0, w - 1, h - 1);
	 }
	    offGraphics.setColor(Color.black);
	    offGraphics.fillRect(0, 0, d.width, d.height);
	
        offGraphics.setFont(new Font("Time", Font.BOLD, 12));
		offGraphics.setColor(Color.red);
		offGraphics.drawString(st, xp,  yp);	 
		g.drawImage(offImage, 0, 0, this);
	}

    public Dimension minimumSize() {
        return new Dimension(400,20);
    }

    public Dimension preferredSize() {
        return minimumSize();
    }

	public void write(int x, int y)
	{
		xp = x;
		yp = y;
		repaint();
	}

    public synchronized boolean handleEvent(Event event) {
		if(event.id==Event.MOUSE_DOWN)
		{
			if(event.metaDown())
				click_r = true;
			else
				click_l = true;
		}
		notify();
		return super.handleEvent(event);
    }
}
