/*
    JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
    Release Version 2.4

    A project from the Physics Dept, The University of Oxford

    Copyright (C) 2007-2010 The University of Oxford

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 as published by
    the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
    Details (including contact information) can be found at: 

    jpc.sourceforge.net
    or the developer website
    sourceforge.net/projects/jpc/

    Conceived and Developed by:
    Rhys Newman, Ian Preston, Chris Dennis

    End of licence header
*/

package org.jpc.j2se;

import java.awt.*;
import java.io.*;

import javax.swing.JScrollPane;

import org.jpc.emulator.*;
import org.jpc.emulator.pci.peripheral.*;
import org.jpc.emulator.peripheral.*;

/**
 * 
 * @author Rhys Newman
 */
public class PCMonitor extends KeyHandlingPanel 
{
    private Keyboard keyboard;
    private DefaultVGACard vgaCard;
    private Updater updater;
    private Component frame = null;
    private PC pc;
    private double scaleX = 1.0;
    private double scaleY = 1.0;
    private boolean ignoreResize = false;
    
    private volatile boolean clearBackground;

    public PCMonitor(PC pc) 
    {
        this(null, pc);
    }

    public PCMonitor(LayoutManager mgr, PC pc) 
    {
        super(mgr);
        this.pc = pc;

        clearBackground = true;
        setDoubleBuffered(false);
        requestFocusInWindow();

        vgaCard = (DefaultVGACard) pc.getComponent(VGACard.class);
        vgaCard.setMonitor(this);
        vgaCard.resizeDisplay(720, 480);
        keyboard = (Keyboard) pc.getComponent(Keyboard.class);
        setInputMap(WHEN_FOCUSED, null);
    }

    protected PC getPC()
    {
        return pc;
    }

    public void saveState(OutputStream out) throws IOException 
    {
        int[] rawImageData = vgaCard.getDisplayBuffer();
        byte[] dummy = new byte[rawImageData.length * 4];
        for (int i = 0, j = 0; i < rawImageData.length; i++) {
            int val = rawImageData[i];
            dummy[j++] = (byte) (val >> 24);
            dummy[j++] = (byte) (val >> 16);
            dummy[j++] = (byte) (val >> 8);
            dummy[j++] = (byte) (val);
        }

        DataOutputStream output = new DataOutputStream(out);
        output.writeInt(rawImageData.length);
        out.write(dummy);
        out.flush();
    }

    public void loadState(InputStream in) throws IOException 
    {
        DataInputStream input = new DataInputStream(in);
        int len = input.readInt();
        int[] rawImageData = vgaCard.getDisplayBuffer();
        if (len != rawImageData.length) {
            throw new IOException("Image size not consistent with saved image state");
        }
        byte[] dummy = new byte[len * 4];
        input.readFully(dummy);
        for (int i = 0, j = 0; i < len; i++) {
            int val = 0;
            val |= (0xff & dummy[j++]) << 24;
            val |= (0xff & dummy[j++]) << 16;
            val |= (0xff & dummy[j++]) << 8;
            val |= 0xff & dummy[j++];

            rawImageData[i] = val;
        }
    }

    public void setFrame(Component f) 
    {
        this.frame = f;
    }

    public void repeatedKeyPress(int keyCode) 
    {
        keyboard.keyPressed(KeyMapping.getScancode(Integer.valueOf(keyCode)));
    }

    public void keyPressed(int keyCode) 
    {
        keyboard.keyPressed(KeyMapping.getScancode(Integer.valueOf(keyCode)));
    }

    public void keyReleased(int keyCode) 
    {
        keyboard.keyReleased(KeyMapping.getScancode(Integer.valueOf(keyCode)));
    }

    public void mouseEventReceived(int dx, int dy, int dz, int buttons) 
    {
        keyboard.putMouseEvent(dx, dy, dz, buttons);
    }

    public synchronized void startUpdateThread() 
    {
        stopUpdateThread();
        updater = new Updater();
        updater.start();
    }

    public synchronized void stopUpdateThread() 
    {
        if (updater != null)
            updater.halt();
    }

    public synchronized boolean isRunning() 
    {
        if (updater == null)
            return false;
        return updater.running;
    }

    class Updater extends Thread 
    {
        private volatile boolean running = !Option.noScreen.isSet();

        public Updater() 
        {
            super("PC Monitor Updater Task");
        }

        public void run() 
        {
            while (running) 
            {
                try
                {
                    Thread.sleep(20);
                }
                catch (InterruptedException e) {}

                vgaCard.prepareUpdate();
                vgaCard.updateDisplay();
                /*
                int xmin = (int)((vgaCard.getXMin() -1 ) * scaleX);
                int xmax = (int)(vgaCard.getXMax() * scaleX);
                int ymin = (int)((vgaCard.getYMin() - 1) * scaleY);
                int ymax = (int)(vgaCard.getYMax() * scaleY);
                
                repaint(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
                */
                repaint();
            }
        }

        public void halt() 
        {
            try 
            {
                running = false;
                interrupt();
            } 
            catch (SecurityException e) {}
        }
    }

    public void resizeDisplay(int width, int height) 
    {
		resizeDisplayCommon((int)(width * scaleX), (int)(height * scaleY));
    }
    private void resizeDisplayCommon(int width, int height) 
    {
    	//System.out.println("resized X="+width+" Y="+height);
    	//System.out.println("resized scaleX="+scaleX+" scaleY="+scaleY);
        setPreferredSize(new Dimension(width, height));
        setMaximumSize(new Dimension(width, height));
        setMinimumSize(new Dimension(width, height));

        clearBackground = true;
        revalidate();
        repaint();
    }

    public void scaleDisplay(int width, int height) 
    {
        
    	Dimension display = vgaCard.getDisplaySize();
    	double displayWidth=display.width;
    	double displayHeight=display.height;
    	if(width > displayWidth) {
    		scaleX = width / displayWidth;
    	}else{
    		scaleX = 1.0;
    	}
    	if(height > displayHeight) {
    		scaleY = height / displayHeight;
    	}else{
    		scaleY = 1.0;
    	}
    	//System.out.println("scale display scaleX="+scaleX+" scaleY="+scaleY + "actual x="+displayWidth+" y="+displayHeight);
    }

    public void update(Graphics g) 
    {
        paint(g);
    }

    public void paint(Graphics g) 
    {
        if (clearBackground)
        {
            g.setColor(Color.white);
            Dimension s1 = getSize();
            Dimension s2 = vgaCard.getDisplaySize();

            if (s1.width > s2.width)
                g.fillRect(s2.width, 0, s1.width - s2.width, s1.height);
            if (s1.height > s2.height)
                g.fillRect(0, s2.height, s1.width, s1.height - s2.height);
            clearBackground = false;
        }
        vgaCard.paintPCMonitor((Graphics2D)g, this);
    }
}
