var isDebug = isDebug || /localhost|jeff\.pokerwit\.com/.test(document.URL);
var isPlaying = /play-poker-hand/.test(document.location);

var log = new Log(isDebug ? Log.DEBUG : Log.NONE, Log.popupLogger);
var game;
var stage;

Event.observe(window, 'load', function() {
    if (isDebug && !isPlaying && $('header')) {
        $('header').down('img.logo').observe('click', function(e) {
            Event.stop(e);
            fillTestData();
        });
    }
    if (isPlaying) {
        updateQuestionAndEndingAction();
    }
});

var Action = Class.create({

    initialize: function(player, type, param) {
        this.type = type || '';
        this.player = player || '';
        this.param = param;
    },

    isAllIn : function () {
        return (this.type == 'raise_to' && this.param[2])
                || (this.type == 'bet' && this.param[1])
                || (this.type == 'call' && this.param[1]);
    },

    getAmount: function () {
        switch (this.type) {
            case 'raise_to':
                return this.param[1];
            case 'bet':
            case 'call':
                return this.param[0];
            case 'small_blind':
            case 'big_blind':
            case 'ante':
                return this.param;
            default:
                return null;
        }
    },

    toString : function() {
        var s = this.player + ": ";
        var p = this.param;
        switch (this.type) {
            case 'fold':
                s += 'folds';
                break;
            case 'check':
                s += 'checks';
                break;
            case 'raise_to':
                if (p[0]) {
                    s += 'raises ' + p[0] + ' to ' + p[1] + (p[2] ? ' and is all-in' : '');
                } else {
                    s += 'raises to ' + p[1] + (p[2] ? ' and is all-in' : '');
                }
                break;
            case 'bet':
                s += 'bets ' + p[0] + (p[1] ? ' and is all-in' : '');
                break;
            case 'call':
                s += 'calls';
                if (p[0]) {
                    s += ' ' + p[0] + (p[1] ? ' and is all-in' : '');
                }
                break;
            default:
                s += this.type + (p ? ' ' + p.toString() : '');

        }
        return s;
    }
});

var PokerGame = Class.create({

    initialize: function(json) {

        this.players = [];
        this.actions = [];
        this.potChipCnt = 0;

        this.title = 'A new poker hand';
        this.userId = 0;

        if (json) {
            this.loadJSON(json);
        }

    },

    addPlayer : function(name, chipCnt, origSeat) {
        var player = {
            name : name,
            chipCnt : parseFloat(chipCnt),
            origSeat : parseInt(origSeat)
        };
        this.players.push(player);
        player.seat = this.players.length;
        //log.debug("addPlayer: " + Object.toJSON(player));
        return player;
    },

    removePlayer : function(name) {
        var temp = [];
        this.players.each(function (p) {
            if (p.name != name) {
                temp.push(p);
                p.seat = temp.length;
            }
        });
        this.players = temp;
    },

    getPlayer : function(name) {
        if (!this._playerByName) {
            var playerByName = {};
            this.players.each(function (p) {
                playerByName[p.name] = p;
            });
            this._playerByName = playerByName;
        }
        return this._playerByName[name];
    },

    getSmallBlind : function() {
        var action = this.actions.find(function (a) {
            return a.type == 'small_blind';
        });
        if (action) {
            return action.param;
        } else {
            return '';
        }
    },

    getBigBlind : function() {
        var action = this.actions.find(function (a) {
            return a.type == 'big_blind';
        });
        if (action) {
            return action.param;
        } else {
            return '';
        }
    },

    getAnte : function() {
        var action = this.actions.find(function (a) {
            return a.type == 'ante';
        });
        if (action) {
            return action.param;
        } else {
            return '';
        }
    },

    getHoleCards : function() {
        var action = this.actions.find(function (a) {
            return a.type == 'dealt_to';
        });
        return action ? action.param : null;
    },

    getFlopCards : function() {
        var action = this.actions.find(function (a) {
            return a.type == 'flop';
        });
        return action ? action.param : ['', '', ''];
    },

    getTurnCard : function() {
        var action = this.actions.find(function (a) {
            return a.type == 'turn';
        });
        return action ? action.param : null;
    },

    getRiverCard : function() {
        var action = this.actions.find(function (a) {
            return a.type == 'river';
        });
        return action ? action.param : null;
    },

    getHero : function() {
        var player = this.players.find(function(p) {
            return p.isHero;
        });
        if (player == null) {
            var action = this.actions.find(function (a) {
                return a.type == 'dealt_to';
            });
            player = this.getPlayer(action.player);
            player.isHero = true;
        }
        return player;
    },

    getHeroSeat : function() {
        return this.getHero().seat;
    },

    getDealerSeat : function() {

        var player = this.players.find(function(p) {
            return p.origSeat == this.button;
        }.bind(this));

        if (!player) {
            this._adjustButton();
            player = this.players.find(function(p) {
                return p.origSeat == this.button;
            }.bind(this));
        }

        return player.seat;
    },

    _adjustButton : function() {
        // sometimes (rarely) the log file says the button is seat X - where seat X does not exist,
        // need to make button the next seat lower to X in that case
        var temp;
        for (var i=0; i<this.players.length; i++) {
            var p = this.players[i];
            if (p.origSeat <= this.button) {
                temp = p.origSeat;
            }

            if (p.origSeat >= this.button) {
                 break;
            }
        }
        this.button = temp;
    },

    setButton : function(button) {

        this.button = button;

    },

    addAction : function(playerName, type, param) {
        if (playerName) {
            var player = this.players.find(function(p) {
                return p.name == playerName;
            }.bind(this));
            if (!player) {
                throw ('Error parsing log: player not found: ' + playerName
                        + '\n' + 'Please check line: ' + this._lineNum
                        + '\n' + this._linesBeingParsed[this._lineNum]);
            }
        }
        var action = new Action(playerName, type, param);
        this.actions.push(action);
        log.debug("addAction: " + action.toString());
    },

    parse: function(log) {
        var lines = log.split("\n");
        this._linesBeingParsed = lines;
        try {
            this.parseTiltLog(lines) ||  this.parsePokerStar(lines);
        } catch (ex) {
            alert(ex);
        }
    },

    parseTiltLog : function(lines) {

        var stage = null;
        var isFirstLine = true;
        for (var lineNum = 0; lineNum < lines.length; lineNum++) {
            this._lineNum = lineNum;
            var line = lines[lineNum].strip();
            log.debug(lineNum + ": " + line);

            if (line == '') {
                continue;
            }
            var ar;

            /*
             Full Tilt Poker Game #13138418640: Table Stour (6 max) - $30/$60 - Limit Hold'em - 12:14:55 ET �C

             The $30/$60 are the blinds,
             ��Limit Hold��em�� means it��s a fixed limit game.
             So for cash games they will have ��$�� listed, which they don��t for tournaments.

             Overall �C I believe the information is very similar to how it appears on the PokerStars log.

             For tournaments as far as I can tell, they do not list the buy-in in the log.
             We��ll need to leave it empty and have the user fill it.
             */

            //Full Tilt Poker Game #6438398263: $12,500 Guarantee (48288302), Table 7 - 15/30 - No Limit Hold'em - 12:15:00 ET - 2008/05/16
            if (isFirstLine) {
                isFirstLine = false;
                ar = /^(Full Tilt Poker|FullTiltPoker) Game #(\d*):(.*) - (\$?\d*)\/(\$?\d*) (.*)/.exec(line);
                if (ar) {
                    log.debug(ar.inspect());
                    this.gameType = (ar[4].startsWith('$')) ? 'cash' : 'tournament';
                    this.buyInAmount = '';
                    this.limitType = /No Limit/i.test(ar[6]) ? 'none' : 'fixed';
                    log.debug(Object.toJSON(this));
                    continue;
                } else {
                    return false;
                }
            }
/*
*** HOLE CARDS ***
*** FLOP *** [9d 7h 9h]
*** FLOP *** [9d 7h 9h]
*** TURN *** [9d 7h 9h] [4c]
*** RIVER *** [9d 7h 9h 4c] [Qh]
*** SHOW DOWN ***
*** SUMMARY ***
*/
            ar = /\*{3} (.*) \*{3}(.*)/.exec(line);
            if (ar) {
                stage = ar[1];

                ar = /.*FLOP.*\[(..) (..) (..)\]/.exec(line);
                if (ar) {
                    this.addAction('', 'flop', [ar[1].toLowerCase(), ar[2].toLowerCase(), ar[3].toLowerCase()]);
                    continue;
                }

                ar = /.*TURN.*\[.. .. ..\] \[(..)\]/.exec(line);
                if (ar) {
                    this.addAction('', 'turn', ar[1].toLowerCase());
                    continue;
                }

                ar = /.*RIVER.*\[.. .. .. ..\] \[(..)\]/.exec(line);
                if (ar) {
                    this.addAction('', 'river', ar[1].toLowerCase());
                    continue;
                }

                continue;
            }

            if (null == stage) {
                //The button is in seat #2
                ar = /The button is in seat \#(\d)/.exec(line);
                if (ar) {
                    this.setButton(ar[1]);
                    log.debug("button:" + this.button);
                    continue;
                }

                //Seat 1: superroger47 (2,402)
                //Seat 5: usdjim (13,084), is sitting out
                ar = /Seat\s([0-9]+): (.*) \((.*)\)(.*)$/.exec(line);
                if (ar) {
                    var player = this.addPlayer(ar[2], ar[3].replace(',', ''), ar[1]);
                    log.debug(Object.toJSON(player));
                    continue;
                }
            }

            //TheMiltMan posts the small blind of 15
            //AmarilloSlimJr posts the big blind of 30

            ar = /(.*) posts the small blind of \$?(\d*(,\d{3})*)/.exec(line);
            if (ar) {
                this.addAction(ar[1], 'small_blind', parseFloat(ar[2].replace(',', '')));
                continue;
            }

            ar = /(.*) posts the big blind of \$?(\d*(,\d{3})*)/.exec(line);
            if (ar) {
                this.addAction(ar[1], 'big_blind', parseFloat(ar[2].replace(',', '')));
                continue;
            }
            //PocketJaKKd antes 25
            ar = /(.*) antes \$?(\d*(,\d{3})*)/.exec(line);
            if (ar) {
                this.addAction(ar[1], 'ante', parseFloat(ar[2].replace(',', '')));
                continue;
            }

            //Dealt to AmirSF [Ad Td]
            ar = /Dealt to (.*) \[(..) (..)\]/.exec(line);
            if (ar) {
                var player = ar[1];
                this.addAction(player, 'dealt_to', [ar[2].toLowerCase(), ar[3].toLowerCase()]);
                this.getPlayer(player).isHero = true;
                continue;
            }
            //TheMiltMan has 15 seconds left to act
            ar = /(.*) has (\d*) seconds left to act/.exec(line);
            if (ar) {
                // do nothing
                continue;
            }

            //Zpaceman folds
            if (ar = /(.*) folds/.exec(line)) {
                this.addAction(ar[1], 'fold');
                continue;
            }

            //TheMiltMan checks
            if (ar = /(.*) checks/.exec(line)) {
                this.addAction(ar[1], 'check');
                continue;
            }

            //TheMiltMan bets 300
            if (ar = /(.*) bets \$?(.*)( .*)?/.exec(line)) {
                this.addAction(ar[1], 'bet', [parseFloat(ar[2].replace(',', '')), /all in/.test(ar[3])]);
                continue;
            }

            //TheMiltMan calls 90
            if (ar = /(.*) calls \$?(.*)( .*)?/.exec(line)) {
                this.addAction(ar[1], 'call', [parseFloat(ar[2].replace(',', '')), /all in/.test(ar[3])]);
                continue;
            }
            //INOSPEAKAENGRIS raises to 105
            if (ar = /(.*) raises to \$?(.*)( .*)?/.exec(line)) {
                this.addAction(ar[1], 'raise_to', ['', parseFloat(ar[2].replace(',', '')), /all in/.test(ar[4])]);
                continue;
            }

            /*
             *** SHOW DOWN ***
             AmirSF shows [Ad Td] a pair of Nines
             TheMiltMan shows [8d 7d] two pair, Nines and Sevens
             */
            if (ar = /(.*) shows \[(..) (..)\]( .*)?/.exec(line)) {
                this.addAction(ar[1], 'show', [ar[2].toLowerCase(), ar[3].toLowerCase(), ar[4]]);
            }

            //Seat 7: bmw60 mucked [Jc 9h] - three of a kind, Aces
            //Seat 9: LITTLETEN (big blind) mucked [2c 7c] - a pair of Fives
            if (ar = /(.*) mucked \[(..) (..)\]( .*)?/.exec(line)) {
                var tmp = /(Seat .: )?([^(]*)( \(.*\) )?/.exec(ar[1]);
                var playerName = tmp[2].trim();
                this.addAction(playerName, 'show', [ar[2].toLowerCase(), ar[3].toLowerCase(), ar[4]]);

            }

            //TheMiltMan wins the pot (2,770) with two pair, Nines and Sevens
            if (/(.*) wins the pot/.test(line)) {
                this.addAction('', 'result', line);
                continue;
            }

            /*
             *** SUMMARY ***
             Duration 22s
             Total pot 442,776 | Rake 0
             Board: [6d 4c 7s Kc 2d]
             Seat 2: IAmDestroyer (button) showed [Jc Jh] and lost with a pair of Jacks
             Seat 6: cbails420 (small blind) folded before the Flop
             Seat 9: FTPmon3yMak3n (big blind) showed [As Kd] and won (442,776) with a pair of Kings
             */
            ar = /.*SUMMARY.*/.exec(line);
            if (ar) {
                // do nothing
            }

            // Seat 2: Thanks4CominOut is sitting out
            ar = /Seat\s([0-9]+): (.*) is sitting out/.exec(line);
            if (ar) {
                this.removePlayer(ar[2]);
                log.debug("remove player: " + ar[2]);
                continue;
            }

        }

        this._parseActions();
        return true;
    },

    parsePokerStar : function(lines) {

        var beforePlayers = true;

        var isFirstLine = true;
        for (var lineNum = 0; lineNum < lines.length; lineNum++) {
            this._lineNum = lineNum;
            var line = lines[lineNum].strip();
            log.debug(lineNum + ": " + line);
            if (line == '') {
                continue;
            }
            var ar;

            if (isFirstLine) {
                isFirstLine = false;
                if (/PokerStars (Game|Hand)/.test(line)) {
                    ar = /PokerStars Game #(\d*): Tournament #(\d*), \$(\d*\.?\d*)\+\$(\d*\.?\d*)(.*)/.exec(line);
                    if (ar) {
                        this.gameType = 'tournament';
                        this.buyInAmount = parseFloat(ar[3]) + parseFloat(ar[4]);
                    } else {
                        this.gameType = 'cash';
                        this.buyInAmount = '';
                    }
                    this.limitType = /No Limit/i.test(line) ? 'none' :
                                     /Pot Limit/i.test(line) ? 'pot' : 'fixed';
                    log.debug('gameType:' + this.gameType);
                    log.debug('buyInAmount:' + this.buyInAmount);
                    continue;
                } else {
                    return false;
                }
            }

            ar = /.*(\d) is the button/.exec(line);
            if (ar) {
                this.setButton(ar[1]);
                log.debug("button:" + this.button);
                continue;
            }

            ar = /Seat\s([0-9]+):\s(.*)\s\(\$?(\d*\.?\d*) in chips\)(.*)/.exec(line);
            if (ar) {
                if (! /out of hand/.test(ar[4])) {
                    this.addPlayer(ar[2], ar[3], ar[1]);
                    beforePlayers = false;
                }
                continue;
            }

            if (beforePlayers) {
                continue;
            }

            ar = /(.*): posts small blind\s\$?(\d*\.?\d*)/.exec(line);
            if (ar) {
                this.addAction(ar[1], 'small_blind', parseFloat(ar[2]));
                continue;
            }

            ar = /(.*): posts big blind\s\$?(\d*\.?\d*)/.exec(line);
            if (ar) {
                this.addAction(ar[1], 'big_blind', parseFloat(ar[2]));
                continue;
            }

            ar = /(.*): posts the ante\s\$?(\d*\.?\d*)/.exec(line);
            if (ar) {
                this.addAction(ar[1], 'ante', parseFloat(ar[2]));
                continue;
            }

            ar = /Dealt to (.*) \[(..)\s(..)\]/.exec(line);
            if (ar) {
                var player = ar[1];
                this.addAction(player, 'dealt_to', [ar[2].toLowerCase(), ar[3].toLowerCase()]);
                this.getPlayer(player).isHero = true;
                continue;
            }

            ar = /.*FLOP.*\[(..) (..) (..)\]/.exec(line);
            if (ar) {
                this.addAction('', 'flop', [ar[1].toLowerCase(), ar[2].toLowerCase(), ar[3].toLowerCase()]);
                continue;
            }

            ar = /.*TURN.*\[(..)\]/.exec(line);
            if (ar) {
                this.addAction('', 'turn', ar[1].toLowerCase());
                continue;
            }

            ar = /.*RIVER.*\[(..)\]/.exec(line);
            if (ar) {
                this.addAction('', 'river', ar[1].toLowerCase());
                continue;
            }

            ar = /.*SUMMARY.*/.exec(line);
            if (ar) {
                // do nothing
            }

            if (/(.*) collected \$?(\d*\.?\d*) from pot/.test(line)
                    || /(.*) doesn't show hand/.test(line)) {
                this.addAction('', 'result', line);
                continue;
            }

            if (ar = /(.*): folds/.exec(line)) {
                this.addAction(ar[1], 'fold');
                continue;
            }

            if (ar = /(.*): checks/.exec(line)) {
                this.addAction(ar[1], 'check');
                continue;
            }

            if (ar = /(.*): bets \$?(\d*\.?\d*)(.*)/.exec(line)) {
                this.addAction(ar[1], 'bet', [parseFloat(ar[2]), /all-in/.test(ar[3])]);
                continue;
            }

            if (ar = /(.*): calls \$?(\d*\.?\d*)(.*)/.exec(line)) {
                this.addAction(ar[1], 'call', [parseFloat(ar[2]), /all-in/.test(ar[3])]);
                continue;
            }

            if (ar = /(.*): raises \$?(\d*\.?\d*) to \$?(\d*\.?\d*)(.*)/.exec(line)) {
                this.addAction(ar[1], 'raise_to', [parseFloat(ar[2]), parseFloat(ar[3]), /all-in/.test(ar[4])]);
                continue;
            }

            if (ar = /(.*): shows \[(..) (..)\]\s\((.*)\)/.exec(line)) {
                this.addAction(ar[1], 'show', [ar[2].toLowerCase(), ar[3].toLowerCase(), ar[4]]);
            }

            // Seat 7: AmirSF (small blind) mucked [9c 8c]
            if (ar = /(.*) mucked \[(..) (..)\]( .*)?/.exec(line)) {
                var tmp = /(Seat .: )?([^(]*)( \(.*\) )?/.exec(ar[1]);
                var playerName = tmp[2].trim();
                this.addAction(playerName, 'show', [ar[2].toLowerCase(), ar[3].toLowerCase(), ar[4]]);
            }
        }

        this._parseActions();
        return true;
    },

    loadJSON : function (data) {
        Object.extend(this, data);
        for (var i = 0; i < this.actions.length; i++) {
            var a = this.actions[i];
            this.actions[i] = new Action(a.player, a.type, a.param);
        }
        this._parseActions();
    },

    /**
     * actions after hero's action in pre-flop lastBetOrRasiseActionstage until flop or result
     */
    getPreFlopActions : function() {
        var ret = [];
        var end = this._flopActionIdx || this._resultActionIdx || this.actions.length;
        if (end) {
            for (var i = end - 1; i > this._dealtToActionIdx; i--) {
                var action = this.actions[i];
                if (action.player == this._hero) {
                    break;
                }
                ret.push(action);
            }
        }
        return ret.reverse();
    },

    /**
     * actions after hero's action in flop stage until turn or result
     */
    getFlopActions : function() {
        var ret = [];
        var end = this._turnActionIdx || this._resultActionIdx || this.actions.length;
        if (end && this._flopActionIdx) {
            for (var i = end - 1; i > this._flopActionIdx; i--) {
                var action = this.actions[i];
                if (action.player == this._hero) {
                    break;
                }
                ret.push(action);
            }
        }
        return ret.reverse();
    },

    /**
     * actions after hero's action in turn stage until river or result
     */
    getTurnActions : function() {
        var ret = [];
        var end = this._riverActionIdx || this._resultActionIdx || this.actions.length;
        if (end && this._turnActionIdx) {
            for (var i = end - 1; i > this._turnActionIdx; i--) {
                var action = this.actions[i];
                if (action.player == this._hero) {
                    break;
                }
                ret.push(action);
            }
        }
        return ret.reverse();
    },

    /**
     * actions after hero's action in river stage until show or result
     */
    getRiverActions : function() {
        var ret = [];
        var end = this._showActionIdx || this._resultActionIdx || this.actions.length;
        if (end && this._riverActionIdx) {
            for (var i = end - 1; i > this._riverActionIdx; i--) {
                var action = this.actions[i];
                if (action.player == this._hero) {
                    break;
                }
                ret.push(action);
            }
        }
        return ret.reverse();
    },

    getHeroActionIdx : function(stage, questionNum) {
        try {
            return this._stageHeroActionIdx[stage][questionNum];
        } catch (e) {
            log.error(e);
            return -1;
        }
    },

    _parseActions : function() {
        var hero = this.players[this.getHeroSeat() - 1];
        var stage = null;
        var stageHeroActionIdx = {
            "pre-flop" : [],
            "flop"     : [],
            "turn"     : [],
            "river"    : []
        };

        this._heroActionIdx = [];
        for (var i = 0; i < this.actions.length; i++) {
            var action = this.actions[i];
            switch (action.type) {
                case 'big_blind' :
                case 'small_blind' :
                    // do nothing
                    break;
                case 'dealt_to' :
                    if (! this._dealtToActionIdx) { // just to deal with data error where dealt_to is duplicated
                        this._dealtToActionIdx = i;
                        this._hero = action.player;
                        stage = 'pre-flop';
                    }
                    break;
                case 'flop' :
                    this._flopActionIdx = i;
                    stage = 'flop';
                    break;
                case 'turn' :
                    this._turnActionIdx = i;
                    stage = 'turn';
                    break;
                case 'river' :
                    this._riverActionIdx = i;
                    stage = 'river';
                    break;
                case 'show' :
                    if (!this._showActionIdx) this._showActionIdx = i;
                    stage = null;
                    break;
                case 'result' :
                    if (!this._resultActionIdx) this._resultActionIdx = i;
                    stage = null;
                    break;
                case 'fold' :
                case 'bet' :
                case 'call' :
                case 'check' :
                case 'raise_to' :
                    if ((stage != null) && (action.player == hero.name)) {
                        stageHeroActionIdx[stage].push(i);
                    }
                    break;
                default:
            }
        }

        this._stageHeroActionIdx = stageHeroActionIdx;
    },

    _buildQuestions : function(startIdx, endIdx) {
        var questions = [];
        var actions = [];
        for (var i = startIdx; i < endIdx; i++) {
            var action = this.actions[i];
            if ( 'show' == action.type) {
                break;
            }
            if (action.player == this._hero) {
                var question = {
                    actions : actions,
                    summary : getActionsSummary(actions),
                    heroAction : action
                };
                questions.push(question);
                actions = [];
            } else {
                actions.push(action);
            }
        }
        return questions;
    },

    getStageBeginIndex : function(stage) {
        var idx;
        switch (stage) {
            case 'index':
                idx = 0;
                break;
            case 'pre-flop':
                idx = this._dealtToActionIdx;
                break;
            case 'flop':
                idx = this._flopActionIdx;
                break;
            case 'turn':
                idx = this._turnActionIdx;
                break;
            case 'river':
                idx = this._riverActionIdx;
                break;
            case 'conclusion':
                idx = this._resultActionIdx;
        }
        return idx;
    },

    getStageEndIndex : function(stage) {
        var idx;
        switch (stage) {
            case "index"      : return this._dealtToActionIdx || this._showActionIdx || this._resultActionIdx || this.actions.length;
            case "pre-flop"   : return this._flopActionIdx || this._showActionIdx || this._resultActionIdx || this.actions.length;
            case "flop"       : return this._turnActionIdx || this._showActionIdx || this._resultActionIdx || this.actions.length;
            case "turn"       : return this._riverActionIdx || this._showActionIdx || this._resultActionIdx || this.actions.length;
            case "river"      : return this._showActionIdx || this._resultActionIdx || this.actions.length;
            case "conclusion" : return this.actions.length;
        }
        return idx;
    },

    getActionsBeforeStage : function(stage) {
        var endIdx = this.getStageBeginIndex(stage);
        if (endIdx) {
            return this.actions.slice(0, endIdx);
        } else {
            return this.actions;
        }
    },

    actionsAfterStage: function(stage) {
        var idx = this.getStageEndIndex(stage);
        if (idx) {
            return this.actions.slice(idx, this.actions.length);
        } else {
            return [];
        }
    },

    getVillains : function() {
        return this.actions.findAll(function(a) {
            return (a.type == 'show') && (a.player != this._hero);
        }.bind(this));
    },

    getConclusion : function() {
        var s = '';
        this.actions.each(function(a) {
            if (a.type == 'result') {
                if (s != '') s += '\n';
                s += a.param;
            }
        }.bind(this));
        return s;
    },

    setStageFirstPlayer : function(stage) {
        this.setCurrentPlayerIdx(this.button - 1); // start from the dealer
        if (stage == 'pre-flop') {
            // skip small blind,
            if (this.players.length > 2) {
                this.nextPlayer();
            }
            // skip big blind
            this.nextPlayer();
        }
        return this.nextPlayer();
    },

    setCurrentPlayerIdx : function (idx) {
        this.currentPlayerIdx = idx;
        this.currentPlayerIdx = this.currentPlayerIdx % this.players.length;
    },

    currentPlayer : function () {
        return this.players[this.currentPlayerIdx];
    },

    nextPlayer : function () {
        this.setCurrentPlayerIdx(this.currentPlayerIdx + 1);
        return this.currentPlayer();
    },

    /**
     * calculate the call amount and raise xxx to amount.
     * in manaul input action detail, these are not in the input field, but can be calculated from previous
     * actions.
     */
    calcActionsAmount : function() {
        var playerChipCnt = {};
        var roundAmount = 0;
        var roundPlayerAmount = {};
        this.players.each(function(p) {
            playerChipCnt[p.name] = p.chipCnt;
            roundPlayerAmount[p.name] = 0;
        });
        for (var i = 0; i < this.actions.length; i++) {
            var action = this.actions[i];
            if (['flop', 'turn', 'river'].include(action.type)) {
                roundAmount = 0;
                this.players.each(function(p) {
                    roundPlayerAmount[p.name] = 0;
                });
            }

            if (!['ante', 'big_blind', 'small_blind', 'bet', 'raise_to', 'call'/*, 'fold', 'check'*/].include(action.type)) {
                continue;
            }
            var amount;
            if (action.isAllIn()) {
                amount = playerChipCnt[action.player];
                switch (action.type) {
                    case 'bet':
                    case 'call':
                        action.param[0] = amount;
                        break;
                    case 'raise_to':
                        action.param[1] = roundPlayerAmount[action.player] + amount;
                        action.param[0] = action.param[1] - roundAmount;
                        break;
                }
            } else {
                switch (action.type) {
                    case 'small_blind':
                    case 'big_blind':
                    case 'ante':
                        amount = parseFloat(action.param);
                        if (amount > playerChipCnt[action.player]) {
                            amount = playerChipCnt[action.player];
                        }
                        action.param = amount;
                        break;
                    case 'bet':
                        amount = parseFloat(action.param[0]);
                        if (amount >= playerChipCnt[action.player]) {
                            amount = playerChipCnt[action.player];
                            action.param[1] = true;
                        }
                        action.param[0] = amount;
                        break;
                    case 'raise_to':
                        amount = action.param[1] - roundPlayerAmount[action.player];
                        if (amount >= playerChipCnt[action.player]) {
                            amount = playerChipCnt[action.player];
                            action.param[1] = roundPlayerAmount[action.player] + amount;
                            action.param[2] = true;
                        }
                        action.param[0] = action.param[1] - roundAmount;
                        break;
                    case 'call':
                        amount = roundAmount - roundPlayerAmount[action.player];
                        if (amount >= playerChipCnt[action.player]) {
                            amount = playerChipCnt[action.player];
                            action.param[1] = true;
                        }
                        action.param[0] = amount;
                        break;
                }
            }
            amount = parseFloat(amount);
            if ('ante' != action.type) {
                roundPlayerAmount[action.player] += amount;
            }
            playerChipCnt[action.player] -= amount;
            roundAmount = Math.max(roundAmount, roundPlayerAmount[action.player]);
        }
    },

    replayActionsTo : function(actionIdx) {
        log.debug('Replaying actions: *** ' + actionIdx);
        debugLog(this);
        var playerChipCnt = {};
        var playerFolded = {};
        var roundPlayerAmount = {};
        var roundAmount = 0;
        var potSize = 0;
        this.players.each(function(p) {
            playerChipCnt[p.name] = p.chipCnt;
            roundPlayerAmount[p.name] = 0;
        });
        for (var i = 0; i < actionIdx; i++) {
            var action = this.actions[i];
            if (['flop', 'turn', 'river'].include(action.type)) {
                roundAmount = 0;
                this.players.each(function(p) {
                    roundPlayerAmount[p.name] = 0;
                });
            }

            if ('fold' == action.type) {
                playerFolded[action.player] = true;
                continue;
            }
            if (!['ante', 'big_blind', 'small_blind', 'bet', 'raise_to', 'call'].include(action.type)) {
                continue;
            }
            var amount = null;
            switch (action.type) {
                case 'small_blind':
                case 'big_blind':
                case 'ante':
                    amount = action.param;
                    break;
                case 'bet':
                    amount = action.param[0];
                    break;
                case 'raise_to':
                    amount = action.param[1] - roundPlayerAmount[action.player];
                    break;
                case 'call':
                    amount = roundAmount - roundPlayerAmount[action.player];
                    break;
            }
            amount = parseFloat(amount);
            potSize += amount;
            if ('ante' != action.type) {
                roundPlayerAmount[action.player] += amount;
            }
            playerChipCnt[action.player] -= amount;
            roundAmount = Math.max(roundAmount, roundPlayerAmount[action.player]);

            if (playerChipCnt[action.player] < 0) { // handle all-in but not call the full amount
                var diff = - playerChipCnt[action.player];
                amount -= diff;
                potSize -= diff;
                playerChipCnt[action.player] += diff;

                /*
                 this.players.each(function(p) {
                 // handling return uncalled amount
                 if (roundPlayerAmount[p.name] >= roundAmount) {
                 potSize -= diff;
                 roundPlayerAmount[p.name] -= diff;
                 playerChipCnt[p.name] += diff;
                 }
                 ;
                 });
                 roundAmount = roundAmount - diff;
                 */
            }
        }
        var ret = {
            potSize : potSize,
            playerChipCnt : playerChipCnt,
            playerFolded : playerFolded
        };
        log.debug(Object.toJSON(ret));
        return ret;
    },

    getStageQuestions : function(stage) {
        var startIdx = this.getStageBeginIndex(stage) + 1;
        var endIdx = this.getStageEndIndex(stage);
        var questions = [];
        if (startIdx && endIdx) {
            questions = this._buildQuestions(startIdx, endIdx);
        }
        log.debug('getStageQuestions: ' + stage + ", start:" + startIdx + ', end:' + endIdx + ", quenstion num:" + questions.length);
        return questions;
    },

    getStageEndingActions: function(stage) {
        switch (stage) {
            case 'pre-flop':
                return this.getPreFlopActions();
            case 'flop':
                return this.getFlopActions();
            case 'river':
                return this.getRiverActions();
            case 'turn':
                return this.getTurnActions();
        }
    },

    toString : function() {
        var tmp = {};
        for (var x in this) {
            if (! x.startsWith('_')) {
                tmp[x] = this[x];
            }
        }
        return Object.toJSON(tmp);
    }

});

/**
 *
 * @param actions a list of actions
 * @param isEnding is it ending action of flop, turn etc. ?
 */
function getActionsSummary(actions, isEnding) {
    /*
     if (actions.length == 0 && !isEnding) {
     return 'I am first to act';
     }
     */
    var s = '';
    var foldCount = 0;
    actions.each(function(a) {
        if (a.type == 'fold') {
            foldCount ++;
        }
    });
    actions.each(function(a) {
        if (a.type != 'fold' || foldCount <= 1) {
            s += a.toString() + "\n";
        }
    });
    if (foldCount > 1) {
        if (foldCount == actions.length) {
            s = "everyone folds.\n";
        } else {
            s += "everyone else folds.\n";
        }
    }
    /*
     if (!isEnding) {
     s += "action is on me.\n";
     }
     */
    if (s.endsWith('\n')) s = s.substring(0, s.length - 1);
    return s;
}

function importPokerLog() {
    var game = new PokerGame();
    game.parse($('poker_log').value);
    if (game.players.length > 0) {
        var form = $('poker_log').form;
        game['addPokerHand'] = toAddPokerHandSessionForms(game);
        $('json').value = game.toString();
        form.submit();
    } else {
        alert('Invalid format.');
    }
}

function toAddPokerHandSessionForms(game) {
    var addPokerHand = {
        'visited_pages' : {}
    };

    // 1. index
    addPokerHand['index'] = {
        'game_type'         : game.gameType,
        'title'             : $F('title'),
        'buy_in_amount'     : game.buyInAmount,
        'big_blind'         : game.getBigBlind(),
        'small_blind'       : game.getSmallBlind(),
        'ante'              : game.getAnte(),
        'num_seats'         : game.players.length,
        'hero_seat'         : game.getHeroSeat(),
        'dealer_seat'       : game.getDealerSeat()
    };

    addPokerHand['index']['limit_type'] = game.limitType;

    game.players.each(function(p, i) {
        addPokerHand['index']['seat_' + (i + 1) + '_nickname'] = p.name;
        addPokerHand['index']['seat_' + (i + 1) + '_chips'] = p.chipCnt;
    });
    addPokerHand['visited_pages']['index'] = true;

    // 2. pre-flop
    var cards = game.getHoleCards();
    var questions = game.getStageQuestions('pre-flop');
    addPokerHand['visited_pages']['pre-flop'] = true;
    addPokerHand['pre-flop'] = {
        'card_1' : cards[0],
        'card_2' : cards[1],
        'num_questions' : questions.length
    };
    questions.each(function (question, i) {
        var heroAction = question.heroAction;
        addPokerHand['pre-flop']['question_' + (i + 1)] = ''; //question.summary;
        addPokerHand['pre-flop']['question_' + (i + 1) + '_num_answer_choices'] = 4;

        for (var j = 1; j <= 5; j++) {
            var choice = 'question_' + (i + 1) + '_answer_choice_' + j;
            addPokerHand['pre-flop'][choice] = heroAction.type;
            if (['raise_to', 'bet'].include(heroAction.type)) {
                var allIn = heroAction.isAllIn();
                addPokerHand['pre-flop'][choice + '_all_in'] = allIn;
                if (allIn) {
                    addPokerHand['pre-flop'][choice + '_amount'] = '';
                } else {
                    addPokerHand['pre-flop'][choice + '_amount'] = heroAction.getAmount();
                }
            }
        }
    });
    addPokerHand['pre-flop']['ending_action'] = ''; //getActionsSummary(game.getPreFlopActions(), true);

    // 3. flop
    cards = game.getFlopCards();
    questions = game.getStageQuestions('flop');
    addPokerHand['visited_pages']['flop'] = questions.length > 0;
    addPokerHand['flop_submitted'] = questions.length > 0;
    addPokerHand['flop'] = {
        'card_1' : cards[0],
        'card_2' : cards[1],
        'card_3' : cards[2],
        'num_questions' : questions.length
    };
    questions.each(function (question, i) {
        var heroAction = question.heroAction;
        addPokerHand['flop']['question_' + (i + 1)] = ''; //question.summary;
        addPokerHand['flop']['question_' + (i + 1) + '_num_answer_choices'] = 4;

        for (var j = 1; j <= 5; j++) {
            var choice = 'question_' + (i + 1) + '_answer_choice_' + j;
            addPokerHand['flop'][choice] = heroAction.type;
            if (['raise_to', 'bet'].include(heroAction.type)) {
                var allIn = heroAction.isAllIn();
                addPokerHand['flop'][choice + '_all_in'] = allIn;
                if (allIn) {
                    addPokerHand['flop'][choice + '_amount'] = '';
                } else {
                    addPokerHand['flop'][choice + '_amount'] = heroAction.getAmount();
                }
            }
        }
    });
    addPokerHand['flop']['ending_action'] = ''; //getActionsSummary(game.getFlopActions(), true);

    // 4. turn
    questions = game.getStageQuestions('turn');
    addPokerHand['visited_pages']['turn'] = questions.length > 0;
    addPokerHand['turn_submitted'] = questions.length > 0;
    addPokerHand['turn'] = {
        'card' : game.getTurnCard(),
        'num_questions' : questions.length
    };
    questions.each(function (question, i) {
        var heroAction = question.heroAction;
        addPokerHand['turn']['question_' + (i + 1)] = ''; //question.summary;
        addPokerHand['turn']['question_' + (i + 1) + '_num_answer_choices'] = 4;

        for (var j = 1; j <= 5; j++) {
            var choice = 'question_' + (i + 1) + '_answer_choice_' + j;
            addPokerHand['turn'][choice] = heroAction.type;
            if (['raise_to', 'bet'].include(heroAction.type)) {
                var allIn = heroAction.isAllIn();
                addPokerHand['turn'][choice + '_all_in'] = allIn;
                if (allIn) {
                    addPokerHand['turn'][choice + '_amount'] = '';
                } else {
                    addPokerHand['turn'][choice + '_amount'] = heroAction.getAmount();
                }
            }
        }
    });
    addPokerHand['turn']['ending_action'] = ''; //getActionsSummary(game.getTurnActions(), true);

    // 5. river
    questions = game.getStageQuestions('river');
    addPokerHand['visited_pages']['river'] = questions.length > 0;
    addPokerHand['river_submitted'] = questions.length > 0;
    addPokerHand['river'] = {
        'card' : game.getRiverCard(),
        'num_questions' : questions.length
    };

    questions.each(function (question, i) {
        var heroAction = question.heroAction;
        addPokerHand['river']['question_' + (i + 1)] = ''; //question.summary;
        addPokerHand['river']['question_' + (i + 1) + '_num_answer_choices'] = 4;

        for (var j = 1; j <= 5; j++) {
            var choice = 'question_' + (i + 1) + '_answer_choice_' + j;
            addPokerHand['river'][choice] = heroAction.type;
            if (['raise_to', 'bet'].include(heroAction.type)) {
                var allIn = heroAction.isAllIn();
                addPokerHand['river'][choice + '_all_in'] = allIn;
                if (allIn) {
                    addPokerHand['river'][choice + '_amount'] = '';
                } else {
                    addPokerHand['river'][choice + '_amount'] = heroAction.getAmount();
                }
            }
        }
    });
    addPokerHand['river']['ending_action'] = ''; //getActionsSummary(game.getRiverActions(), true);

    // 6. conclusion
    cards = game.getFlopCards();
    addPokerHand['conclusion'] = {
        'flop_card_1' : cards[0],
        'flop_card_2' : cards[1],
        'flop_card_3' : cards[2],
        'turn_card' : game.getTurnCard(),
        'river_card' : game.getRiverCard(),
        'conclusion' : game.getConclusion()
    };
    addPokerHand['visited_pages']['conclusion'] = true;
    var showActions = game.getVillains();
    addPokerHand['conclusion']['show_villains'] = showActions.length > 0;
    addPokerHand['conclusion']['num_villains'] = showActions.length;
    showActions.each(function(a, i) {
        addPokerHand['conclusion']['villain_' + (i + 1) + '_seat'] = game.getPlayer(a.player).seat;
        addPokerHand['conclusion']['villain_' + (i + 1) + '_card_1'] = a.param[0];
        addPokerHand['conclusion']['villain_' + (i + 1) + '_card_2'] = a.param[1];
    });

    addPokerHand['furthest_action'] = 5;
    return addPokerHand;
}

var Table = Class.create({

    initialize: function(game) {
        this.game = game;
        this.reset();
    },

    reset : function() {
        this.potChipCnt = 0;
        this.seatChipCnt = [];
        this.folded = [];
        this.seatChipCntOrig = [];
        this.game.players.each(function (p) {
            this.seatChipCntOrig[p.seat] = p.chipCnt;
            this.seatChipCnt[p.seat] = p.chipCnt;
            this.folded     [p.seat] = false;
        }.bind(this));
    },

    replayToQuestion : function(stage, questionNum) {
        log.debug('replayToQuestion: ' + stage + ', question: ' + questionNum);
        this.reset();
        var actionIdx = this.game.getHeroActionIdx(stage, questionNum);
        this._replayToIndex(actionIdx);
    },

    replayToStageEnd : function(stage) {
        this.reset();
        var actionIdx = this.game.getStageEndIndex(stage);
        this._replayToIndex(actionIdx);
    },

    replayToResult : function() {
        this._replayToIndex(this.game._resultActionIdx || this.game.actions.length);
    },

    replayToStart: function() {
        this._replayToIndex(this.game.getStageEndIndex('index'));
    },

    _replayToIndex : function(actionIdx) {
        var result = this.game.replayActionsTo(actionIdx);
        this.potChipCnt = result.potSize;
        this.game.players.each(function (p) {
            this.seatChipCnt[p.seat] = result.playerChipCnt[p.name];
            this.folded     [p.seat] = result.playerFolded[p.name];
        }.bind(this));
    },

    toString : function() {
        var s = 'pot: ' + this.potChipCnt;
        for (var i = 1; i < this.seatChipCnt.length; i++) {
            s += "; ";
            s += 'seat ' + i + ": " + this.seatChipCnt[i];
            if (this.folded[i]) {
                s += '(folded)';
            }
        }
        return s;
    }
});

function updateTableAtQuestion(game, stage, questionNum) {

    if (!game) return;
    var table = new Table(game);

    if (stage == 'conclusion') {
        table.replayToResult();
    } else if (stage == 'index') {
        table.replayToStart();
    } else {
        table.replayToQuestion(stage, questionNum - 1);
    }
    updateView(table);
    log.debug('updateTableAtQuestion:' + stage + ', ' + questionNum);
}

function updateTableToStageEnd(game, stage) {

    if (!game) return;
    var table = new Table(game);

    table.replayToStageEnd(stage);
    updateView(table);
    log.debug('updateTableToStageEnd:' + stage);
}

function updateTableToStageBegin(game, stage) {

    var prev = prevStage(stage);
    if (prev) {
        updateTableToStageEnd(game, prev);
    }
}

function prevStage(stage) {

    var stages = ['index', 'pre-flop', 'flop', 'turn', 'river', 'conclusion'];
    var n = stages.indexOf(stage);
    if (n > 0) {
        return stages[n - 1];
    } else {
        return null;
    }
}

function nextStage(stage) {

    var stages = ['index', 'pre-flop', 'flop', 'turn', 'river', 'conclusion'];
    var n = stages.indexOf(stage);
    if (n >= 0 && n + 1 < stages.length) {
        return stages[n + 1];
    } else {
        return null;
    }
}
function updateView(table) {
    var pokerTable = $$('.poker_table')[0];

    function renderAmt(amt) {
        if ('cash' == table.game.gameType) {
            return '$' + addCommas(parseFloat(amt).toFixed(2));
        }
        else {
            return addCommas(parseFloat(amt));
        }
    }

    for (var i = 1; i < table.seatChipCnt.length; i++) {
        var seatEle = pokerTable.select('.seat_seat_' + i)[0];
        seatEle.select('.chip_count')[0].update(
                renderAmt(table.seatChipCnt[i]) +
                (isDebug ? ' (' + renderAmt(table.seatChipCntOrig[i]) + ')' : '')
                );
        if (table.folded[i]) {
            seatEle.addClassName('folded');
        }
    }
    pokerTable.select('.pot_chip_count')[0].update('Pot: ' + renderAmt(table.potChipCnt));
}

function addCommas(nStr) {
    nStr += '';
    var x = nStr.split('.');
    var x1 = x[0];
    var x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    return x1 + x2;
}
/**
 * called from answer question ajax onSuccess, game and stage are given in .phtml file
 * @param response
 */
function updateTable(response) {
    if (response.current_question_number) {
        updateTableAtQuestion(game, stage, response.current_question_number);
    } else {
        updateTableToStageEnd(game, stage);
    }
    updateQuestionAndEndingAction();
}

function readSetupForm(sessionForm, game) {
    var form = sessionForm['index'];

    game.gameType = form['game_type'];
    game.title = form['title'];
    game.limitType = form['limitType'];
    game.button = form['dealer_seat'];

    var num_seats = form['num_seats'];
    for (var i = 1; i <= num_seats; i++) {
        var name = form['seat_' + i + '_nickname'] ||
                   (i == form['hero_seat'] ? 'Hero' : ('Seat ' + i));
        var chipCnt = form['seat_' + i + '_chips'];
        game.addPlayer(name, chipCnt, i);
    }
    var hero = game.players[form['hero_seat'] - 1];
    hero.isHero = true;

    if (form['ante']) {
        game.players.each(function(player) {
            game.addAction(player.name, 'ante', form['ante']);
        });
    }

    // start from dealer
    game.setCurrentPlayerIdx(game.button - 1);
    var player = game.currentPlayer();

    // then small_blind
    if (game.players.length > 2) {
        player = game.nextPlayer();
    }
    if (form['small_blind']) {
        game.addAction(player.name, 'small_blind', form['small_blind']);
    }
    player = game.nextPlayer();
    if (form['big_blind']) {
        game.addAction(player.name, 'big_blind', form['big_blind']);
    }

    game._parseActions();
}

function insertActionInput(container, player) {
    //log.debug("actionInput: " + container.id +  ' - ' + player.name);
    var actionInputTemplate =
            '<dl class="player_action" id="{questionId}_{seat}">' +
            '    <dt><span class="player_label">{player}</span><label><span class="required">*</span></label></dt>' +
            '    <dd>' +
            '        <select class="action_type answer_choice">' +
            '            <option value="" label="">- select -</option>' +
            '            <option value="fold" label="Fold">Fold</option>' +
            '            <option value="check" label="Check">Check</option>' +
            '            <option value="call" label="Call">Call</option>' +
            '            <option value="bet" label="Bet">Bet</option>' +
            '            <option value="raise_to" label="Raise To">Raise To</option>' +
            '        </select>' +
            '        <span class="bet_amount_container" style="display:none;">amount' +
            '            <input type="text" value=""' +
            '                   class="amount int max_length_6"/>' +
            '            <label class="all_in checkbox">' +
            '                <input type="checkbox"/> All In</label>' +
            '         </span>' +
            '    </dd>' +
            '</dl>';
    if (! $(container).down('dl')) {
        var txt = container.id == 'ending_action_container' ?
                  'Describe how the ' + stage + ' action concluded.' :
                  'Explain how the round has played out so far.';
        container.innerHTML +=
        '<dl class="">' +
        '    <dt><label class="player_label"></label></dt>' +
        '    <dd>' +
        '        <span>' + txt +
        '        </span>' +
        '    </dd>' +
        '</dl>';

    }
    container.innerHTML += actionInputTemplate
            .replace('{questionId}', container.id)
            .replace('{seat}', player.seat)
            .replace('{player}', player.name);
}

function sessionForm2PokerGame(sessionForm, game) {
    if (sessionForm && sessionForm['index'] && sessionForm['index']['json']) {
        game.loadJSON(sessionForm['index']['json'].evalJSON());
        if (!game.manualEntered) {
            updateTableAtQuestion(game, stage, 1);
            updateQuestionAndEndingAction();

            var questions = game.getStageQuestions(nextStage(stage));
            if (questions.length == 0) {
                var continueBtn = $('submit_continue');
                if (continueBtn) continueBtn.hide();
            } else {
                var concludeBtn = $('submit_conclusion');
                if (concludeBtn) concludeBtn.hide();
            }
            return;
        }
    } else {
        readSetupForm(sessionForm, game);
        game.manualEntered = true;
    }

    if (game.manualEntered) {
        continueBtn = $('submit_continue');
        if (continueBtn) {
            continueBtn.up().down('.note').update('If there is additional action in the hand then');
        }
        concludeBtn = $('submit_conclusion');
        if (concludeBtn) {
            concludeBtn.up().down('.note').update('Otherwise');
        }
    }

    updateTableToStageBegin(game, stage);

    $$('form')[0].observe('submit', formSubmitCheckActions);

    debugLog(game);
    var actionsBeforeStage = game.getActionsBeforeStage(stage);
    log.debug('actionsBeforeStage:<br/>' +
              actionsBeforeStage
                      .map(function(a) {
                  return a.toString();
              })
                      .join('<br>'));
    if (stage == 'conclusion') {
        return;
    }

    var player = game.setStageFirstPlayer(stage);
    for (var questionIdx = 1; questionIdx <= 5; questionIdx++) {
        while (!player.isHero) {
            insertActionInput($('question_action_container_' + questionIdx), player);
            player = game.nextPlayer();
        }
        player = game.nextPlayer();
    }

    while (!player.isHero) {
        insertActionInput($('ending_action_container'), player);
        player = game.nextPlayer();
    }

    var folded = {};
    actionsBeforeStage.each(function (act) {
        if (act.type == 'fold') {
            folded[act.player] = true;
        }
    });
    $$('.player_action').each(function (actionInput) {
        var player = (actionInput).down('.player_label').innerHTML;
        if (folded[player]) {
            (actionInput).addClassName('folded');
        }
    });
    $$('.question_container select, .question_container input').each(function (input) {
        input.observe('change', actionChanged);
    });

    fillActionsForStage(game, stage, false);
    readActionInputs();
}

function actionChanged(e) {
    handleActionChanged(Event.element(e));
}

function handleActionChanged(input) {
    if (input.hasClassName('action_type')) {
        var value = $F(input);
        var span = input.next('span.bet_amount_container');

        if ('bet' === value || 'raise_to' === value) {
            span.show();
        } else {
            span.hide();
        }
    }
    readActionInputs();
}

function readActionInput(actionInput) {
    var actionType = actionInput.down('.action_type').value;
    var player = actionInput.down('.player_label').innerHTML;
    var amount = actionInput.down('.amount').value;
    var allIn = actionInput.down('.all_in input').checked;
    amount = parseFloat(amount.replace(',', ''));
    return newAction(player, actionType, amount, allIn);
}

function readActionInputs() {
    var hero = game.getHero();
    var actions = [];
    if (stage == 'pre-flop') {
        actions.push(new Action(hero.name, 'dealt_to', [$F('card_1'), $F('card_2')]));
    } else if (stage == 'flop') {
        actions.push(new Action('', 'flop', [$F('card_1'), $F('card_2'), $F('card_3')]));
    } else if (stage == 'turn') {
        actions.push(new Action('', 'turn', $F('card')));
    } else if (stage == 'river') {
        actions.push(new Action('', 'river', $F('card')));
    } else if (stage == 'conclusion') {
        if ($('flop_card_1')) {
            actions.push(new Action('', 'flop', [$F('flop_card_1'), $F('flop_card_2'), $F('flop_card_3')]));
        }
        if ($('turn_card')) {
            actions.push(new Action('', 'turn', $F('turn_card')));
        }
        if ($('river_card')) {
            actions.push(new Action('', 'river', $F('river_card')));
        }
        for (i = 1; i <= $F('num_villains'); i++) {
            actions.push(new Action(game.players[$F('villain_' + i + '_seat') - 1].name,
                    'show', [$F('villain_' + i + '_card_1'), $F('villain_' + i + '_card_2')]));
        }
    }

    if (stage == 'conclusion') {
        return actions;
    }
    var folded = {};
    var all_in = {};
    var actionsBeforeStage = game.getActionsBeforeStage(stage);
    actionsBeforeStage.each(function (action) {
        if (action.type == 'fold') {
            folded[action.player] = true;
        }
        if (action.isAllIn()) {
            all_in[action.player] = true;
        }
    });
    var num = $F('num_questions');
    for (var idx = 1; idx <= num; idx++) {
        var container = $('question_action_container_' + idx);
        container.select('.player_action').each(function (actionInput) {
            var action = readActionInput(actionInput);
            if (folded[action.player]) {
                actionInput.addClassName('folded');
            } else {
                actionInput.removeClassName('folded');
            }
            if (action.type == 'fold') {
                folded[action.player] = true;
            }
            if (all_in[action.player]) {
                actionInput.addClassName('all_in');
            } else {
                actionInput.removeClassName('all_in');
            }
            if (action.isAllIn()) {
                all_in[action.player] = true;
            }
            action.ui = actionInput;
            actions.push(action);
        });
        var choiceIdx = $('question_' + idx + '_hero_answer').value; // 1,2,3,4
        var choice = 'question_' + idx + '_answer_choice_' + choiceIdx;
        var actionType = $F(choice);
        var span = $(choice).next('span.bet_amount_container');
        var allIn = span.down('input[type=checkbox]').checked;
        var amount = span.down('input[type=text]').value;
        /*
         var allIn = span.down('.answer_choice_all_in').checked;
         var amount = span.down('.answer_choice_amount').value;
         */
        var action = newAction(hero.name, actionType, amount, allIn);
        actions.push(action);
    }
    var lastBetOrRasiseAction = null;
    for (var i = actions.length - 1; i >= 0; i--) {
        action = actions[i];
        if (['bet', 'raise_to'].include(action.type)) {
            lastBetOrRasiseAction = action;
            break;
        }
    }
    var lastPlayer = lastBetOrRasiseAction != null ?
                     lastBetOrRasiseAction.player :
                     game.setStageFirstPlayer(stage).name;

    var theRestActionInputsNotNeeded = false;
    log.debug('lastBetOrRasiseAction: ' + Object.toJSON(lastBetOrRasiseAction));
    $('ending_action_container').select('.player_action').each(function (actionInput) {
        var action = readActionInput(actionInput);
        action.ui = actionInput;
        actions.push(action);
        if (action.player == lastPlayer) {
            theRestActionInputsNotNeeded = true;
        }
        if (folded[action.player]) {
            actionInput.addClassName('folded');
        } else {
            actionInput.removeClassName('folded');
        }
        if (theRestActionInputsNotNeeded) {
            actionInput.hide();
        } else {
            actionInput.show();
        }
        if (all_in[action.player]) {
            actionInput.addClassName('all_in');
        } else {
            actionInput.removeClassName('all_in');
        }
        if (action.isAllIn()) {
            all_in[action.player] = true;
        }
    });
    return actions;
}

function debugLog(game) {
    var s = [];
    s.push('----beging game -------');
    game.players.each(function(p) {
        s.push(Object.toJSON(p));
    });
    s.push('---');
    game.actions.each(function(a, i) {
        s.push('' + i + ' : ' + a.toString());
    });
    s.push('--- end game ------');
    log.debug(s.join('</br>'));
}

function formSubmitCheckActions(e) {
    try {
        log.debug('== formSubmitCheckActions ==');
        var invalidActions = [];
        var actions = readActionInputs();
        var actionsBeforeStage = game.getActionsBeforeStage(stage);
        var actionsAfterStage = game.actionsAfterStage(stage);
        //log.debug('== formSubmitCheckActions ==' + actions.map(function(action) {return action.toString();}).join("<br>"));
        for (var i = 0; i < actions.length; i++) {
            var act = actions[i];
            if (act.ui && !act.type
                    && act.ui.visible()
                    && !act.ui.hasClassName('all_in') && !act.ui.hasClassName('folded')
                    ) {
                invalidActions.push(act);
            }
        }
        if (invalidActions.length > 0) {
            var invalidAction = invalidActions[0];
            alert("Please fill in all the player actions");
            invalidAction.ui.down('.action_type').focus();
            Event.stop(e);
            return;
        }
        actions = actions.findAll(function (act) {
            return act.type;
        });
        game.actions = actionsBeforeStage.concat(actions).concat(actionsAfterStage);
        game.manualEntered = true;
        game.calcActionsAmount();
        $('manual_entered_game_json').value = game.toString();
        debugLog(game);

    } catch(ex) {
        // todo
        // fix IE bug access denied
        Event.stop(e);
        log.debug(Object.toJSON(ex));
        throw (ex);
    }
}

function newAction(player, type, amount, allIn) {
    var param = [];
    switch (type) {
        case 'raise_to':
            param = [null, amount, allIn];
            break;
        case 'bet':
            param = [amount, allIn];
            break;
        case 'call':
            param = [null, allIn];
    }
    return new Action(player, type, param);
}

function fillTestData() {
    var g = new PokerGame(test);
    var test;
    new Ajax.Request('/testData.php', {method:'get', asynchronous:false, onSuccess:function(transport) {
        test = transport.responseText.trim();
    }});
    if (!test) {
        return;
    }
    try {
        g.loadJSON(test.evalJSON());
    } catch(e) {
        g.parse(test);
    }
    log.debug('---- test data ----');
    debugLog(g);
    if (!stage || stage == 'index') {
        $('title').value = g.title;
        $('buy_in_amount').value = g.buyInAmount;
        $('big_blind').value = g.getBigBlind();
        $('small_blind').value = g.getSmallBlind();
        $('ante').value = g.getAnte();
        $('num_seats').value = g.players.length;
        updateNumSeats();
        for (var i = 1; i <= g.players.length; i++) {
            $('seat_' + i + '_nickname').value = g.players[i - 1].name;
            $('seat_' + i + '_chips').value = g.players[i - 1].chipCnt;
        }
        $('seat_' + g.getHeroSeat() + '_droppable').appendChild($('poker_chip_hero'));
        $('seat_' + g.getDealerSeat() + '_droppable').appendChild($('poker_chip_dealer'));
        poker_chip_onDrop($('poker_chip_hero'));
        poker_chip_onDrop($('poker_chip_dealer'));
        /*
         $('hero_seat').value = g.getHeroSeat();
         $('dealer_seat').value = g.getDealerSeat();
         */
    } else if (stage == 'pre-flop') {
        var cards = g.getHoleCards();
        $('card_1').value = cards[0];
        $('card_2').value = cards[1];
    } else if (stage == 'flop') {
        cards = g.getFlopCards();
        $('card_1').value = cards[0];
        $('card_2').value = cards[1];
        $('card_3').value = cards[2];
    } else if (stage == 'turn') {
        $('card').value = g.getTurnCard();
    } else if (stage == 'river') {
        $('card').value = g.getRiverCard();
    } else if (stage == 'conclusion') {
        if ($('flop_card_1')) {
            cards = g.getFlopCards();
            $('flop_card_1').value = cards[0];
            $('flop_card_2').value = cards[1];
            $('flop_card_3').value = cards[2];
        }
        if ($('turn_card')) {
            $('turn_card').value = g.getTurnCard();
        }
        if ($('river_card')) {
            $('river_card').value = g.getRiverCard();
        }
        $('conclusion').value = g.getConclusion();
        var actions = g.getVillains();
        if (actions.length > 0) {
            $('show_villains').checked = true;
            $('villains_container').show();
            $('num_villains').value = actions.length;
            num_villains_changed();
            for (i = 1; i <= actions.length; i++) {
                var action = actions[i - 1];
                $('villain_' + i + '_seat').value = g.getPlayer(action.player).seat;
                $('villain_' + i + '_card_1').value = action.param[0];
                $('villain_' + i + '_card_2').value = action.param[1];
            }
        }
    }
    $$('input.card').each(function(input) {
        input.fire('fire:showCardEvent');
    });
    if (/pre-flop|flop|turn|river/.test(stage)) {
        fillActionsForStage(g, stage, true);
    }
}

function fillActions(container, actions) {
    var s = [];
    s.push('*** < fillActions:' + container.id);
    s.push(actions.map(function(a) {
        return a.toString();
    }).join('<br/>'));
    s.push('-- players --:')
    s.push(container.select('.player_action')
            .map(function(actionInput) {
        return actionInput.down('.player_label').innerHTML;
    })
            .join('<br/>'));
    var currIdx = 0;
    container.select('.player_action').each(function(actionInput) {
        var action = currIdx < actions.length ? actions[currIdx] : null;
        var player = actionInput.down('.player_label').innerHTML;
        s.push('**' + player + ' -- ' + Object.toJSON(action));
        if (action && action.player == player) {
            actionInput.show();
            actionInput.down('.action_type').value = action.type;
            actionInput.down('.amount').value = action.getAmount();
            actionInput.down('.all_in input').checked = action.isAllIn();
            handleActionChanged(actionInput.down('.action_type'));
            currIdx ++;
        } else {
            actionInput.hide();
        }
    });
    s.push('*** > end fillActions:' + container.id);
    log.debug(s.join('<br>'));
}

function fillActionsForStage(game, stage, initQustionChoices) {
    log.debug('*** fillActionsForStage: ' + stage);
    var questions = game.getStageQuestions(stage);
    var endingActions = game.getStageEndingActions(stage);
    if (questions.length == 0) {
        return;
    }
    for (var i = 1; i <= questions.length; i++) {
        var container = $('question_action_container_' + i);
        var question = questions[i - 1];
        var actions = question.actions;
        fillActions(container, actions);

        if (initQustionChoices) {
            var heroAction = question.heroAction;
            for (var j = 1; j <= 5; j++) {
                var choice = 'question_' + i + '_answer_choice_' + j;
                $(choice).value = heroAction.type;
                $(choice).fire('fire:change');
                if (['raise_to', 'bet'].include(heroAction.type)) {
                    $(choice + '_all_in').checked = heroAction.isAllIn();
                    $(choice + '_amount').value = heroAction.getAmount();
                }
            }
        }
    }
    $('num_questions').value = questions.length;
    updateNumQuestions();
    fillActions($('ending_action_container'), endingActions);
}

function concludeUpdateQuestionAndEndingAction() {
    $$('.question_actions_summary').each(function(span) {
        var stage = span.getAttribute('stage').toLowerCase();
        var questionId = span.getAttribute('questionId');

        //log.debug('** ' + stage + ", " + questionId + " **");
        var questions = game.getStageQuestions(stage);
        //log.debug(questions);
        span.innerHTML = getActionsSummary(questions[questionId - 1].actions, false)
                .replace(/\n/g, '<br>');
    });
    $$('.stage_end').each(function(div) {
        var stage = div.getAttribute('stage').toLowerCase();
        var endingActions = game.getStageEndingActions(stage);
        if (endingActions.length > 0) {
            div.down('.action_summary').innerHTML = getActionsSummary(endingActions, true)
                    .replace(/\n/g, '<br>');
        } else {
            div.hide();
        }
    });
}

function updateQuestionAndEndingAction() {
    if (!game) {
        return;
    }
    var questions = game.getStageQuestions(stage);
    if (!questions) {
        return;
    }
    var questionDts = $$('#content dl dt').findAll(function (dt) {
        return /^Question \d$/.test(dt.innerHTML) ||
               (dt.down('label') && /^Question \d$/.test(dt.down('label').innerHTML));
    });

    function setActionsSummary(dt, actions, isEnding) {
        var dl = dt.up('dl');
        var dd = dl.down('dd');
        var div = dd.down('div.summary');
        if (!div) {
            div = new Element('div', {'class' : 'summary'});
            dd.insert({top:div});
        }
        var summary = getActionsSummary(actions, isEnding);
        div.innerHTML = summary.replace(/\n/g, '<br>');
        log.debug(div.innerHTML);
    }

    questions.each(function (question, i) {
        ;
        var dt = questionDts[i];
        if (dt) {
            var actions = question.actions;
            setActionsSummary(dt, actions, false);
        }
    });
    // todo:
    // have better way to identify the ending action input (both in manual enter and playback)
    var dt = $$('#content dl dt').find(function (dt) {
        return /^Ending Action$/.test(dt.innerHTML) ||
               (dt.down('label') && /Ending Action$/.test(dt.down('label').innerHTML));
    });
    if (dt) {
        var endingActions = game.getStageEndingActions(stage);
        if (endingActions.length > 0) {
            setActionsSummary(dt, endingActions, true);
        } else {
            dt.style.visibility = 'hidden';
            if (dt.up('#ending_action_div')) {
                dt.up('#ending_action_div').hide();
            }
        }
    }

}


function var_dump(obj) {
   if(typeof obj == "object") {
      return "Type: "+typeof(obj)+((obj.constructor) ? "\nConstructor: "+obj.constructor : "")+"\nValue: " + obj;
   } else {
      return "Type: "+typeof(obj)+"\nValue: "+obj;
   }
}//end function var_dump
