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

    A project from the Physics Dept, The University of Oxford

    Copyright (C) 2007 Isis Innovation Limited

    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: 

    www.physics.ox.ac.uk/jpc
*/

package org.jpc.support;

//Do not event think about adding an import line to this class - especially not import java.net.*!
import org.jpc.emulator.*;

public class DriveSet extends AbstractHardwareComponent
{
    public static final int FLOPPY_BOOT = 0;
    public static final int HARD_DRIVE_BOOT = 1;
    public static final int CD_BOOT = 2;
    
    private int bootType;
    private BlockDevice bootDevice;
    private BlockDevice[] floppies;
    private BlockDevice[] ides;

    public DriveSet(int bootType, BlockDevice floppyDrive, BlockDevice hardDrive)
    {
        this(bootType, floppyDrive, null, hardDrive, null, null, null);
    }

    public DriveSet(int bootType, BlockDevice floppyDriveA, BlockDevice floppyDriveB, BlockDevice hardDriveA, BlockDevice hardDriveB, BlockDevice hardDriveC, BlockDevice hardDriveD)
    {
        this.bootType = bootType;

	floppies = new BlockDevice[2];
	floppies[0] = floppyDriveA;
	floppies[1] = floppyDriveB;

	ides = new BlockDevice[4];
	ides[0] = hardDriveA;
        ides[1] = hardDriveB;
	ides[2] = (hardDriveC == null) ? new CDROMBlockDevice() : hardDriveC;
	ides[3] = hardDriveD;

        if (bootType == FLOPPY_BOOT)
            bootDevice = floppyDriveA;
        else if (bootType == CD_BOOT)
            bootDevice = hardDriveC;
        else 
            bootDevice = hardDriveA;
    }

    public BlockDevice getHardDrive(int index)
    {
	if (index > 3)
	    return null;

	return ides[index];
    }

    public void setHardDrive(int index, BlockDevice device)
    {
        ides[index] = device;
    }
	
    public BlockDevice getFloppyDrive(int index)
    {
	if (index > 1)
	    return null;

	return floppies[index];
    }

    public BlockDevice getBootDevice()
    {
        return bootDevice;
    }

    public int getBootType()
    {
        return bootType;
    }

    public static String findArg(String[] args, String key, String defaultValue)
    {
        if (key.startsWith("-"))
            key = key.substring(1);

        for (int i=0; i<args.length-1; i++)
        {
            String arg = args[i];
            if (arg.startsWith("-"))
                arg = arg.substring(1);

            if (arg.equalsIgnoreCase(key))
                return args[i+1];
        }

        return defaultValue;
    }

    private static BlockDevice createFloppyBlockDevice(String spec)
    {
        if (spec == null)
            return null;

	SeekableIODevice ioDevice = null;
	Class ioDeviceClass = null;

	BlockDevice device = null;
	try {
	    if (spec.startsWith("mem:")) {   // use this option in the applet
		spec = spec.substring(4);
		ioDeviceClass = Class.forName("org.jpc.support.ArrayBackedSeekableIODevice");
	    } else // use this to read and _write_ to disk
		ioDeviceClass = Class.forName("org.jpc.support.FileBackedSeekableIODevice");
	    
	    ioDevice = (SeekableIODevice)(ioDeviceClass.newInstance());
	    ioDevice.configure(spec);

	    device = new FloppyBlockDevice(ioDevice);
	} catch (Exception e) {
	    return null;
	}

	return device;
    }

    private static BlockDevice createHardDiskBlockDevice(String spec)
    {
        if (spec == null)
            return null;

	BlockDevice device = null;
	try {	
	    if (spec.startsWith("dir:")) {
		Class deviceClass = Class.forName("org.jpc.support.TreeBlockDevice");
		spec = spec.substring(4);	       
		device = (BlockDevice)(deviceClass.newInstance());
		device.configure(spec);
	    } else if (spec.startsWith("net:")) {
		Class deviceClass = Class.forName("org.jpc.support.RemoteBlockDevice");
		spec = spec.substring(4);
		device = (BlockDevice)(deviceClass.newInstance());
		device.configure(spec);
	    } else if (spec.startsWith("mem:")) {   // use this option in the applet
		Class blockClass = Class.forName("org.jpc.support.ArrayBackedSeekableIODevice");
		SeekableIODevice ioDevice = (SeekableIODevice)(blockClass.newInstance());
		spec = spec.substring(4);
		ioDevice.configure(spec);
		device = new RawBlockDevice(ioDevice);
	    } else {   // use this to read and _write_ to disk
		Class blockClass = Class.forName("org.jpc.support.FileBackedSeekableIODevice");
		SeekableIODevice ioDevice = (SeekableIODevice)(blockClass.newInstance());
		ioDevice.configure(spec);
		device = new RawBlockDevice(ioDevice);
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	    return null;
	}

        return device;
    }

    public static DriveSet buildFromArgs(String[] args)
    {
        int bootKey = DriveSet.HARD_DRIVE_BOOT;
        BlockDevice floppyA = null, floppyB = null, hardDiskA = null, hardDiskB = null, hardDiskC = null, hardDiskD = null;

        String floppyAFileName = findArg(args, "-fda", null);
        floppyA = createFloppyBlockDevice(floppyAFileName);
        if (floppyA != null)
            bootKey = DriveSet.FLOPPY_BOOT;

        String floppyBFileName = findArg(args, "-fdb", null);
        floppyB = createFloppyBlockDevice(floppyBFileName);

        String hardDiskPrimaryMasterFileName = findArg(args, "-hda", null);
        hardDiskA = createHardDiskBlockDevice(hardDiskPrimaryMasterFileName);
        if (hardDiskA != null)
            bootKey = DriveSet.HARD_DRIVE_BOOT;

	String hardDiskPrimarySlaveFileName = findArg(args, "-hdb", null);
        hardDiskB = createHardDiskBlockDevice(hardDiskPrimarySlaveFileName);

        String hardDiskSecondaryMasterFileName = findArg(args, "-hdc", null);
        hardDiskC = createHardDiskBlockDevice(hardDiskSecondaryMasterFileName);

        String hardDiskSecondarySlaveFileName = findArg(args, "-hdd", null);
        hardDiskD = createHardDiskBlockDevice(hardDiskSecondarySlaveFileName);

        String cdRomFileName = findArg(args, "-cdrom", null);
        if (cdRomFileName != null)
        {
	    try {
		Class ioDeviceClass = Class.forName("org.jpc.support.FileBackedSeekableIODevice");	    
		SeekableIODevice ioDevice = (SeekableIODevice)(ioDeviceClass.newInstance());
		ioDevice.configure(cdRomFileName);
		hardDiskC = new CDROMBlockDevice(ioDevice);
		bootKey = DriveSet.CD_BOOT;
	    } catch (Exception e) {}
        }

        String bootArg = findArg(args, "-boot", null);
        if (bootArg != null)
        {
            bootArg = bootArg.toLowerCase();
            if (bootArg.equals("fda"))
                bootKey = DriveSet.FLOPPY_BOOT;
            else if (bootArg.equals("hda"))
                bootKey = DriveSet.HARD_DRIVE_BOOT;
            else if (bootArg.equals("cdrom"))
                bootKey = DriveSet.CD_BOOT;
        }

        return new DriveSet(bootKey, floppyA, floppyB, hardDiskA, hardDiskB, hardDiskC, hardDiskD);
    }
}
