/Users/petercappello/NetBeansProjects/56-2014/56-2014-5-Gala/src/ControlPanel.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.Arrays;
//import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

/**
 *
 * @author Pete Cappello
 */
public class ControlPanel extends JPanel
{
    private final JPanel assemblerPanel = new JPanel();
        private final JTextArea consoleTextArea = new JTextArea( 10, 30 );
        private final JTextArea programTextArea = new JTextArea( 10, 30 );
    private final JPanel buttonPanel = new JPanel();
        private final JButton loadButton  = new JButton( "Load" );
        private final JButton runButton   = new JButton( "Run" );
        private final JButton stepButton  = new JButton( "Step" );
    
    private final JScrollPane assemblerScrollPane = new JScrollPane( assemblerPanel );
    private final JScrollPane programScrollPane = new JScrollPane( programTextArea );
    private final JScrollPane consoleScrollPane = new JScrollPane( consoleTextArea );
    private final JScrollPane viewScrollPane;
    
    private final View view = new View();
    private final GVM gvm;
    
    ControlPanel( GVM gvm ) 
    {
        this.gvm = gvm;
        
        // NORTH
        buttonPanel.setLayout( new GridLayout( 1, 3 ) );
        buttonPanel.add( loadButton );
        buttonPanel.add( runButton );
        buttonPanel.add( stepButton );
        
        // CENTER
        viewScrollPane = new JScrollPane( view );
        
        // SOUTH
        assemblerPanel.setLayout( new GridLayout( 1, 2) );
        assemblerPanel.add( programScrollPane, BorderLayout.CENTER );
        assemblerPanel.add( consoleScrollPane, BorderLayout.SOUTH );
        
        setLayout( new BorderLayout() );
        
        add( buttonPanel, BorderLayout.NORTH );
        add( viewScrollPane, BorderLayout.CENTER );
        add( assemblerScrollPane, BorderLayout.SOUTH );

        addEventListeners();
        view.setImage( gvm.getImage() );
        
        Dimension dimension = new Dimension( programTextArea.getWidth() + consoleTextArea.getWidth(), 
                buttonPanel.getHeight() + view.getHeight() + programTextArea.getHeight() );
        setSize( dimension );
        setPreferredSize( dimension );
        
        Instruction.setClass();
        enableExecution( false );
    }

    private void addEventListeners() 
    {
        //------------------------------------------
        // contoller TEMPLATE CODE for each action
        //------------------------------------------
        // If you are running Java 8, use lambda expressions
        loadButton.addActionListener( ( ActionEvent actionEvent ) -> 
        {
            loadButtonActionPerformed( actionEvent );
        });
        runButton.addActionListener( ( ActionEvent actionEvent ) -> 
        {
            runButtonActionPerformed( actionEvent );
        });
        
        stepButton.addActionListener( ( ActionEvent actionEvent ) -> 
        {
            stepButtonActionPerformed( actionEvent );
        });
        
        // If you are not running Java 8, uncomment the code below   
//        runButton.addActionListener( new ActionListener() 
//        {
//            @Override
//            public void actionPerformed( ActionEvent actionEvent ) 
//            {
//                runButtonActionPerformed( actionEvent );
//            }
//        });
               
//        stepButton.addActionListener( new ActionListener() 
//        {
//            @Override
//            public void actionPerformed( ActionEvent actionEvent ) 
//            {
//                stepButtonActionPerformed( actionEvent );
//            }
//        });
    }

    // _____________________________
    //  controller for each action
    // _____________________________
    private void loadButtonActionPerformed( ActionEvent actionEvent ) 
    {
        enableExecution( false );
        consoleTextArea.append( "\n\nStarting assembly ...\n");
        programTextArea.setText( "" );
        Assembler assembler = new Assembler();
        if ( ! assembler.hasFile() )
        {
            consoleTextArea.append( "No file chosen." );
            return;
        }
        try 
        {
            boolean isSuccessful = assembler.assemble( programTextArea, consoleTextArea );
            String assemblyTime = String.format("Assembly time: %.2f sec.", ( assembler.getProcessingTime() / 1000.0) );
            if ( isSuccessful )
            {
                consoleTextArea.append( "\nBUILD SUCCESSFUL. " );
                consoleTextArea.append( assemblyTime );
                gvm.load( assembler.toArray() );
                enableExecution( true );
            }
            else
            {
                consoleTextArea.append( "\nBUILD FAILED. " + assemblyTime +  " Number of invalid statements: " + assembler.getNumInvalidStatements() );
                enableLoad( true );
            }
        } 
        catch( IOException e ) 
        {
            consoleTextArea.append( "\nIOException" + e.getMessage() );
        }
        repaint();
    }
    
    private void runButtonActionPerformed( ActionEvent actionEvent ) 
    {
        enableLoad( false );
        long startTime = System.currentTimeMillis();
        gvm.run();
        long runTime = System.currentTimeMillis() - startTime;
        String runTimeMessage = String.format("\nExecution time: %.2f sec.\n", ( (float) (runTime / 1000.0) ) );
        consoleTextArea.append( runTimeMessage );
        enableLoad( true );
        repaint();
    }

    private void stepButtonActionPerformed( ActionEvent actionEvent ) 
    { 
        enableLoad( false );
        gvm.step();
        enableLoad( true );
        view.repaint();
        repaint();
    }

    private void enableExecution( boolean value )
    {
        runButton.setEnabled( value );
        stepButton.setEnabled( value );
    }
    
    private void enableLoad( boolean value ) { loadButton.setEnabled( value ); }
    
    /**
     * Compare assembler output with hand-coded program.
     * @param assembler an Assembler that has assembled a Gala program.
     */
    private void testCompare( Assembler assembler )
    {
        int[][] hardcodedProgram = gvm.getProgramMemory();
        int[][] assembledProgram = assembler.toArray();
        if ( hardcodedProgram.length != assembledProgram.length )
        {
            System.out.println("hardcodedProgram.length: " + hardcodedProgram.length + " assembledProgram.length: " + assembledProgram.length );
            return;
        }
        //________Begin test: compare each instruction for equality
        for ( int i = 0; i < assembledProgram.length; i++ )
        {
            if ( ! Arrays.equals( hardcodedProgram[ i ], assembledProgram[ i ] ) )
            {
                System.out.println("Mismatch on instruction " + i );
                for ( int j = 0; j < hardcodedProgram[ i ].length; j++ )
                {
                    System.out.println("opcodes: H: " + hardcodedProgram[ i ][0] + " A: " + assembledProgram[ i ][0] );
                    System.out.println("operands: H: " + hardcodedProgram[ i ][1] + "A: " + assembledProgram[ i ][1] );
                } 
            }
            else
            {
                System.out.println("instructions " + i + " match.");
                System.out.println("opcodes: H: "  + hardcodedProgram[ i ][0] + " A: " + assembledProgram[ i ][0] );
                if ( hardcodedProgram[ i ].length == 2 )
                {
                    System.out.println("operands: H: " + hardcodedProgram[ i ][1] + " A: " + assembledProgram[ i ][1] );
                }
            }
        }
    }
}