/**
  *  * * * * * * * * * * * * * * * * * * * * *
  *  *                                       *
  *  *  Stack Machine Simulator Minimal GUI  *
  *  *                                       *
  *  *                                       *
  *  * * * * * * * * * * * * * * * * * * * * *
  */

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import StackMachine;
import TextFileLoader;


 /*
  * *********************
  *    GLI ASCOLTATORI
  * *********************
  */


class SMSWinListener implements WindowListener{

 /* questo serve alla applicazione stand-alone */

  public void windowClosing(WindowEvent e){
	e.getWindow().dispose(); 
	System.exit(0);
	System.out.println("Stack Machine simulation terminated");
  }
  public void windowActivated(WindowEvent e){
  }
  public void windowDeactivated(WindowEvent e){
  }
  public void windowIconified(WindowEvent e){
  }
  public void windowDeiconified(WindowEvent e){
  }
  public void windowOpened(WindowEvent e){
  }
  public void windowClosed(WindowEvent e){
  }
}



class EngageListener implements ActionListener{
 
  private SMSmgui app;

  public EngageListener( SMSmgui rif ){ app = rif;}

  public void actionPerformed( ActionEvent e ){
	/**
	  *  E N G A G E !
	  */
	StackMachine m1 = app.getMachine();
	try{
		m1.go( app.getStackMonitoringFlag() );
	}
	catch( HaltMessage hm ){
		String msg =  "Esecuzione senza errori.";
		System.out.println( msg );
		app.appendToLog( msg );
	}catch( HaltNotFoundException hnfe ){
		app.appendToLog("* HALT not found");
	}catch( InvalidDataReadAddressException idrae ){
		app.appendToLog("* Error while reading data from the DS");
	}catch( InvalidDataWriteAddressException idwae ){
		app.appendToLog("* Error while writing data from the DS");
	}catch( EmptyMachineStackException ese ){
		app.appendToLog( "* Stack was found empty when shouldn\'t"); 
	}catch( NotRecognizedInstructionException nrie ){
		app.appendToLog( "* Unknown instruction found in " );
	}catch( MachineException me ){
		String msg= "*" + me.getClass().getName() +" rilevata nella "
			  + m1.getClass().getName() +" " +m1.machineName();
		System.out.println( msg );
		app.appendToLog( msg );
	}
	catch( MachineSignal ms ){
		app.appendToLog( ms.getClass().getName() + "intercepted" );
	}
	System.out.println( m1.rapportoDOS());
	app.updateReportTA();
  }
}//EngageListener




class AggiornaCSListener implements ActionListener{
 
  private SMSmgui app;

  public AggiornaCSListener( SMSmgui rif ){ app = rif;}

  public void actionPerformed( ActionEvent e ){
	app.getMachine().loadNewProgram( app.getCodeInputTA().getText() );
	app.appendToLog( "CS modified" );
	app.updateReportTA();
  }
}//AggiornaCSListener



class ResetPCListener implements ActionListener{

  private SMSmgui app;

  public ResetPCListener( SMSmgui theapplet ){ app= theapplet;}

  public void actionPerformed( ActionEvent e ){
	app.getMachine().PC().reset();
	app.appendToLog( "PC reset" );
	app.updateReportTA();
  }
}//ResetPCListener


class IncPCListener implements ActionListener{

  private SMSmgui app;

  public IncPCListener( SMSmgui theapplet ){ app= theapplet;}

  public void actionPerformed( ActionEvent e ){
	StackMachine m= app.getMachine();
	m.PC().inc();
	app.appendToLog( "inc PC: " + m.PC().contents() );
	app.updateReportTA();
  }
}//IncPCListener



class LoadProgramListener implements ActionListener{

 /**
   *   Questa classe carica, alla pressione del tasto INVIO, un nuovo prg.
   *   leggendolo dall'URL indicato dal TextField "loadProgram" del panel
   *
   */

  private SMSmgui app;

  public LoadProgramListener( SMSmgui rif ){ app = rif;}

  protected String getPath(){ return app.getLoadProgramTF().getText() ;}

  public void actionPerformed( ActionEvent ev ){
	String newFileName = getPath();
	TextFileLoader tfl = app.getTextFileLoader();
	tfl.changeFile( newFileName );
	tfl.reLoad();
	app.setNewProgram( tfl.getLoadedContents() );
	app.appendToLog( newFileName + " loaded");
	app.updateReportTA();
  }

}//LoadProgramListener



 /*
  * *******************
  *    COMMAND PANEL
  * *******************
  */


class VPanel extends Panel{
  private SMSmgui   theapplet;
  private Label     titolo;
  private Button    ATTIVARE;    //Fa partire il ciclo fetch-exec
  private Button    aggiornaCS;  //Premendo si effettua l'aggiorn. nella S.M.
  private Button    resetPC;     //resetta il PC
  private Button    incPC;       //incrementa il PC
  private Checkbox  stkMonitor;  //CheckBox per settare lo Stack monitoring a runtime
  private TextField loadProgram; //il TF per caricare un nuovo prg;
  private TextArea  machineLog;  //"diario di bordo" della macchina
  private Panel     subPanel;    //il sub-panel con tutti i tasti
  private TextFileLoader tfl;    //un oggetto che si occupa dello sporco lavoro del loading

  public VPanel( SMSmgui app ){
	theapplet= app;
	setLayout( new GridLayout( 2, 1, 10, 10) );
	titolo = new Label("Stack Machine Simulator", Label.CENTER );
	ATTIVARE = new Button( "ATTIVARE" );
	aggiornaCS = new Button( "Aggiornare CS" );
	resetPC = new Button( "Reset PC" );
	incPC = new Button( "PC++" );
	stkMonitor = new Checkbox( "Stack Monitoring", false );
	loadProgram =
		 new TextField("To load a prg, type in a filename", app.LARG_IN_CHAR-1);
	loadProgram.setEditable( true );
	machineLog= 
		new TextArea("Machine Log --\n",10,10, TextArea.SCROLLBARS_VERTICAL_ONLY);
	machineLog.setEditable(false);
	ATTIVARE.addActionListener( new EngageListener( theapplet ) );
	aggiornaCS.addActionListener( new AggiornaCSListener( theapplet ));
	resetPC.addActionListener( new ResetPCListener( theapplet ) );
	incPC.addActionListener(new IncPCListener( theapplet ));
	//stkMonitor.addMouseListener( new StkFlagListener( theapplet ));//INUTILE
	loadProgram.addActionListener( new LoadProgramListener( theapplet ));
	tfl = new TextFileLoader("");
	
	subPanel = new Panel();
	subPanel.setLayout( new GridLayout( 7, 1, 5, 5) );
	subPanel.add( titolo );
	subPanel.add( ATTIVARE );
	subPanel.add( aggiornaCS );
	subPanel.add( resetPC );
	subPanel.add( incPC );
	subPanel.add( stkMonitor );
	subPanel.add( loadProgram );
	add( subPanel );
	add( machineLog );
  }
/*
  public void paint( Graphics g){
	titolo.paint(g);
	ATTIVARE.paint(g);
	aggiornaCS.paint(g);
	resetPC.paint(g);
	incPC.paint(g);
	stkMonitor.paint(g);
	loadProgram.paint(g);
	machineLog.paint(g);
  }
*/
  public Insets getInsets(){ return new Insets( 10, 10, 10, 10);}

  public boolean getStackMonitoringFlag(){ return stkMonitor.getState();}

  public TextField getLoadProgramTF(){ return loadProgram; }

  public TextFileLoader getTextFileLoader(){ return tfl;}

  public void appendToLog( String msg ){
	machineLog.append( "\n"+msg );
//	machineLog.repaint();			 // NON VA
//	machineLog.paint( this.getGraphics() );  //***FUNZIONA!In realtà non c'è bisogno di far nulla
  }

}



 /*
  * ***************
  *    L'APPLET
  * ***************
  */


public class SMSmgui extends java.applet.Applet {

  public final int LARG_IN_CHAR = 15;

  private TextArea codeinputTA;
  private TextArea reportTA;
  private VPanel commandPanel;
  private StackMachine m1;

  public void init(){
	setLayout( new GridLayout(1, 3, 5, 5) );
	m1 = new StackMachine( "m1", "");  //crea una SM con CS vuoto (solo l'istr. HALT)
	codeinputTA= new TextArea( "", 50, LARG_IN_CHAR, TextArea.SCROLLBARS_BOTH);
	reportTA= new TextArea( machineReport(), 50,LARG_IN_CHAR,TextArea.SCROLLBARS_BOTH);
	commandPanel = new VPanel( this );
	codeinputTA.setEditable(true);
	reportTA.setEditable(false);
	add( codeinputTA );
	add( reportTA );
	add( commandPanel );
	

  }

  public String machineReport(){
	DataSegment DS= m1.getDataSegment();
	CodeSegment CS= m1.getCodeSegment();
	return m1.rapportoV() + "CS Base Addr: " +CS.base() +"\n" + "DS Base Addr: "
		+ DS.base() + "\n\n" + "DATA SEGMENT:\n" +DS.getDataVector().stringView(DS)
		+ "\n\nCODE SEGMENT:\n" + CS.getCodeVector().stringView(CS);
  }

  public void updateReportTA(){
	/* AGGIORNA LA T.A. DI OUTPUT
	 * VIENE CHIAMATA DOPO UN'ATTIVAZIONE;
	 *		  DOPO UN'AGGIORNAMENTO DEL CS;
	 *                DOPO AVER CARICATO UN NUOVO PRG.
	 */
	reportTA.setText( machineReport() );
//	reportTA.repaint();		//***NON VA
//	reportTA.paint(getGraphics());  //***FUNZIONA ! Ma in realtà non c'è bisogno di far nulla
  }

  public void appendToLog( String msg ){ commandPanel.appendToLog( msg );}

  public StackMachine getMachine(){ return m1; }

  public TextArea getCodeInputTA(){ return codeinputTA;}

  public boolean getStackMonitoringFlag(){ return commandPanel.getStackMonitoringFlag();}

  public TextField getLoadProgramTF(){ return commandPanel.getLoadProgramTF();}

  public TextFileLoader getTextFileLoader(){ return commandPanel.getTextFileLoader();}

  public void setNewProgram(String prg){
	/**
	  * serve per il caricamento da disco di un nuovo programma
	  */
	m1.loadNewProgram( prg );
	codeinputTA.setText( prg );
	codeinputTA.repaint();
	
  }

/*
  public void paint( Graphics g) {
	//paintComponents(g);
	reportTA.paint(g);
	codeinputTA.paint(g);
	commandPanel.paint(g);
  }
*/

  protected static void makeWindow(){
	Frame f = new Frame("SMS");
	SMSmgui smsapplet = new SMSmgui();
	smsapplet.init(); 
	smsapplet.start();
	f.add(smsapplet);
	f.setSize(600,400);
	f.addWindowListener( new SMSWinListener() );
	f.show();
	//System.exit(0);   //exit chiuderebbe subito la fin. appena aperta
  }

  public static void main( String[] args ){
	System.out.println(" Stack Machine Simulator running" );
	makeWindow();
  }
}//SMSmgui

