/**
 * 天赋系的视图对象及相关函数。
 * @create   2004-9-29 source0
 * @author   source0 source0@hotmail.com
 * @copyright 版权所有（C） 2004  source0
 *                这一程序是自由软件，你可以遵照自由软件基金会出版的GNU通用
 *            公共许可证条款来修改和重新发布这一程序。或者用许可证的第二版，
 *            或者（根据你的选择）用任何更新的版本。
 *                发布这一程序的目的是希望它有用，但没有任何担保。甚至没有
 *            适合特定目的的隐含的担保。更详细的情况请参阅GNU通用公共许可证。
 *                你应该已经和程序一起收到一份GNU通用公共许可证的副本。如果
 *            还没有，写信给：
 *                The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
 *                MA02139, USA
 */


function toX(column){
    return 2 * column + 1;
}
function toY(tier){
    return 2 * tier + 1;
}
function toColumn(x){
    return (x - 1) / 2;
}
function toTier(y){
    return (y - 1) / 2;
}

/**
 * 天赋系的视图。
 * @param model CTalentClass 实例。
 * @param hspace 显示时，天赋之间的水平间隔。
 * @param vspace 显示时，天赋之间的垂直间隔。
 * @param imgPath 连线图片目录。
 */
function CTalentClassView(model, hs, vs, imgPath){
    this.model = model;
    this.hSpace = 20;
    this.vSpace = 14;
    this.commonImagePath = "common/image";
    this.nullGridImage = "nullgrid.gif";

    if ( hs ){
        this.hSpace = hs;
    }
    if ( vs ){
        this.vSpace = vs;
    }
    if ( imgPath ){
        this.commonImagePath = imgPath;
    }

    var imageWidth = 40;
    var imageHeight = 40;
    var imageBorderSize = 3;

    var GRID_SYMBOL = "_GRID_";
    var TALENT_AREA_SYMBOL = "_TALENT_AREA_";
    var TALENT_ICON_SYMBOL = "_TALENT_ICON_";
    var RANK_AREA_SYMBOL = "_TALENT_RANK_";

    /** 保存每个天赋所在位置信息的Map。key=X_Y， value=天赋视图实例。 */
    var talentsPos = new Array();


    /**
     * 保存网格中所有路径的信息Map。key=X_Y, value=连线方向定义。
     * 例如，如果value["3_4"]="ESN"，则表示，在(3,4)位置上，从北(N)到南(S)、从北(N)到东(E)各有一条直线经过。
     */
    var pathInfo = new Array();

    /** 箭头信息。key=X_Y, value=箭头方向信息，两个字符，第一个为起始位置，第二个为终止位置。*/
    var arrowInfo = new Array();

    var talentViews = model.getTalentViews();

    createPath(talentViews, talentsPos, pathInfo, arrowInfo);

    this.reset = function(){
        model.reset();
        talentViews = model.getTalentViews();
        talentsPos = new Array();
        pathInfo = new Array();
        arrowInfo = new Array();
        createPath(talentViews, talentsPos, pathInfo, arrowInfo);
        this.getHtml();
    }
    this.getImageWidth = function(){
        return imageWidth;
    }
    this.getImageHeight = function(){
        return imageHeight;
    }

    /**
     * 判断指定的天赋格是否是空闲的。
     * @param areaId 天赋格子的ID。
     * @return 如果该格子内既没有天赋视图，又没有连线，返回 true，否则返回 false。
     */
    this.isEmptyGrid = function(areaId){
        var tv = this.getTalentViewByAreaId(areaId);

        /* 检查目的地是否存在连线。 */
        var loc = this.getLocationByAreaId(areaId);
        var curPos = loc["column"];
        var curTier = loc["tier"];
        var lineImage = this.getLineImage(curPos, curTier);

        if (( null == tv ) && (null == lineImage)){
            return true;
        } else {
            return false;
        }
    }

    /**
     * 得到指定位置的连线图片地址。
     * @param column 天赋的位置。
     * @param tier 天赋层次。
     * @return 如果指定参数下存在连线，返回图片地址 ，否则返回 null 。
     */
    this.getLineImage = function(column, tier){
        if ( null != pathInfo[toX(column) + "_" + toY(tier)] ){
            return getLineImageSrc(this.commonImagePath, pathInfo[toX(column) + "_" + toY(tier)]);
        } else {
            return null;
        }
    }

    /**
     * 得到指定天赋在显示时的位置。
     * @param id 天赋的ID。
     * @return 天赋的位置，格式为：X_Y 。X为天赋在表格中的列，Y为天赋在表格中的行。
     */
    this.getTalentPos = function(id){
        var tv = talentViews[id];
        for ( var key in talentsPos ){
            if ( talentsPos[key] == tv ){
                return key;
            }
        }
        return null;
    }

    /**
     * 得到指定层次、指定位置处的天赋视图实例。
     * @param column 天赋的位置。
     * @param tier 天赋层次。
     * @return 如果指定参数下存在天赋视图实例，返回实例，否则返回 null。
     */
    this.getTalentView = function(column, tier){
        return talentsPos[toX(column) + "_" + toY(tier)];
    }

    /**
     * 得到格子处的天赋视图实例。
     * @param gridID 格子的ID。
     * @return 如果指定参数下存在天赋视图实例，返回实例，否则返回 null。
     * @create 2004-10-05 source0
     */
    this.getTalentViewByGridId = function(gridId){
        if ( this.model.id != gridId.substring(0, gridId.indexOf(GRID_SYMBOL))){
            return null;
        }
        grid = gridId.substring(parseInt(gridId.indexOf(GRID_SYMBOL)) + parseInt(GRID_SYMBOL.length), gridId.length);
        return talentsPos[grid];
    }

    /**
     * 得到天赋格子处的天赋视图实例。
     * @param id 天赋格子的ID。
     * @return 如果指定参数下存在天赋视图实例，返回实例，否则返回 null。
     * @create 2004-10-07 source0
     */
    this.getTalentViewByAreaId = function(id){
        if ( this.model.id != id.substring(0, id.indexOf(TALENT_AREA_SYMBOL))){
            return null;
        }
        id = id.substring(parseInt(id.indexOf(TALENT_AREA_SYMBOL)) + parseInt(TALENT_AREA_SYMBOL.length), id.length);
        return talentsPos[id];
    }
    /**
     * 得到指定天赋的图标的ID。
     * @param id 天赋的ID。
     * @return 图标的ID。
     * @create 2004-10-07 source0
     */
    this.getIconId = function(id){
        return this.model.id + TALENT_ICON_SYMBOL + id;
    }
    /**
     * 得到指定天赋的级别显示区域的ID。
     * @param id 天赋的ID。
     * @return ID。
     * @create 2004-10-07 source0
     */
    this.getRankAreaId = function(id){
        return this.model.id + RANK_AREA_SYMBOL + id;
    }
    /**
     * 得到指定位置上的格子的ID。
     * @param column 水平位置。
     * @param tier 天赋层次。
     * @create 2004-10-05 source0
     */
    this.getGridId = function(column, tier){
        return this.model.id + GRID_SYMBOL + toX(column) + "_" + toY(tier);
    }
    /**
     * 根据格子位置，得到指定位置上的格子的ID。
     * @param x 格子的 X 坐标。
     * @param y 格子的 Y 坐标。
     * @create 2004-10-05 source0
     */
    this.getGridIdByPos = function(x, y){
        return this.model.id + GRID_SYMBOL + x + "_" + y;
    }
    /**
     * 得到指定位置上的天赋格子的ID。
     * @param column 水平位置。
     * @param tier 天赋层次。
     * @create 2004-10-07 source0
     */
    this.getAreaId = function(column, tier){
        return model.id + TALENT_AREA_SYMBOL + toX(column) + "_" + toY(tier);
    }
    /**
     * 根据格子位置，得到指定位置上的天赋格子的ID。
     * @param x 格子的 X 坐标。
     * @param y 格子的 Y 坐标。
     * @create 2004-10-05 source0
     */
    this.getAreaIdByPos = function(x, y){
        return model.id + TALENT_AREA_SYMBOL + x + "_" + y;
    }

    /* 与 getGridId(id) 等价。仅为对象内的私有函数调用而存在。*/
    function createGridId(column, tier){
        return model.id + GRID_SYMBOL + toX(column) + "_" + toY(tier);
    }
    /* 仅为对象内的私有函数调用而存在。*/
    function createAreaId(x, y){
        return model.id + TALENT_AREA_SYMBOL + x + "_" + y;
    }
    /* 与 getIconId() 等级。仅为对象内的私有函数调用而存在。*/
    function createIconId(id){
        return model.id + TALENT_ICON_SYMBOL + id;
    }
    /* 与 getRankAreaId(id) 等价。仅为对象内的私有函数调用而存在。*/
    function createRankAreaId(id){
        return model.id + RANK_AREA_SYMBOL + id;
    }
    /**
     * 得到指定格子ID所对应的天赋的位置信息。
     * @param gridId 格子的ID。
     * @return Map型的数组，["column"] 为水平位置。 ['tier']  为天赋层次。
     * @create 2004-10-05 source0
     */
    this.getLocationByGridId = function(gridId){
        var rt = new Array();
        var grid = gridId.substring(parseInt(gridId.indexOf(GRID_SYMBOL)) + parseInt(GRID_SYMBOL.length), gridId.length);
        rt["column"] = toColumn(grid.substring(0, grid.indexOf('_')));
        rt['tier'] = toTier(grid.substring(1 + parseInt(grid.indexOf('_')), grid.length));
        return rt;
    }

    /**
     * 得到指定天赋格子ID所对应的天赋的位置信息。
     * @param areaId 天赋格子的ID。
     * @return Map型的数组，["column"] 为水平位置。 ['tier']  为天赋层次。
     * @create 2004-10-07 source0
     */
    this.getLocationByAreaId = function(areaId){
        var rt = new Array();
        var grid = areaId.substring(parseInt(areaId.indexOf(TALENT_AREA_SYMBOL)) + parseInt(TALENT_AREA_SYMBOL.length), areaId.length);
        rt["column"] = toColumn(grid.substring(0, grid.indexOf('_')));
        rt['tier'] = toTier(grid.substring(1 + parseInt(grid.indexOf('_')), grid.length));
        return rt;
    }

    var gridsHtml = null;
    var ranksHtml = null;
    var talentsHtml = null;

    /**
     * 得到定位用的格子的HTML文本。
     * @param showName 是否显示天赋的名字。
     * @return 包含连线的定位用的格子的HTML文本。
     * @create 2004-10-07 source0
     */
    this.getGridsHtml = function(showName){
        if ( null == gridsHtml ){
            this.getHtml(showName);
        }
        return gridsHtml;
    }
    /**
     * 得到显示天赋级别用的对象的HTML文本。
     * @return 显示天赋级别的HTML对象文本。
     */
    this.getRanksHtml = function(){
        if ( null == ranksHtml ){
            this.getHtml();
        }
        return ranksHtml;
    }
    /**
     * 得到显示天赋图标用的对象的HTML文本。
     * @return 显示天赋图标的HTML对象文本。
     */
    this.getTalentsHtml = function(){
        if ( null == talentsHtml ){
            this.getHtml();
        }
        return talentsHtml;
    }

    /**
     * 得到用于显示的全部HTML文本。
     * @param showName 是否显示天赋的名字。
     */
    this.getHtml = function(showName){
        ranksHtml = new String();
        gridsHtml = new String();
        talentsHtml = new String();

        var width = 4 * ( 2 * imageBorderSize + parseInt(imageWidth)) + 5 * this.hSpace;
        var height = 7 * ( 2 * imageBorderSize + parseInt(imageHeight)) + 8 * this.vSpace;
        var rt = new String();
        rt += "<table border=0 class='talentClassArea'";
        rt += " cellspacing=0 cellpadding=0 width=" + width + ">";
        if ( showName ){
            rt += "<thead>";
            rt += "<tr><th>";
            rt += "<table border=0 width='100%'><tbody>";
            rt += "<th width='20%'><span>&nbsp;</span></th>";
            rt += "<th width='60%' class='talentClassName' align=center>" + model.name + "</th>";
            rt += "<th width='20%' align=right>";
            rt += "<span id='" + model.id + "_AMOUNT' class='talentClassAmountNoData'";
            rt += " onMouseOver='onMouseOverTalentClass(\"" + model.id + "\", arguments[0]);'>&nbsp;&nbsp;&nbsp;</span>";
            rt += "</tbody></table>";
            rt += "</th></tr>";
            rt += "</thead>";
            rt += "<tr><th>";
            rt += "<table width='" + width + "' height='1' style='margin:5;'><tr><td height='1' class='separator'></td></tr></table>";
            rt += "</th></tr>";
        }

        rt += "<tbody>";
        rt += "<tr><td width=" + width + " height='100%'>";
        rt += createTable(this.hSpace, this.vSpace, this.commonImagePath, model.path, model.image);
        rt += "</td></tr>";
        rt += "</tbody>";
        rt += "</table>";
        gridsHtml = rt;
        return gridsHtml + talentsHtml + ranksHtml;
    }

    function createTable(hSpace, vSpace, commonImagePath, imagePath, bgImage){
        var rt = new String();
        rt += "<table class='talentClassAreaGridTable'";
        rt += " border=0 cellspacing=0 cellpadding=1";
        if ( bgImage ){
            rt += " style='background:url(" + imagePath + "/" + bgImage + ");'";
        }
        rt += ">";
        rt += "<tbody>";
        for ( var row = 0; row < 15; row ++ ){
            rt += createRow(row, hSpace, vSpace, commonImagePath, imagePath);
        }
        rt += "</tbody>";
        rt += "</table>";
        return rt;
    }
    function createRow(row, hSpace, vSpace, commonImagePath, imagePath){
        var str = new String();
        str += "<tr>";
        for ( var i = 0; i < 9; i++ ){
            var grid = i + "_" + row;
            var gridId = createGridId(toColumn(i), toTier(row));//model.id + GRID_SYMBOL + grid;
            str += '<td id="' + gridId + '"';
            if ( isInTalentGrid(i, row) ){
                str += ' width=' + (parseInt(imageWidth) + parseInt(2 * imageBorderSize));
                str += ' height=' + (parseInt(imageHeight) + parseInt(2 * imageBorderSize));
            } else {
                if ( isInTalentRow(row) ){
                    str += ' width=' + hSpace;
                }
                if ( isInTalentColumn(i) ){
                    str += ' height=' + vSpace;
                }
            }
            if (( arrowInfo[grid] ) || ( pathInfo[grid] )){
                if ( arrowInfo[grid] ){
                    str += ' style="background:url(' + commonImagePath + '/' + arrowInfo[grid] + 'A.gif);"';
                } else {
                    str += ' style="background:url(' + getLineImageSrc(pathInfo[grid], commonImagePath) + ');"';
                }
            }
            str += '>';
            str += "</td>";

            if ( isInTalentGrid(i, row) ){
                /* 如果鼠标进入了显示天赋的格子，则：
                 * 如果该格子里有天赋，则设置与该天赋相关的信息。
                 * 如果没有天赋，仅激发一个事件。*/
                var areaId = createAreaId(i, row);
                talentsHtml += '<table id="' + createAreaId(i, row) + '"';
                talentsHtml += ' cellspacing=0 cellpadding=0 style="position:absolute;"';
                talentsHtml += '><tbody><tr><td';

                talentsHtml += ' >';
                talentsHtml += '<img';
                talentsHtml += ' width=' + imageWidth + ' height=' + imageHeight;
                talentsHtml += ' onMouseOver="onMouseOverTalentGrid(\'' + areaId + '\', arguments[0]);"';
                talentsHtml += ' onMouseOut="onMouseOutTalentGrid(\'' + areaId + '\', arguments[0]);"';
                talentsHtml += ' onMouseMove="onMouseMoveTalentGrid(\'' + areaId + '\', arguments[0]);"';
                talentsHtml += ' onMouseDown="onMouseDownTalentGrid(\'' + areaId + '\', arguments[0]);"';
                talentsHtml += ' onMouseUp="onMouseUpTalentGrid(\'' + areaId + '\', arguments[0]);"';
                talentsHtml += ' onClick="return onMouseClickedTalentGrid(\'' + areaId + '\', arguments[0]);"';
                talentsHtml += ' onDblClick="return onMouseDblClickedTalentGrid(\'' + areaId + '\', arguments[0]);"';
                talentsHtml += ' onContextMenu=\"return false;\"';
                if ( talentsPos[grid] ){
//                    talentsHtml += imagePath + "/" + talentsPos[grid].icon;
                    talentsHtml += ' src=""';
                    talentsHtml += ' id="' + createIconId(talentsPos[grid].model.id) + '"';

                    /* 表格用于定位以及显示背景图片。单元格内显示级别。 */
                    ranksHtml += '<table id="' + createRankAreaId(talentsPos[grid].model.id) + '"';
                    ranksHtml += ' class="talentRankAreaValid" cellspacing=0 cellpadding=0';
                    ranksHtml += '><tr><td align=center>';
                    ranksHtml += '<span id="' + talentsPos[grid].model.id + '_RANK"';
                    if ( talentsPos[grid].isValid() ){
                        ranksHtml += ' class="talentRankValueValid">0</span>';
                    } else {
                        ranksHtml += ' class="talentRankValueInvalid">0</span>';
                    }
                    ranksHtml += '</td</tr></table>';
                } else {
                    talentsHtml += ' src="';
                    talentsHtml += commonImagePath + '/nullgrid.gif"';
                }
                talentsHtml += " onError='this.src=\"" + IMAGE_PATH + "/" + NOT_EXIST_IMAGE + "\";'";
                talentsHtml += '/>';
                talentsHtml += '</td></tr></tbody></table>';
            }
        }
        str += "</tr>";
        return str;
    }
    /** 是否处于天赋视图的格子里。 */
    function isInTalentGrid(x, y){
        return ((x % 2 ) == 1) && ((y % 2) == 1);
    }
    function isInTalentRow(row){
        return (row % 2) == 1;
    }
    function isInTalentColumn(col){
        return (col % 2) == 1;
    }
    function getLineImageSrc(lineType, commonImagePath){
        var file = commonImagePath + "/";
        var direction = new Array("E", "S", "W", "N", "A");
        for ( var i = 0; i < direction.length; i++ ){
            if ( lineType.indexOf(direction[i]) >= 0 ){
                file += direction[i];
            }
        }
        return file + ".gif";
    }
}

function createPath(talentViews, talentsPos, pathInfo, arrowInfo)
{
    /* 将所有的天赋的位置保存起来。 */
    for ( var key in talentViews){
        var tv = talentViews[key];
        talentsPos[toX(tv.column) + "_" + toY(tv.model.tier)] = tv;
    }

    /* 将所有的连线构造起来。 */
    for ( var key in talentViews){
        var tv = talentViews[key];
        if ( tv.model.requirements ){
            var reqs = tv.model.requirements;
            for ( var key in reqs ){
               addPath(pathInfo, talentViews[reqs[key].talent], tv);
            }
        }
    }

    /**
     * 在两个天赋视图中找到一条有箭头的连线。
     * @param pathInfo 路径信息。
     * @param fromTV 起始位置。
     * @param toTV 终止位置。
     */
    function addPath(pathInfo, fromTV, toTV){
        var x1 = toX(fromTV.column);
        var y1 = toY(fromTV.model.tier);
        var x2 = toX(toTV.column);
        var y2 = toY(toTV.model.tier);
        var arrow = searchPath(pathInfo, x1, y1, x2, y2);
        arrowInfo["" + arrow[0] + "_" + arrow[1]] = arrow[2];
    }
    /**
     * 寻找 (x1, y1)-(x2, y2) 之间的一条路径。
     * @param path 路径数组。
     * @param x1 第一个天赋视图的X。
     * @param y1 第一个天赋视图的Y。
     * @param x2 第二个天赋视图的X。
     * @param y2 第二个天赋视图的Y。
     * @return 箭头信息的数组：[0]X坐标，[1]Y坐标， [2]表示方向的字符串。
     * 如果两者在同行或者同列，路径为一条直线(直线可能会压在天赋视图上)。
     * 其它情况路径为一条折线。折线优先从行开始，折线的弯折次数不超过1。折线总是位于两个点所确定的矩形内部。
     */
    function searchPath(path, x1, y1, x2, y2){
        var key = new String();
        var value = new String();

        /* 在同行或者同列 */
        if ( x1 == x2 ){
            addVLine(path, x1, y1, y2);
            return new Array(x1, y2 - 1, "NS");
        } else if ( y1 == y2 ){
            addHLine(path, x1, x2, y1);
            if ( x1 > x2 ){
                return new Array(x2 + 1, y2, "EW");
            } else {
                return new Array(x2 - 1, y2, "WE");
            }
        }

        /* 折线 */
        /* 首先尝试横向转折后再往下。不行尝试先纵向往下，再横向转折。
         * 如果还找不到，返回压住天赋视图的一条路径。*/
        if ( testHLine(x1, x2, y1) && testVLine(x2, y1, y2 - 1)){//先尝试横向转折后再往下
            addHLine(path, x1, x2, y1);
            addPathPoint(path, x2, y1, "S", (x1 > x2) ? "E" : "W");
            addVLine(path, x2, y1, y2);
            return new Array(x2, y2 - 1, "NS");
        } else {//无论如何，返回一条先下，再横向的路径。
            addVLine(path, x1, y1, y2);
            addPathPoint(path, x1, y2, "N", (x1 > x2) ? "W" : "E");
            addHLine(path, x1, x2, y2);
            if ( x1 > x2 ){
                return new Array(x2 + 1, y2, "EW");
            } else {
                return new Array(x2 - 1, y2, "WE");
            }
        }
    }
    /**
     * 测试指定的水平线上是否可以让连线经过。测试时不包含第一个点，包含第二个点。
     * @param x1 第一个点的X。
     * @param x2 第二个点的X。
     * @param y 两个点的Y。
     * @return 如果连线可以经过，返回true，否则返回 false。
     */
    function testHLine(x1, x2, y){
        if ( x1 > x2 ){
            for ( var x = x1 - 1; x >= x2; x-- ){
                if ( talentsPos[x + "_" + y] ){
                    return false;
                }
            }
        } else {
            for ( var x = x1 + 1; x <= x2; x++ ){
                if ( talentsPos[x + "_" + y] ){
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * 测试指定的垂直线上是否可以让连线经过。测试时不包含第一个点，包含第二个点。
     * @param x 两个点的X。
     * @param y1 第一个点的Y。
     * @param y2 第二个点的Y。
     * @return 如果连线可以经过，返回true，否则返回 false。
     */
    function testVLine(x, y1, y2){
        for ( var y = y1 + 1; y <= y2; y++ ){
            if ( talentsPos[x + "_" + y] ){
                return false;
            }
        }
        return true;
    }
    /* 添加一条水平线，不包含两个端点。*/
    function addHLine(path, x1, x2, y){
        for ( var x = Math.min(x1, x2) + 1; x < Math.max(x1, x2); x++ ){
            addPathPoint(path, x, y, "E", "W");
        }
    }
    /* 添加一条垂直线，不包含两个端点。*/
    function addVLine(path, x, y1, y2){
        for ( var y = y1 + 1; y < y2; y++ ){
            addPathPoint(path, x, y, "N", "S");
        }
    }
    /* 添加一个路径点。如果该路径点已经存在，仅仅添加新的方向信息。 */
    function addPathPoint(path, x, y, dir1, dir2){
        var key = x + "_" + y;
        if ( path[key] ){
        } else {
            path[key] = new String();
        }
        if ( path[key].indexOf(dir1) < 0 ){
            path[key] += dir1;
        }
        if ( path[key].indexOf(dir2) < 0 ){
            path[key] += dir2;
        }
    }

}