// ピンイン早打ちゲーム
var app = new Hayauchi();
function Hayauchi() {
    this.MONDAISU = 12;     // 一度にストックする問題数（＝キーの数）
    this.MAXSHUTSUDAI = 12; // 一度に画面に表示する文字数（オーバーすると失敗）
    this.STAGESU = 10;      // ステージ数（この数に達するとステージ１に戻る）
    this.ROUNDSU = 3;       // ラウンド数（１ステージにつきこの回数だけ、リトライできる）
    this.STAGEMONDAISU = 20;    // ステージ問題数（この数の問題に答えればクリア）
    this.DANSU = 32;        // 弾数（１ラウンドで答えられる数）
    this.loaded = false;    // XML読み込み完了フラグ

    this.gameEnable = false;    // ゲームが進行中かどうか（falseはキー入力を無視）
    this.gameStart = false;     // ゲームが開始したかどうか（開始ボタンでtrueになる）
    this.pause = false;         // ゲームが中断中かどうか（中断ボタンでfalseになる）
    this.stage = 0;         // 現在のステージ番号
    this.seikaisu = 0;      // 現ステージでの正解数

    // 次の問題が出題されるまでの時間（ミリ秒）、ステージ毎に異なる
    this.TIMERLIST = [ 3000, 2600, 2300, 2000, 1800, 1600, 1400, 1200, 1000, 900 ];

    // アプリケーションを初期化する
    this.init = function() {
        this.gameEnable = false;
        this.gameStart = false;
        this.pause = true;
        this.stage = 0;
        this.seikaisu = 0;
        this.pageno = 0;
        this.dispDocPage();
        this.mondaiList = null;
        document.getElementById("ekisho").innerHTML= "ピンイン早打ちゲーム";
        document.getElementById("startButton").style.display = "inline";
        document.getElementById("saikaiButton").style.display = "none";
        document.getElementById("info").innerHTML = "&nbsp;";
        document.forms.form0.inputarea.value = "";
        var e = document.getElementById("subwin");
        e.style.display = "none";
        buttonOnmouseout(document.getElementById("startButton"));
        this.dispMondai();
    }

    // ゲームを開始する
    this.startGame = function() {
        document.getElementById("startButton").style.display = "none";
        document.getElementById("info").style.display = "block";
        document.getElementById("inputarea").style.display = "block";
        document.forms.form0.inputarea.focus();
        this.initGame();
        this.gameStart = true;
        this.pause = false;
        document.getElementById("ekisho").innerHTML
            = "GAME START : HIGH SCORE = " + this.highscore;
        this.qIcon.start(true);
        setTimeout("app.initStage()", 3000);
    }

    // キー入力でEnterを押した時のイベント
    this.submitKey = function() {
        if (! app.gameEnable) {
            return false;
        }
        var key = document.forms.form0.inputarea.value;
        if (key == "") {
            return false;
        }
        document.forms.form0.inputarea.value = "";
        if (app.dansu <= 0) {
            document.getElementById("info").innerHTML = "弾切れです！";
        } else if (! app.hantei(key) ) {
            this.missed = true;
            document.getElementById("info").innerHTML = "NG : " + key;
        }
        return false;
    }

    // 漢字データXMLを読み込む
    this.loadXml = function() {
        try {
                var e = document.getElementById("subwin");
                e.style.display = "block";
                e.innerHTML = "データを読み込んでいます。<br/>しばらくお待ちください。";
                document.getElementById("startButton").style.display = "none";
                document.forms.form0.inputarea.value = "";
                this.hanziXml = null;
                this.dispMondai();
                var fname = "hanzi.xml";
                if (isKonqueror() ) {
                    fname = "hanzi_utf16.xml";
                }
                xmlReader.readXml(fname, loaddoc);
        } catch (ex) {
                document.getElementById("subwin").innerHTML
                    = "ファイル読み込み中にエラー発生." + ex;
        }
    }

    // ゲームを初期化する
    this.initGame = function() {
        this.stage = 0;
        this.point = 0;
    }

    // 問題（＝ボタン）を表示する
    this.dispMondai = function() {
        var buttonElm = document.getElementById("button");
        var str = "<table><tr>";
        var classname = "answerButton";
        if (isIE() ) {
             classname = "answerButtonIE";
        }
        for (var i = 0; i < this.MONDAISU; i++) {
            if ( (i % (this.MONDAISU / 2) < 1) && (i != 0) ) {
                str += "</tr><tr>";
            }
            var pin = "";
            if (this.mondaiList) {
                pin = this.mondaiList[i].getAttribute("pin1");
            }
            var pinstr = makePinString(pin);
            var sizeclass = "normalpin";
            if (pinstr.length <= 3) {
                sizeclass = "largepin";
            }
            str += '<td>';
            str += '<a class="' + classname + '" onclick="app.pushButton(this);" ';
            str += 'onmouseover="buttonOnmouseover(this)" ';
            str += 'onmouseout="buttonOnmouseout(this)" ';
            str += 'keystring="' + pin + '" ';
            str += '>';
            str += '<span class="' + sizeclass + '" ';
            str += '>';
            str += pinstr;
            str += '</span>';
            str += '</a>';
            str += '</td>';
        }
        str += "</tr></table>";
        buttonElm.innerHTML = str;
    }

    // 問題を選択する（ダブらないように）
    this.choiceMondai = function() {
        var d = 0;
        do {
            d = dropDice(this.hanziList.length);
        } while (this.checkContain(this.hanziList[d], this.mondaiList));
        return this.hanziList[d];
    }

    // すでに問題に出題済みかどうかをチェックする
    this.checkContain = function(e, mondaiList) {
        for (var i = 0; i < mondaiList.length; i++) {
            if (mondaiList[i]) {
                if (mondaiList[i].getAttribute("zi") == e.getAttribute("zi") ) {
                    return true;
                }
            }
        }
        return false;
    }

    // 問題を出題する（画面に問題の字を表示する）
    this.shutsudai = function() {
        if (this.pause) {
            this.gameEnable = false;
            return;
        }
        var d = 0;
        if (this.shutsudaiList.length >= this.MAXSHUTSUDAI) {
            this.over();
            return;
        }
        do {
            d = dropDice(this.mondaiList.length);
        } while (this.checkContain(this.mondaiList[d], this.shutsudaiList));
        this.shutsudaiList.push(this.mondaiList[d]);
        this.dispShutsudai();
        this.gameEnable = true;
        this.timer = setTimeout("app.shutsudai()", this.timeout);
    }

    // 出題エリアの文字列を表示する（ラウンド、残弾数、出題中の文字）
    this.dispShutsudai = function() {
        var ekisho = document.getElementById("ekisho");
        var str = "<table><tr>";
        str += '<td class="round" id="round" nowrap="norap">';
        str += 'R' + this.round + ":";
        str += "</td>";
        str += '<td class="dansu" id="dansu" nowrap="norap">';
        str += this.dansu;
        str += "</td>";
        str += '<td class="shutsudai" id="shutsudai" nowrap="norap">';
        for (var i = 0; i < this.shutsudaiList.length; i++) {
            str += this.shutsudaiList[i].getAttribute("zi");
        }
        str += "&nbsp;</td>";
        str += "</tr></table>";
        ekisho.innerHTML = str;
    }

    // ラウンドオーバーの時の処理
    this.over = function() {
        this.gameEnable = false;
        var ekisho = document.getElementById("ekisho");
        ekisho.style.textAlign = "left";
        this.dispSeikai();
        if (this.round < this.ROUNDSU) {
            // 次のラウンドへ
            this.initRound();
        } else {
            // ゲームオーバー
            this.gameStart = false;
            if (this.highscore < this.point) {
               ekisho.innerHTML = "HIGH SCORE! :" + this.point;
               this.highscore = this.point;
               saveHighscore(this.highscore);
            } else {
                ekisho.innerHTML = "**** GAME OVER **** SCORE:" + this.point;
            }
            this.qIcon.start(false);
            document.getElementById("startButton").style.display = "inline";
            buttonOnmouseout(document.getElementById("startButton"));
        }
    }

    // 解答ボタンを押した時のイベント処理
    this.pushButton = function(e) {
        if (! this.gameEnable) {
            return;
        }
        if (this.shutsudaiList.length == 0) {
            return;
        }
        var key = e.getAttribute("keystring");
        if (this.dansu <= 0) {
            document.getElementById("info").innerHTML = "弾切れです！";
        } else if (! this.hantei(key) ) {
            this.missed = true;
            document.getElementById("info").innerHTML = "NG : " + makePinString(key);
        }
        document.forms.form0.inputarea.focus();
    }

    // 解答が正解かどうかを判定する(true : 正解, false : 不正解 or 弾切れ)
    this.hantei = function(key) {
        if (this.dansu <= 0) {
            return false;
        }
        document.forms.form0.inputarea.value = "";
        this.dansu--;
        if (document.getElementById("dansu")) {
            document.getElementById("dansu").innerHTML = this.dansu;
        } else {
            return false;
        }
        var hanzi = this.shutsudaiList[0];
        for (var i = 1; i <= 4; i++) {
            var pin = hanzi.getAttribute("pin" + i);
            if (! pin) {
                break;
            }
            if (pin == key) {
                // 正解処理
                this.dispSeikai();
                this.shutsudaiList.shift();
                if (! this.missed) {
                    // ミスなしで答えたら、別の問題に置き換える
                    this.replaceMondai(hanzi);
                }
                this.missed = false;
                this.dispShutsudai();
                this.seikaisu++;
                if (this.seikaisu >= this.STAGEMONDAISU) {
                    if ( (this.round == 1)
                        && (this.seikaisu == (this.DANSU - this.dansu) ) ) {
                        // ステージをミスなしで答えたら、パーフェクト
                        this.dispPerfect();
                    } else {
                        // ステージクリア
                        this.dispStageClear();
                    }
                }
                // 得点を加算
                this.point += (12 - this.shutsudaiList.length + 1) * 10;
                return true;
            }
        }
        // ここにきてしまったら、不正解
        return false;
    }

    // 正解を情報エリアへ表示する
    this.dispSeikai = function() {
        var hanzi = this.shutsudaiList[0];
        var str = "";
        str += hanzi.getAttribute("zi") + "&nbsp;";
        for (var j = 1; j <= 4; j++) {
            var pin = hanzi.getAttribute("pin" + j);
            if (! pin) {
                break;
            }
            str += makePinString(pin) + "&nbsp;";
        }
        document.getElementById("info").innerHTML = str;
    }

    // 問題を置き換える
    this.replaceMondai = function(hanzi) {
        var newhanzi = this.choiceMondai();
        for (var i = 0; i < this.mondaiList.length; i++) {
            if (hanzi.getAttribute("zi") == this.mondaiList[i].getAttribute("zi") ) {
                this.mondaiList[i] = newhanzi;
                this.dispMondai();
                return;
            }
        }
    }

    // ステージ初期化処理
    this.initStage = function() {
        this.stage++;
        if (this.stage > this.STAGESU) {
            this.stage = 1;
        }
        this.seikaisu = 0;
        this.round = 0;
        this.timeout = this.TIMERLIST[this.stage - 1];
        if (this.stage == 1) {
            this.initMondai();
        }
        this.initRound();
    }

    // ラウンド初期化処理
    this.initRound = function() {
        this.gameEnable = false;
        this.round++;
        this.dansu = this.DANSU;

        document.forms.form0.inputarea.value = "";
        var ekisho = document.getElementById("ekisho");
        ekisho.style.textAlign = "left";
        ekisho.innerHTML = "STAGE " + this.stage + " - ROUND " + this.round
            + " : REST = " + (this.STAGEMONDAISU - this.seikaisu);

        this.shutsudaiList = new Array();
        this.dispMondai();
        this.missed = false;

        setTimeout("app.shutsudai()", 3000);
    }

    // 問題の初期化（ゲーム開始時と10ステージクリア時に全問題を入れ換え)
    this.initMondai = function() {
        this.mondaiList = new Array();
        for (var i = 0; i < this.MONDAISU; i++) {
            this.mondaiList.push(this.choiceMondai());
        }
        this.mondaiList = randomArray(this.mondaiList);
    }

    // ステージクリアを表示する
    this.dispStageClear = function() {
        clearTimeout(this.timer);
        var ekisho = document.getElementById("ekisho");
        ekisho.style.textAlign = "left";
        ekisho.innerHTML = "STAGE " + this.stage + " ALL CLEAR! SCORE:" + this.point;
        setTimeout("app.initStage()", 3000);
    }

    // パーフェクトを表示する
    this.dispPerfect = function() {
        clearTimeout(this.timer);
        var ekisho = document.getElementById("ekisho");
        ekisho.style.textAlign = "left";
        ekisho.innerHTML = "PERFECT! BONUS 1000";
        this.point += 1000;
        setTimeout("app.dispStageClear()", 3000);
    }

    // 再開ボタンのイベント処理
    this.saikai = function() {
        this.pause = false;
        document.getElementById("saikaiButton").style.display = "none";
        this.shutsudai();
    }

    // 表面を表示する。
    this.dispOmote = function() {
        if (window.widget) {
            widget.prepareForTransition("ToFront");
        }
        document.getElementById("ura").style.display = "none";
        document.getElementById("quitwin").style.display = "none";
        document.getElementById("omote").style.display = "block";
        if (this.gameStart) {
            document.getElementById("saikaiButton").style.display = "inline";
            document.getElementById("ekisho").innerHTML = "*** PAUSE ***";
            buttonOnmouseout(document.getElementById("saikaiButton"));
        }
        if (window.widget) {
            setTimeout("widget.performTransition();", 0);
        }
    }

    // 裏面を表示する
    this.dispUra = function() {
        if (window.widget) {
            widget.prepareForTransition("ToBack");
        }
        this.pause = true;
        this.gameEnable = false;
        document.getElementById("omote").style.display = "none";
        document.getElementById("ura").style.display = "block";
        buttonOnmouseout(document.getElementById("returnButton"));
        infoout("ibuttonback");
        if (window.widget) {
            setTimeout("widget.performTransition();", 0);
        }
    }

    // ゲーム終了処理（現在のゲームを終了する）
    this.quitGame = function() {
        this.qIcon.start(false);
        this.init();
        this.dispOmote();
    }

    // 終了画面表示
    this.dispQuit = function() {
        if (window.widget) {
            widget.prepareForTransition("ToBack");
        }
        this.pause = true;
        this.gameEnable = false;
        document.getElementById("omote").style.display = "none";
        document.getElementById("quitwin").style.display = "block";
        buttonOnmouseout(document.getElementById("quitButton"));
        buttonOnmouseout(document.getElementById("returnButton2"));
        infoout("qbuttonback");
        if (window.widget) {
            setTimeout("widget.performTransition();", 0);
        }
    }

    // 裏面の説明文を１ページだけ表示する。
    this.dispDocPage = function() {
        if (this.docPageList.length == 0) {
            return;
        }
        if (this.pageno <= 0 ) {
            document.getElementById("prepage").style.display = "none";
            document.getElementById("preback").style.display = "none";
        } else {
            document.getElementById("prepage").style.display = "block";
        }
        if (this.pageno >= this.docPageList.length - 1) {
            document.getElementById("nextpage").style.display = "none";
            document.getElementById("nextback").style.display = "none";
        } else {
            document.getElementById("nextpage").style.display = "block";
        }
        var elm = this.docPageList[this.pageno];
        var str = "";
        var subtitle = elm.getAttribute("subtitle");
        if (subtitle) {
            document.getElementById("doctitle").innerHTML = subtitle;
        }
        var list = elm.getElementsByTagName("Sentence");
        for (var i = 0; i < list.length; i++) {
            var snt = list[i].firstChild.nodeValue;
            var org = "";
            do {
                org = snt;
                snt = snt.replace("\{", "<");
                snt = snt.replace("\}", ">");
            } while (org != snt);
            str += snt;
        }
        document.getElementById("docArea").innerHTML = str;
    }

    // 裏面の前のページアイコンのイベント処理
    this.prepage = function() {
        if (this.pageno > 0) {
            this.pageno--;
        }
        this.dispDocPage();
    }

    // 裏面の次のページアイコンのイベント処理
    this.nextpage = function() {
        if (this.pageno < this.docPageList.length - 1) {
            this.pageno++;
        }
        this.dispDocPage();
    }

    // bodyにマウスが乗った時のイベント処理
    this.overBody = function() {
        if (this.infoIcon) {
            this.infoIcon.start(true);
        }
        if (this.gameStart) {
            if (this.qIcon) {
                this.qIcon.start(true);
            }
        }
    }

    // マウスカーソルがbodyから出た時のイベント処理
    this.outBody = function() {
        if (this.infoIcon) {
            this.infoIcon.start(false);
        }
        if (this.qIcon) {
            this.qIcon.start(false);
        }
    }

    return this;
}

// widget表示時のイベント処理（何もしてない）
function onshow() {
}

// widget非表示時のイベント処理(pauseする)
function onhide() {
    app.pause = true;
    app.gameEnable = false;
    if (app.gameStart) {
        document.getElementById("saikaiButton").style.display = "inline";
        document.getElementById("ekisho").innerHTML = "*** PAUSE ***";
        buttonOnmouseout(document.getElementById("saikaiButton"));
    }
}

// アプリケーションロード時のイベント処理
function appLoad() {
    // Konquerorは何故かクラッシュしてしまうので、安全のために処理中断
    // TODO: 原因を突き止めよう！
    if (isKonqueror() ) {
        document.getElementById("ekisho").innerHTML
            = "Konquererには対応していません。";
        document.getElementById("startButton").style.display = "none";
        return false;
    }
    // 裏面の文字サイズを、Widgetの時に最適なサイズに変更
    if (window.widget) {
        widget.onshow = onshow;
        widget.onhide = onhide;
        document.getElementById("docArea").style.fontSize = "13px";
    }
    app.highscore = loadHighscore();
    app.loadXml();
    app.infoIcon
        = new OpacityIcon(document.getElementById("ibutton"), "app.infoIcon");
    app.qIcon
        = new OpacityIcon(document.getElementById("qbutton"), "app.qIcon");
}

// アルファベット＋数字の文字列を声調つきピンインに変換する
// これでホントに合っているのか？
function makePinString(str) {
    var seicho = str.substring(str.length - 1, str.length);
    if (! isNaN(seicho) ) {
        str = str.substring(0, str.length - 1);
        seicho = parseInt(seicho) - 1;
        if (str.indexOf("a") >= 0) {
            str = str.replace(/a/, "āáǎà".charAt(seicho) );
        } else if (str.indexOf("e") >= 0) {
            str = str.replace(/e/, "ēéěè".charAt(seicho) );
        } else if (str.indexOf("i") >= 0) {
            str = str.replace(/i/, "īíǐì".charAt(seicho) );
        } else if (str.indexOf("o") >= 0) {
            str = str.replace(/o/, "ōóǒò".charAt(seicho) );
        } else if (str.indexOf("u") >= 0) {
            str = str.replace(/u/, "ūúǔù".charAt(seicho) );
        } else if (str.indexOf("v") >= 0) {
            str = str.replace(/v/, "ǖǘǚǜ".charAt(seicho) );
        }
    }
    str = str.replace(/v/, "ü");
    return str;
}

// 裏面の説明文用XMLをロードする
function loaddoc() {
    app.hanziXml = xmlReader.xml;
    if (isSafari() ) {
        app.hanziXml = xmlReader.xml.responseXML;
    }
    app.hanziList = app.hanziXml.documentElement.getElementsByTagName("hanzi");
    var fname = "doc.xml";
    if (isKonqueror() ) {
        fname = "doc_utf16.xml";
    }
    xmlReader.readXml(fname, loadfinish);
}

// XMLロード完了時の処理
function loadfinish() {
    try {
        app.docxml = xmlReader.xml;
        if (isSafari() ) {
            app.docxml = xmlReader.xml.responseXML;
        }
        if (! app.docxml) {
            return;
        }
        app.docPageList
            = app.docxml.documentElement.getElementsByTagName("Page");
        document.forms.form0.onsubmit = app.submitKey;
        app.init();
    } catch (ex) {
        document.getElementById("subwin").innerHTML
          = "ファイル読み込み中にエラーが発生しました。" + ex;
    }
}

// ボタンの上にマウスカーソルが来た時のイベント処理
function buttonOnmouseover(e) {
    e.style.backgroundColor = "EEEEEE";
    e.style.color = "444444";
}

// ボタンの上からマウスカーソルがはなれた時のイベント処理
function buttonOnmouseout(e) {
    e.style.backgroundColor = "777777";
    e.style.color = "ffffff";
}

// iアイコン上にマウスカーソルが来た時のイベント処理
function infoover(id) {
    document.getElementById(id).style.display = "block";
}

// iアイコン上からマウスカーソルが離れた時のイベント処理
function infoout(id) {
    document.getElementById(id).style.display = "none";
}

// ハイスコアをPreference(Widget)またはCookie(ブラウザ)に保存する
function saveHighscore(score) {
    if (window.widget) {
        widget.setPreferenceForKey (score, createkey("highscore"));
    } else {
        setCookie("hayauchiHighScore", score);
    }
}

// ハイスコアをロードする
function loadHighscore() {
    var score = 0;
    if (window.widget) {
        score = widget.preferenceForKey (createkey("highscore"));
    } else {
           score = getCookie("hayauchiHighScore");
    }
    if ( (! score) || isNaN(score)) {
        score = 0;
    } else {
        score = parseInt(score);
    }
    return score;
}

// ハイスコア保存用のキーを作成する。
function createkey(key)
{
    return widget.identifier + "-" + key;
}

