import java.io.DataInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;

class KeyEvent {
    static final int VK_LEFT = -12345678;
    static final int VK_RIGHT = -12345678;
    static final int VK_UP = -12345678;
    static final int VK_DOWN = -12345678;
    static final int VK_SPACE = -12345678;
    static final int VK_F1 = -12345678;
    static final int VK_BACK_SPACE = -12345678;
    static final int VK_ENTER = -12345678;
}

public class GameShell implements Runnable {
    public static GameFrame gameFrame = null;
    private static String charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"\243$%^&*()-_=+[{]};:'@#~,<.>/?\\| ";
    public int mouseActionTimeout;
    public int loadingStep;
    public String logoHeaderText;
    public boolean keyLeft;
    public boolean keyRight;
    public boolean keyUp;
    public boolean keyDown;
    public boolean keySpace;
    public int threadSleep;
    public int mouseX;
    public int mouseY;
    public int mouseButtonDown;
    public int lastMouseButtonDown;
    public boolean interlace;
    public String inputTextCurrent;
    public String inputTextFinal;
    public String inputPmCurrent;
    public String inputPmFinal;
    public int appletWidth;
    public int appletHeight;
    private Thread appletThread;
    private int targetFps;
    private int maxDrawTime;
    private long timings[];
    private boolean startedAsApplet;
    private int stopTimeout;
    private int interlaceTimer;
    private boolean hasRefererLogoNotused;
    private int loadingProgressPercent;
    private String loadingProgessText;
    /*
    private Font fontTimesRoman15;
    private Font fontHelvetica13b;
    private Font fontHelvetica12;
    private Image imageLogo;*/
    protected int fps;

    public GameShell() {
        appletWidth = 512;
        appletHeight = 344;
        targetFps = 20;
        maxDrawTime = 1000;
        timings = new long[10];
        loadingStep = 1;
        hasRefererLogoNotused = false;
        loadingProgessText = "Loading";
        /*
        fontTimesRoman15 = new Font("TimesRoman", 0, 15);
        fontHelvetica13b = new Font("Helvetica", Font.BOLD, 13);
        fontHelvetica12 = new Font("Helvetica", 0, 12);*/
        keyLeft = false;
        keyRight = false;
        keyUp = false;
        keyDown = false;
        keySpace = false;
        threadSleep = 1;
        interlace = false;
        inputTextCurrent = "";
        inputTextFinal = "";
        inputPmCurrent = "";
        inputPmFinal = "";
    }

    private void dummy() {
        this.keyPressed('A', 97);
        this.keyReleased(97);
        this.mouseMoved(0, 0);
        this.mouseReleased(0, 0);
        this.mousePressed(0, 0, 0);
        this.mouseDragged(0, 0, 0);
    }

    protected void startGame() {
    }

    protected synchronized void handleInputs() {
    }

    protected void onClosing() {
    }

    protected synchronized void draw() {
    }

    protected void startApplication(int width, int height, String title, boolean resizeable) {
        startedAsApplet = false;
        System.out.println("Started application");
        appletWidth = width;
        appletHeight = height;
        gameFrame = new GameFrame(this, width, height, title, resizeable, false);
        loadingStep = 1;
        appletThread = new Thread(this);
        appletThread.start();
        appletThread.setPriority(1);
        dummy();
    }

    protected void setTargetFps(int i) {
        targetFps = 1000 / i;
    }

    protected void resetTimings() {
        for (int i = 0; i < 10; i++) {
            timings[i] = 0L;
        }
    }

    public synchronized void keyPressed(char chr, int code) {
        handleKeyPress(chr);
        mouseActionTimeout = 0;

        if (code == KeyEvent.VK_LEFT) {
            keyLeft = true;
        } else if (code == KeyEvent.VK_RIGHT) {
            keyRight = true;
        } else if (code == KeyEvent.VK_UP) {
            keyUp = true;
        } else if (code == KeyEvent.VK_DOWN) {
            keyDown = true;
        } else if (code == KeyEvent.VK_SPACE) {
            keySpace = true;
        } else if (code == KeyEvent.VK_F1) {
            interlace = !interlace;
        } else {
            boolean foundText = false;

            for (int i = 0; i < charMap.length(); i++) {
                if (charMap.charAt(i) == chr) {
                    foundText = true;
                    break;
                }
            }

            if (foundText) {
                if (inputTextCurrent.length() < 20) {
                    inputTextCurrent += chr;
                }
                if (inputPmCurrent.length() < 80) {
                    inputPmCurrent += chr;
                }
            }
        }
        if (code == KeyEvent.VK_BACK_SPACE) {
            if (inputTextCurrent.length() > 0) {
                inputTextCurrent = inputTextCurrent.substring(0, inputTextCurrent.length() - 1);
            }
            if (inputPmCurrent.length() > 0) {
                inputPmCurrent = inputPmCurrent.substring(0, inputPmCurrent.length() - 1);
            }
        }
        if (code == KeyEvent.VK_ENTER) {
            inputTextFinal = inputTextCurrent;
            inputPmFinal = inputPmCurrent;
        }
    }

    protected void handleKeyPress(int i) {
    }


    public synchronized void keyReleased(int code) {
        if (code == KeyEvent.VK_LEFT) {
            keyLeft = false;
        } else if (code == KeyEvent.VK_RIGHT) {
            keyRight = false;
        } else if (code == KeyEvent.VK_UP) {
            keyUp = false;
        } else if (code == KeyEvent.VK_DOWN) {
            keyDown = false;
        } else if (code == KeyEvent.VK_SPACE) {
            keySpace = false;
        }
    }

    public synchronized void mouseMoved(int x, int y) {
        mouseX = x;
        mouseY = y;
        mouseButtonDown = 0;
        mouseActionTimeout = 0;
    }

    public synchronized void mouseReleased(int x, int y) {
        mouseX = x;
        mouseY = y;
        mouseButtonDown = 0;
    }

    public synchronized void mousePressed(int x, int y, int button) {
        mouseX = x;
        mouseY = y;
        mouseButtonDown = button;
        lastMouseButtonDown = mouseButtonDown;
        mouseActionTimeout = 0;
        handleMouseDown(mouseButtonDown, x, y);
    }

    public synchronized void mouseDragged(int x, int y, int button) {
        mouseX = x;
        mouseY = y;
        mouseButtonDown = button;
    }

    protected void handleMouseDown(int i, int j, int k) {
    }

    public void init() {
        startedAsApplet = true;
        System.out.println("Started applet");
        /*
        appletWidth = 512;
        appletHeight = 344;*/
        loadingStep = 1;
        /*
        Utility.appletCodeBase = getCodeBase();
        addMouseListener(this);
        addMouseMotionListener(this);
        addKeyListener(this);*/
        startThread(this);
    }

    public void start() {
        if (stopTimeout >= 0)
            stopTimeout = 0;
    }

    public void stop() {
        if (stopTimeout >= 0)
            stopTimeout = 4000 / targetFps;
    }

    public void destroy() {
        stopTimeout = -1;

        try {
            Thread.sleep(5000L);
        } catch (Exception ignored) {
        }

        if (stopTimeout == -1) {
            System.out.println("5 seconds expired, forcing kill");
            closeProgram();
        }
    }

    private void closeProgram() {
        stopTimeout = -2;
        System.out.println("Closing program");
        onClosing();

        try {
            Thread.sleep(1000L);
        } catch (Exception ignored) {
        }

        System.exit(0);
    }

    public void run() {
        if (loadingStep == 1) {
            loadingStep = 2;
            //graphics = getGraphics();
            loadJagex();
            drawLoadingScreen(0, "Loading...");
            startGame();
            loadingStep = 0;
        }

        int i = 0;
        int j = 256;
        int sleep = 1;
        int i1 = 0;

        for (int j1 = 0; j1 < 10; j1++) {
            timings[j1] = System.currentTimeMillis();
        }

        while (stopTimeout >= 0) {
            if (stopTimeout > 0) {
                stopTimeout--;
                if (stopTimeout == 0) {
                    closeProgram();
                    appletThread = null;
                    return;
                }
            }
            int k1 = j;
            int lastSleep = sleep;
            j = 300;
            sleep = 1;
            long time = System.currentTimeMillis();
            if (timings[i] == 0L) {
                j = k1;
                sleep = lastSleep;
            } else if (time > timings[i])
                j = (int) ((long) (2560 * targetFps) / (time - timings[i]));
            if (j < 25)
                j = 25;
            if (j > 256) {
                j = 256;
                sleep = (int) ((long) targetFps - (time - timings[i]) / 10L);
                if (sleep < threadSleep)
                    sleep = threadSleep;
            }
            try {
                Thread.sleep(sleep);
            } catch (InterruptedException ignored) {
            }
            timings[i] = time;
            i = (i + 1) % 10;
            if (sleep > 1) {
                for (int j2 = 0; j2 < 10; j2++)
                    if (timings[j2] != 0L)
                        timings[j2] += sleep;

            }
            int k2 = 0;
            while (i1 < 256) {
                handleInputs();
                i1 += j;
                if (++k2 > maxDrawTime) {
                    i1 = 0;
                    interlaceTimer += 6;
                    if (interlaceTimer > 25) {
                        interlaceTimer = 0;
                        interlace = true;
                    }
                    break;
                }
            }
            interlaceTimer--;
            i1 &= 0xff;
            draw();

            this.fps = (1000 * j) / (this.targetFps * 256);
        }

        if (stopTimeout == -1)
            closeProgram();

        appletThread = null;
    }

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

    public void paint(Graphics g) {
        if (loadingStep == 2 && imageLogo != null) {
            drawLoadingScreen(loadingProgressPercent, loadingProgessText);
        }
    }*/

    private void loadJagex() {
        //graphics.setColor(Color.black);
        //graphics.fillRect(0, 0, appletWidth, appletHeight);

        byte buff[] = readDataFile("jagex.jag", "Jagex library", 0);

        if (buff != null) {
            byte logo[] = Utility.loadData("logo.tga", 0, buff);
            //imageLogo = createImage(logo);
        }

        buff = readDataFile("fonts" + Version.FONTS + ".jag", "Game fonts", 5);

        if (buff != null) {
            Surface.createFont(Utility.loadData("h11p.jf", 0, buff), 0);
            Surface.createFont(Utility.loadData("h12b.jf", 0, buff), 1);
            Surface.createFont(Utility.loadData("h12p.jf", 0, buff), 2);
            Surface.createFont(Utility.loadData("h13b.jf", 0, buff), 3);
            Surface.createFont(Utility.loadData("h14b.jf", 0, buff), 4);
            Surface.createFont(Utility.loadData("h16b.jf", 0, buff), 5);
            Surface.createFont(Utility.loadData("h20b.jf", 0, buff), 6);
            Surface.createFont(Utility.loadData("h24b.jf", 0, buff), 7);
        }
    }

    private void drawLoadingScreen(int percent, String text) {
        /*
        try {
            int midx = (appletWidth - 281) / 2;
            int midy = (appletHeight - 148) / 2;
            graphics.setColor(Color.black);
            graphics.fillRect(0, 0, appletWidth, appletHeight);

            if (!hasRefererLogoNotused) {
                graphics.drawImage(imageLogo, midx, midy, this);
            }

            midx += 2;
            midy += 90;
            loadingProgressPercent = percent;
            loadingProgessText = text;
            graphics.setColor(new Color(132, 132, 132));

            if (hasRefererLogoNotused) {
                graphics.setColor(new Color(220, 0, 0));
            }

            graphics.drawRect(midx - 2, midy - 2, 280, 23);
            graphics.fillRect(midx, midy, (277 * percent) / 100, 20);
            graphics.setColor(new Color(198, 198, 198));

            if (hasRefererLogoNotused) {
                graphics.setColor(new Color(255, 255, 255));
            }

            drawString(graphics, text, fontTimesRoman15, midx + 138, midy + 10);

            if (!hasRefererLogoNotused) {
                drawString(graphics, "Created by JAGeX - visit www.jagex.com", fontHelvetica13b, midx + 138, midy + 30);
                drawString(graphics, "\2512001-2002 Andrew Gower and Jagex Ltd", fontHelvetica13b, midx + 138, midy + 44);
            } else {
                graphics.setColor(new Color(132, 132, 152));
                drawString(graphics, "\2512001-2002 Andrew Gower and Jagex Ltd", fontHelvetica12, midx + 138, appletHeight - 20);
            }

            if (logoHeaderText != null) {
                graphics.setColor(Color.white);
                drawString(graphics, logoHeaderText, fontHelvetica13b, midx + 138, midy - 120);
            }
        } catch (Exception ignored) {
        }*/
    }

    protected void showLoadingProgress(int i, String s) {
        /*
        try {
            int j = (appletWidth - 281) / 2;
            int k = (appletHeight - 148) / 2;
            j += 2;
            k += 90;
            loadingProgressPercent = i;
            loadingProgessText = s;
            int l = (277 * i) / 100;
            graphics.setColor(new Color(132, 132, 132));
            if (hasRefererLogoNotused)
                graphics.setColor(new Color(220, 0, 0));
            graphics.fillRect(j, k, l, 20);
            graphics.setColor(Color.black);
            graphics.fillRect(j + l, k, 277 - l, 20);
            graphics.setColor(new Color(198, 198, 198));
            if (hasRefererLogoNotused)
                graphics.setColor(new Color(255, 255, 255));
            drawString(graphics, s, fontTimesRoman15, j + 138, k + 10);
        } catch (Exception ignored) {
        }*/
    }

    /*
    protected void drawString(Graphics g, String s, Font font, int i, int j) {
        Object obj;
        if (gameFrame == null)
            obj = this;
        else
            obj = gameFrame;
        FontMetrics fontmetrics = ((Component) (obj)).getFontMetrics(font);
        fontmetrics.stringWidth(s);
        g.setFont(font);
        g.drawString(s, i - fontmetrics.stringWidth(s) / 2, j + fontmetrics.getHeight() / 4);
    }

    private Image createImage(byte buff[]) {
        int i = buff[13] * 256 + buff[12];
        int j = buff[15] * 256 + buff[14];
        byte abyte1[] = new byte[256];
        byte abyte2[] = new byte[256];
        byte abyte3[] = new byte[256];

        for (int k = 0; k < 256; k++) {
            abyte1[k] = buff[20 + k * 3];
            abyte2[k] = buff[19 + k * 3];
            abyte3[k] = buff[18 + k * 3];
        }

        IndexColorModel indexcolormodel = new IndexColorModel(8, 256, abyte1, abyte2, abyte3);

        byte abyte4[] = new byte[i * j];
        int l = 0;

        for (int i1 = j - 1; i1 >= 0; i1--) {
            for (int j1 = 0; j1 < i; j1++)
                abyte4[l++] = buff[786 + j1 + i1 * i];

        }

        MemoryImageSource memoryimagesource = new MemoryImageSource(i, j, indexcolormodel, abyte4, 0, i);
        return createImage(memoryimagesource);
    }*/

    protected byte[] readDataFile(String file, String description, int percent) {
        file = "./data204/" + file;
        int archiveSize = 0;
        int archiveSizeCompressed = 0;
        byte archiveData[] = null;

        try {
            showLoadingProgress(percent, "Loading " + description + " - 0%");
            java.io.InputStream inputstream = Utility.openFile(file);
            DataInputStream datainputstream = new DataInputStream(inputstream);
            byte header[] = new byte[6];
            datainputstream.readFully(header, 0, 6);
            archiveSize = ((header[0] & 0xff) << 16) + ((header[1] & 0xff) << 8) + (header[2] & 0xff);
            archiveSizeCompressed = ((header[3] & 0xff) << 16) + ((header[4] & 0xff) << 8) + (header[5] & 0xff);
            showLoadingProgress(percent, "Loading " + description + " - 5%");
            int read = 0;
            archiveData = new byte[archiveSizeCompressed];
            while (read < archiveSizeCompressed) {
                int length = archiveSizeCompressed - read;
                if (length > 1000)
                    length = 1000;
                datainputstream.readFully(archiveData, read, length);
                read += length;
                showLoadingProgress(percent, "Loading " + description + " - " + (5 + (read * 95) / archiveSizeCompressed) + "%");
            }
            datainputstream.close();
        } catch (IOException ignored) {
        }

        showLoadingProgress(percent, "Unpacking " + description);

        if (archiveSizeCompressed != archiveSize) {
            byte decompressed[] = new byte[archiveSize];
            BZLib.decompress(decompressed, archiveSize, archiveData, archiveSizeCompressed, 0);
            return decompressed;
        } else {
            return archiveData;
        }
    }

    protected Socket createSocket(String s, int i) throws IOException {
        Socket socket = new Socket(InetAddress.getByName(s), i);
        socket.setSoTimeout(30000);
        socket.setTcpNoDelay(true);
        return socket;
    }

    protected void startThread(Runnable runnable) {
        Thread thread = new Thread(runnable);
        thread.setDaemon(true);
        thread.start();
    }
}
