package org.unixuser.haruyama.nich;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.StringItem;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class NichMIDlet extends MIDlet implements CommandListener,
        ItemCommandListener, Runnable {

    private Form mainForm;

    private Form loadForm;

    private Form paintForm;

    private Form boardForm;

    private Form threadForm;

    private Display display;

    private Thread commandThread;

    private Command currentCommand;

    private Item currentItem;

    private StringBuffer buffer = new StringBuffer();

    private int currentThreadIndex;

    private String currentThreadTitle;

    private String currentThreadInput;

    private Alert outOfMemoryAlert;

    private final static int CONNECTION_OK = 0;

    private final static int CONNECTION_FAILED = 1;

    private final static Command CMD_EXIT = new Command("Exit", Command.EXIT, 1);

    private final static Command CMD_BACK = new Command("Back", Command.BACK, 1);

    private final static Command CMD_BOARD = new Command("Load", Command.ITEM,
            5);

    private final static Command CMD_THREAD = new Command("Load", Command.ITEM,
            5);

    private final static Command CMD_NEXT = new Command("Next 100",
            Command.SCREEN, 5);

    private final static Command CMD_PREV = new Command("Prev 100",
            Command.SCREEN, 5);

    public NichMIDlet() {
        mainForm = new Form("2chブラウザ試作");
        display = Display.getDisplay(this);

        mainForm.addCommand(CMD_EXIT);

        // ほんとはメソッド化
        Item item = new StringItem("携帯電話ゲーム", "http://hobby7.2ch.net/appli/",
                Item.HYPERLINK);
        item.setDefaultCommand(CMD_BOARD);
        item.setItemCommandListener(this);
        mainForm.append(item);

        item = new StringItem("国内サッカー", "http://ex12.2ch.net/soccer/",
                Item.HYPERLINK);
        item.setDefaultCommand(CMD_BOARD);
        item.setItemCommandListener(this);
        mainForm.append(item);

        loadForm = new Form("読み込み中");
        paintForm = new Form(" 描画中");

    }

    protected void startApp() throws MIDletStateChangeException {
        display.setCurrent(mainForm);

    }

    protected void pauseApp() {
        // TODO Auto-generated method stub

    }

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
        // TODO Auto-generated method stub

    }

    public void commandAction(Command command, Item item) {
        synchronized (this) {
            if (commandThread != null) {
                // process only one command at a time
                return;
            }

            currentCommand = command;
            currentItem = item;
            commandThread = new Thread(this);
            commandThread.start();
        }

    }

    public void commandAction(Command command, Displayable displayable) {

        if (command == CMD_BACK) {
            if (displayable == boardForm) {
                display.setCurrent(mainForm);
            } else if (displayable == threadForm) {
                display.setCurrent(boardForm);
            } else if (displayable == outOfMemoryAlert) {
            
            }
        } else if (command == CMD_EXIT) {
            try {
                destroyApp(false);
            } catch (MIDletStateChangeException e) {

            }
            notifyDestroyed();
        } else if (command == CMD_NEXT) {

            createThreadForm(currentThreadTitle, currentThreadInput,
                    currentThreadIndex + 100);
        } else if (command == CMD_PREV) {
            createThreadForm(currentThreadTitle, currentThreadInput,
                    currentThreadIndex - 100);

        }
    }

    private int getViaHttpConnection(String url) throws IOException {

        InputStream is = null;
        InputStreamReader isr = null;
        HttpConnection connection = null;

        // System.err.println(url);
        // TODO:
        try {
            connection = (HttpConnection) Connector.open(url);
            connection.setRequestMethod(HttpConnection.GET);
            connection.setRequestProperty("User-Agent",
                    "Monazilla/1.00 (2chj2me/0.0.1)");
            connection.setRequestProperty("x-2ch-ua", "2chj2me/0.0.1");

            buffer.setLength(0);
            int rc = connection.getResponseCode();
            if (rc != HttpConnection.HTTP_OK) {

                buffer.append("Response Code: " + connection.getResponseCode()
                        + "\n");
                buffer.append("Response Message: "
                        + connection.getResponseMessage() + "\n\n");

                return CONNECTION_FAILED;
            }

            is = connection.openInputStream();
            isr = new InputStreamReader(is, "Shift_JIS");
            long len = connection.getLength();
            if (len != -1) {
                // Read exactly Content-Length bytes
                for (int i = 0; i < len; i++) {
                    int ch;
                    if ((ch = isr.read()) != -1) {
                        // if (ch <= ' ') {
                        // ch = ' ';
                        // }
                        buffer.append((char) ch);
                    }
                }
            } else {
                char data[] = new char[256];
                int n;
                do {
                    n = isr.read(data, 0, data.length);
                    for (int i = 0; i < n; i++) {

                        // int ch = data[i] & 0x000000ff;
                        // buffer.append((char)ch);
                        buffer.append(data[i]);
                    }
                } while (n > 0);
            }

        } finally {

            if (is != null) {
                is.close();
            }
            if (isr != null) {
                isr.close();
            }

            if (connection != null) {
                connection.close();
            }
        }
        System.gc();
        System.err.println("done");
        return CONNECTION_OK;
    }

    public void run() {
        if (currentCommand == CMD_BOARD) {
            display.setCurrent(loadForm);

            int rc = CONNECTION_FAILED;
            try {
                rc = getViaHttpConnection(((StringItem) currentItem).getText()
                        + "subject.txt");
            } catch (IOException e) {

            }
            // String result = null;
            // try {
            // result = new String(buffer.toString().getBytes("ISO8859_1"),
            // "Shift_JIS");
            // }catch(UnsupportedEncodingException e) {
            // rc = CONNECTION_FAILED;
            // }
            // System.err.println(result);
            if (rc == CONNECTION_FAILED) {
                display.setCurrent(mainForm);
            } else if (rc == CONNECTION_OK) {
                display.setCurrent(paintForm);
                createBoardForm(currentItem.getLabel(),
                        ((StringItem) currentItem).getText(), buffer.toString());
            }

        } else if (currentCommand == CMD_THREAD) {
            display.setCurrent(loadForm);

            currentThreadInput = null;
            threadForm = null;
            System.gc();
            int rc = CONNECTION_FAILED;
            try {
                rc = getViaHttpConnection(((StringItem) currentItem).getText());
            } catch (OutOfMemoryError e) {
                e.printStackTrace();
                outOfMemoryAlert = new Alert("OutOfMemoryError", "Threadの描画中にOutOfMemoryErrorが発生しました。", null , null);
                outOfMemoryAlert.setTimeout(Alert.FOREVER);
                outOfMemoryAlert.addCommand(CMD_BACK);
                outOfMemoryAlert.setCommandListener(this);
                display.setCurrent(outOfMemoryAlert);
                return;

            } catch (IOException e) {

            }

            if (rc == CONNECTION_FAILED) {
                display.setCurrent(boardForm);
            } else if (rc == CONNECTION_OK) {
                display.setCurrent(paintForm);
                createThreadForm(currentItem.getLabel(), buffer.toString(), 1);
            }
        }

        synchronized (this) {
            commandThread = null;
        }

    }

    private void createThreadForm(String title, String input, int index) {

        if (index <= 0) {
            // TODO: alert
            return;
        }

        if (index > 1000) {
            // TODO: alert
            return;
        }

        int indexBegin = 0;
        int indexEnd;

        for (int i = 1; i < index; ++i) {
            indexEnd = input.indexOf('\n', indexBegin);
            if (indexEnd == -1) {
                Alert alert = new Alert("このスレッドは" + (i - 1) + "レスしかないようです。");
                display.setCurrent(alert);
                return;
            }
            indexBegin = indexEnd + 1;
        }

        threadForm = new Form(title);
        threadForm.addCommand(CMD_BACK);
        currentThreadTitle = title;
        currentThreadInput = input;
        currentThreadIndex = index;
        if (index < 900) {
            threadForm.addCommand(CMD_NEXT);
        }
        if (index > 100) {
            threadForm.addCommand(CMD_PREV);
        }
        threadForm.setCommandListener(this);
        StringBuffer bodyBuffer = new StringBuffer();

        try {
            for (int i = index; i < index + 100; ++i) {
                indexEnd = input.indexOf("<>", indexBegin);
                if (indexEnd == -1)
                    break;

                String name = input.substring(indexBegin, indexEnd);
                indexBegin = indexEnd + 2;

                indexEnd = input.indexOf("<>", indexBegin);
                if (indexEnd == -1)
                    break;

                String address = input.substring(indexBegin, indexEnd);
                indexBegin = indexEnd + 2;

                indexEnd = input.indexOf("<>", indexBegin);
                if (indexEnd == -1)
                    break;

                String dateAndID = input.substring(indexBegin, indexEnd);
                indexBegin = indexEnd + 2;

                indexEnd = input.indexOf("<>", indexBegin);
                if (indexEnd == -1)
                    break;

                String body = input.substring(indexBegin, indexEnd);
                // ほんとは別メソッドに
                {

                    bodyBuffer.setLength(0);
                    System.gc();
                    int indexBegin2 = 0;
                    int indexEnd2;

                    do {
                        indexEnd2 = body.indexOf("<br>", indexBegin2);
                        if (indexEnd2 == -1) {
                            bodyBuffer.append(body.substring(indexBegin2));
                            break;
                        }
                        bodyBuffer.append(body
                                .substring(indexBegin2, indexEnd2));
                        bodyBuffer.append('\n');
                        indexBegin2 = indexEnd2 + 4;

                    } while (true);

                }

                indexBegin = indexEnd + 2;

                indexEnd = input.indexOf('\n', indexBegin);
                if (indexEnd == -1)
                    break;
                indexBegin = indexEnd + 1;

                Item item = new StringItem(new Integer(i).toString() + ""
                        + name + " " + address + " " + dateAndID, bodyBuffer
                        .toString());

                threadForm.append(item);

            }
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            
            outOfMemoryAlert = new Alert("OutOfMemoryError", "Threadの描画中にOutOfMemoryErrorが発生しました。", null , null);
            outOfMemoryAlert.setTimeout(Alert.FOREVER);
            outOfMemoryAlert.addCommand(CMD_BACK);
            outOfMemoryAlert.setCommandListener(this);
            display.setCurrent(outOfMemoryAlert);
            return;
        }

        display.setCurrent(threadForm);
    }

    private void createBoardForm(String title, String boardUrl, String input) {
        boardForm = new Form(title);
        boardForm.addCommand(CMD_BACK);
        boardForm.setCommandListener(this);

        int indexBegin = 0;
        int indexEnd;

        for (int i = 0; i < 300; ++i) {
            indexEnd = input.indexOf("<>", indexBegin);
            if (indexEnd == -1)
                break;

            String threadUrl = input.substring(indexBegin, indexEnd);
            indexBegin = indexEnd + 2;
            indexEnd = input.indexOf('\n', indexBegin);

            if (indexEnd == -1)
                break;

            String threadName = input.substring(indexBegin, indexEnd);
            indexBegin = indexEnd + 1;

            Item item = new StringItem(threadName, boardUrl + "dat/"
                    + threadUrl, Item.HYPERLINK);
            item.setDefaultCommand(CMD_THREAD);
            item.setItemCommandListener(this);

            boardForm.append(item);
        }

        display.setCurrent(boardForm);

    }

}
