/*
    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.emulator.processor;

import org.jpc.emulator.memory.*;

import java.io.*;
import static org.jpc.emulator.processor.ProtectedModeSegment.*;

/**
 * 
 * @author Chris Dennis
 */
public class SegmentFactory
{
    private static final long DESCRIPTOR_TYPE = 0x100000000000L;
    private static final long SEGMENT_TYPE = 0xf0000000000L;
    
    public static final Segment NULL_SEGMENT = new NullSegment();
    
    private SegmentFactory()
    {
    }

    public static Segment createRealModeSegment(AddressSpace memory, int selector)
    {
        if (memory == null)
            throw new NullPointerException("Null reference to memory");

        return new RealModeSegment(memory, selector);
    }

    public static Segment createRealModeSegment(AddressSpace memory, Segment ancestor)
    {
        if (memory == null)
            throw new NullPointerException("Null reference to memory");

        return new RealModeSegment(memory, ancestor);
    }

    public static Segment createVirtual8086ModeSegment(AddressSpace memory, int selector, boolean isCode)
    {
        if (memory == null)
            throw new NullPointerException("Null reference to memory");

        return new Virtual8086ModeSegment(memory, selector, isCode);
    }
    
    public static Segment createDescriptorTableSegment(AddressSpace memory, int base, int limit)
    {
        if (memory == null)
            throw new NullPointerException("Null reference to memory");

        return new DescriptorTableSegment(memory, base, limit);
    }

    public static Segment createProtectedModeSegment(AddressSpace memory, int selector, long descriptor)
    {
        return createProtectedModeSegment(memory, selector, descriptor, false);
    }

    public static Segment createProtectedModeSegment(AddressSpace memory, int selector, long descriptor, boolean isStack)
    {
        switch ((int) ((descriptor & (DESCRIPTOR_TYPE | SEGMENT_TYPE)) >>> 40)) {

            // System Segments 
            default:
            case 0x00: //Reserved
            case 0x08: //Reserved
            case 0x0a: //Reserved
            case 0x0d: //Reserved
                throw new ProcessorException(ProcessorException.Type.GENERAL_PROTECTION, 0, true);//ProcessorException.GENERAL_PROTECTION_0;
            case 0x01: //System Segment: 16-bit TSS (Available)
                return new ProtectedModeSegment.Available16BitTSS(memory, selector, descriptor);
            case 0x02: //System Segment: LDT
                return new ProtectedModeSegment.LDT(memory, selector, descriptor);
            case 0x03: //System Segment: 16-bit TSS (Busy)
                return new ProtectedModeSegment.Busy16BitTSS(memory, selector, descriptor);
            case 0x04: //System Segment: 16-bit Call Gate
                return new ProtectedModeSegment.CallGate16Bit(memory, selector, descriptor);
            case 0x05: //System Segment: Task Gate
                return new ProtectedModeSegment.TaskGate(memory, selector, descriptor);
            case 0x06: //System Segment: 16-bit Interrupt Gate
                return new ProtectedModeSegment.InterruptGate16Bit(memory, selector, descriptor);
            case 0x07: //System Segment: 16-bit Trap Gate
                return new ProtectedModeSegment.TrapGate16Bit(memory, selector, descriptor);
            case TYPE_AVAILABLE_32_TSS: //System Segment: 32-bit TSS (Available)
                return new ProtectedModeSegment.Available32BitTSS(memory, selector, descriptor);
            case TYPE_BUSY_32_TSS: //System Segment: 32-bit TSS (Busy)
                return new ProtectedModeSegment.Busy32BitTSS(memory, selector, descriptor);
            case 0x0c: //System Segment: 32-bit Call Gate
                return new ProtectedModeSegment.CallGate32Bit(memory, selector, descriptor);
            case 0x0e: //System Segment: 32-bit Interrupt Gate
                return new ProtectedModeSegment.InterruptGate32Bit(memory, selector, descriptor);
            case 0x0f: //System Segment: 32-bit Trap Gate
                return new ProtectedModeSegment.TrapGate32Bit(memory, selector, descriptor);

            // Data Segments
            case 0x10: //Data Segment: Read-Only
                if (isStack)
                    return new ProtectedModeSegment.ReadOnlyDataStackSegment(memory, selector, descriptor);
                else
                    return new ProtectedModeSegment.ReadOnlyDataSegment(memory, selector, descriptor);
            case 0x11: //Data Segment: Read-Only, Accessed
                if (isStack)
                    return new ProtectedModeSegment.ReadOnlyAccessedStackSegment(memory, selector, descriptor);
                else
                    return new ProtectedModeSegment.ReadOnlyAccessedDataSegment(memory, selector, descriptor);
            case 0x12: //Data Segment: Read/Write
                if (isStack)
                    return new ProtectedModeSegment.ReadWriteStackSegment(memory, selector, descriptor);
                else
                    return new ProtectedModeSegment.ReadWriteDataSegment(memory, selector, descriptor);
            case 0x13: //Data Segment: Read/Write, Accessed
                if (isStack)
                    return new ProtectedModeSegment.ReadWriteAccessedStackSegment(memory, selector, descriptor);
                else
                    return new ProtectedModeSegment.ReadWriteAccessedDataSegment(memory, selector, descriptor);
            case 0x14: //Data Segment: Read-Only, Expand-Down
                throw new IllegalStateException("Unimplemented Data Segment: Read-Only, Expand-Down");
            case 0x15: //Data Segment: Read-Only, Expand-Down, Accessed
                throw new IllegalStateException("Unimplemented Data Segment: Read-Only, Expand-Down, Accessed");
            case 0x16: //Data Segment: Read/Write, Expand-Down
                return new ProtectedModeExpandDownSegment.ReadWriteDataSegment(memory, selector, descriptor);
            case 0x17: //Data Segment: Read/Write, Expand-Down, Accessed
                throw new IllegalStateException("Unimplemented Data Segment: Read/Write, Expand-Down, Accessed");

            // Code Segments
            case 0x18: //Code, Execute-Only
                return new ProtectedModeSegment.ExecuteOnlyCodeSegment(memory, selector, descriptor);
            case 0x19: //Code, Execute-Only, Accessed
                throw new IllegalStateException("Unimplemented Code Segment: Execute-Only, Accessed");
            case 0x1a: //Code, Execute/Read
                return new ProtectedModeSegment.ExecuteReadCodeSegment(memory, selector, descriptor);
            case 0x1b: //Code, Execute/Read, Accessed
                return new ProtectedModeSegment.ExecuteReadAccessedCodeSegment(memory, selector, descriptor);
            case 0x1c: //Code: Execute-Only, Conforming
                throw new IllegalStateException("Unimplemented Code Segment: Execute-Only, Conforming");
            case 0x1d: //Code: Execute-Only, Conforming, Accessed
                return new ProtectedModeSegment.ExecuteOnlyConformingAccessedCodeSegment(memory, selector, descriptor);
            case 0x1e: //Code: Execute/Read, Conforming
                return new ProtectedModeSegment.ExecuteReadConformingCodeSegment(memory, selector, descriptor);
            case 0x1f: //Code: Execute/Read, Conforming, Accessed
                return new ProtectedModeSegment.ExecuteReadConformingAccessedCodeSegment(memory, selector, descriptor);
        }
    }

    public static final class NullSegment extends Segment
    {
        public NullSegment()
        {
            super(null);
        }

        public void printState()
        {
            System.out.println("Null Segment");
        }

        public void saveState(DataOutput output) throws IOException
        {
            output.writeInt(4);
        }

        public int getType()
        {
            throw new ProcessorException(ProcessorException.Type.GENERAL_PROTECTION, 0, true);//ProcessorException.GENERAL_PROTECTION_0;
        }

        public int getSelector()
        {
            return 0;
        }

        public void checkAddress(int offset)
        {
            throw new ProcessorException(ProcessorException.Type.GENERAL_PROTECTION, 0, true);//ProcessorException.GENERAL_PROTECTION_0;
        }

        public int translateAddressRead(int offset)
        {
            throw new ProcessorException(ProcessorException.Type.GENERAL_PROTECTION, 0, true);//ProcessorException.GENERAL_PROTECTION_0;
        }

        public int translateAddressWrite(int offset)
        {
            throw new ProcessorException(ProcessorException.Type.GENERAL_PROTECTION, 0, true);//ProcessorException.GENERAL_PROTECTION_0;
        }

        public void invalidateAddress(int offset)
        {
            throw new ProcessorException(ProcessorException.Type.GENERAL_PROTECTION, 0, true);//ProcessorException.GENERAL_PROTECTION_0;
        }

        public int getBase()
        {
            throw new IllegalStateException(getClass().toString());
        }

        public int getLimit()
        {
            throw new IllegalStateException(getClass().toString());
        }
        
        public int getRawLimit()
        {
            throw new IllegalStateException(getClass().toString());
        }

        public boolean setSelector(int selector)
        {
            throw new IllegalStateException(getClass().toString());
        }

        public int getDPL()
        {
            throw new IllegalStateException(getClass().toString());
        }

        public int getRPL()
        {
            throw new IllegalStateException(getClass().toString());
        }

        public void setRPL(int cpl)
        {
            throw new IllegalStateException(getClass().toString());
        }

        public boolean getDefaultSizeFlag()
        {
            throw new IllegalStateException(getClass().toString());
        }

        public boolean isPresent()
        {
            return true;
        }

        public boolean isSystem()
        {
            return false;
        }
    }
}
