/*global EventProxy, d3, Raphael, $ */
/*!
 * Matrix的兼容定义
 */
;(function (name, definition) {
    if (typeof define === 'function') { // Module
        define(definition);
    } else { // Assign to common namespaces or simply the global object (window)
        this[name] = definition(function (id) { return this[id];});
    }
})('Matrix', function (require) {
    var DataV = require('DataV');
    var theme = DataV.Themes;

    /**
     * 构造函数
     */
    var Matrix = DataV.extend(DataV.Chart, {
        initialize: function (node, options) {
            this.type = "Matrix";
            this.node = this.checkContainer(node);

            // Properties
            this.font = {};

            // Canvas
            this.defaults.width = 1200;
            this.defaults.height = 1200;
            this.defaults.axisWidth = 40;

            this.setOptions(options);
            this.createCanvas();
            this.move = false;
        }
    });

    Matrix.prototype.getDataTable = function (table) {
        var title = table[0];
        table = table.slice(1);

        var titleLength = title.length;
        var tableWidth = table[0].length;
        var tableHeight = table.length;

        this.tableWidth = tableWidth;
        this.tableHeight = tableHeight;

        //for symmetric matrix
        if (tableWidth !== title.length || tableHeight !== title.length) {
            throw new Error("This matrix is not symmetric matrix!!!");
        } else {
            this.tableWidth = tableWidth;
            this.tableHeight = tableHeight;
        }

        this.title = title;
        return table;
    };

    Matrix.prototype.setSource = function (source) {
        var conf = this.defaults;

        this.source = this.getDataTable(source);
        this.hasSort = false;
        // this.source = this.remapSource(source);
    };

    Matrix.prototype.layout = function () {
        var conf = this.defaults;
        var width = conf.width;
        var height = conf.height;
        var tableWidth = this.tableWidth;
        var tableHeight = this.tableHeight;
        var axisWidth = conf.axisWidth;

        this.cellWidth = Math.min((width - axisWidth) / tableWidth, (height - axisWidth) / tableHeight);

        var startX;
        var startY;
        var bRectWidth;
        var matrixWidth;

        if (width > height) {
            startX = (width - height)/2 + axisWidth;
            startY = axisWidth;
            bRectWidth = height - axisWidth;
            matrixWidth = bRectWidth - axisWidth;
        } else if (height > width) {
            startX = axisWidth;
            startY = (height - width) + axisWidth;
            bRectWidth = width - axisWidth;
        } else {
            startX = axisWidth;
            startY = axisWidth;
            bRectWidth = width - axisWidth;
            matrixWidth = bRectWidth - axisWidth;
        }

        this.startX = startX;
        this.startY = startY;
        this.bRectWidth = bRectWidth;
        this.matrixWidth = matrixWidth;
    };

    Matrix.prototype.getColor = function (i) {
        var colorMatrix = DataV.getColor();
        var length = colorMatrix.length;
        var num = i % length;
        //var color = '#939598';
        var color = '#FFFFFF';

        if (num !== 0) {
            color = colorMatrix[num][0];
        }

        return color;
    };

    Matrix.prototype.createCanvas = function () {
        var conf = this.defaults;
        this.canvas = new Raphael(this.node, conf.width, conf.height);

        this.DOMNode = $(this.canvas.canvas);
        var that = this;
        this.DOMNode.click(function (event) {
            that.trigger("click", event);
            that.update();
        });
        this.DOMNode.dblclick(function (event) {
            that.trigger("dblclick", event);
        });

        var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
        this.DOMNode.bind(mousewheel, function (event) {
            that.trigger("mousewheel", event);
        });

        this.DOMNode.bind("contextmenu", function (event) {
            that.trigger("contextmenu", event);
        });

        this.DOMNode.delegate("circle", "click", function (event) {
            that.trigger("circle_click", event);
        });

        this.DOMNode.delegate("circle", "mouseover", function (event) {
            that.trigger("circle_mouseover", event);
        });

        this.DOMNode.delegate("circle", "mouseout", function (event) {
            that.trigger("circle_mouseout", event);
        });
    };

    Matrix.prototype.generatePaths = function () {
        var canvas = this.canvas;
        var source = this.source;
        var conf = this.defaults;
        var width = conf.width;
        var height = conf.height;
        var startX = this.startX;
        var startY = this.startY;
        var cellWidth = this.cellWidth;
        var tableWidth = this.tableWidth;
        var tableHeight = this.tableHeight;
        var bRectWidth = this.bRectWidth;
        var matrixWidth = this.matrixWidth;

        //canvas.clear();
        // var color = this.getColor();
        // var font = this.getFont();
        var font_family = '微软雅黑';
        var font_size = 8;

        var title = this.title;

        var row = [];
        var columnLine = [];
        var columnText = [];

        var backgroundRect = canvas.rect(startX, startY, bRectWidth, bRectWidth);
        //backgroundRect.attr({fill: "#939598", stroke: "none", "fill-opacity": 0.8});
        backgroundRect.attr({fill: "#ffffff", stroke: "none", "fill-opacity": 0.8});
        backgroundRect.toBack();

        var sort;
        if (this.hasSort) {
            sort = this.sort;
        }
        var i, j, a, b, color, rect;
        var rects = [];  //for column change move rect
        for (i = 0; i < tableHeight; i++) {
            if (!this.hasSort){
                a = i;
            } else {
                for (j = 0; j < sort.length; j++) {
                    if (sort[j] === i) {
                        a = j;
                    }
                }
            }
            var rowRect = canvas.set();
            canvas.path("M" + startX + " " + (startY + cellWidth * i) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
             + (startY + cellWidth * i)).attr({stroke: "#D1D1D1", "stroke-width": 1});
            rowRect.push(canvas.text(-20, cellWidth / 2, title[i])
                .attr({"fill": "#000000",
                    "fill-opacity": 0.7,
                    "font-family": "Verdana",
                    //"font-weight": "bold",
                    "font-size": 12}));

            for (j = 0; j < tableWidth; j++) {
                if (!this.hasSort) {
                    color = this.getColor(source[i][j]);
                } else {
                    color = this.getColor(source[i][sort[j]]);
                }
                rect = canvas.rect(cellWidth * j, 0, cellWidth, cellWidth)
                    .attr({stroke: "none", fill: color, "fill-opacity": 0.8});
                rowRect.push(rect);
                rects.push(rect);
            }

            rowRect.transform("t" + startX + ", " + (startY + cellWidth * a));
            row.push(rowRect);
        }

        canvas.path("M" + startX + " " + (startY + cellWidth * tableHeight) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
             + (startY + cellWidth * tableHeight)).attr({stroke: "#D1D1D1", "stroke-width": 1});

        for (i = 0; i < tableWidth; i++) {
            // var columnLine = canvas.set();
            // var columnText = canvas.set();
            if (!this.hasSort){
                a = i;
            } else {
                for (j = 0; j < sort.length; j++) {
                    if (sort[j] === i) {
                        a = j;
                    }
                }
            }
            columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
                .attr({stroke: "#D1D1D1", "stroke-width": 1})
                .transform("t" + (startX + cellWidth * a) + ", " + startY));
            columnText.push(canvas.text(cellWidth / 2, -20, title[i])
                .attr({"fill": "#000000",
                    "fill-opacity": 0.7,
                    "font-family": "Verdana",
                    //"font-weight": "bold",
                    "font-size": 12})
                .transform("t" + (startX + cellWidth * a) + ", " + startY + "r90"));
        }

        columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
                .attr({stroke: "#D1D1D1", "stroke-width": 1})
                .transform("t" + (startX + cellWidth * tableWidth) + ", " + startY));

        this.row = row;
        this.columnText = columnText;
        this.columnLine = columnLine;
        this.rects = rects;
    };

    Matrix.prototype.getSort = function (source) {
        var sumQueue = [];
        var sort = [];
        var rowData;
        var rowLength;
        var sum;
        var means;
        var matrixD = [];
        var quareSum;
        var rowquareSum = [];

        var i, j, k;
        for (i = 0 ; i < source.length ; i++) {
            rowData = source[i];
            rowLength = rowData.length;
            sum = 0;
            quareSum = 0;

            for (j = 0 ; j < rowLength ; j++) {
                sum = sum + rowData[j];
            }

            means = sum / rowLength;
            for (j = 0 ; j < rowLength ; j++) {
                rowData[j] = rowData[j] - means;
                quareSum = quareSum + Math.pow(rowData[j], 2);
            }

            quareSum = Math.sqrt(quareSum);

            rowquareSum.push(quareSum);
            matrixD.push(rowData);
        }

        var rowI;
        var rowJ;
        var matrixR = [];

        for (i = 0 ; i < source.length ; i++) {
            matrixR[i] = [];
            for (j = 0 ; j < source.length ; j++) {
                matrixR[i][j] = 0;
            }
        }

        for (i = 0 ; i < source.length ; i++) {
            rowI = matrixD[i];
            matrixR[i][i] = source[i][i];
            for (j = i + 1 ; j < source.length ; j++) {
                sum = 0;
                rowJ = matrixD[j];
                for (k = 0; k < rowLength; k++) {
                    sum = sum + rowI[k] * rowJ[k];
                }

                sum = sum / (rowquareSum[i] * rowquareSum[j]);
                matrixR[i][j] = sum;
                matrixR[j][i] = sum;
            }
        }



        return matrixR;
    };
    
    Matrix.prototype.update = function () {
        var i, j;
        var source = [];
        for(i = 0; i < this.source.length ; i++){
            source[i] = this.source[i].concat();
        }

        var sort = [];
        for (i = 0; i < source[0].length; i++) {
            sort.push(i);
        }

        if (this.hasSort) {
            this.sort = sort;
            this.hasSort = false;
        } else {
            var getSort = this.getSort;
            var i, j;
            var pt;
            var nowSort = [];
            var iterations = 12;

            for (i = 0; i < iterations; i++) {
                source = getSort(source);
            }

            nowSort = source[0];

            var a, b;
            for (i = 1; i < sort.length; i++) {
                a = sort[i];
                for (j = i + 1; j < sort.length; j++) {
                    b = sort[j];
                    if (nowSort[a] < nowSort[b]) {
                        pt = sort[i];
                        sort[i] = sort[j];
                        sort[j] = pt;
                    }
                }
            }
            sort = [0,7,5,2,8,3,1,9,6,14,15,4,13,10,16,11,12];
            this.sort = sort;
            this.hasSort = true;
        }

        if (!this.move) {
            this.move = true;
            var rects = this.rects;
            var num;
            var startX = this.startX;
            var startY = this.startY;
            var cellWidth = this.cellWidth;

            var rowAnim;
            var columnLineAnim;
            var columnTextAnim;
            var anim;

            for (i = 0; i < sort.length; i++) {
                num = sort[i];
                // if (num != i) {
                rowAnim = Raphael.animation({transform: ["t", startX, (startY + cellWidth * i)]}, 200, "<>");
                this.row[num].animate(rowAnim.delay(100 * i));
                // }
            }

            var that = this;
            var moveEnd = function () {
                that.move = false;
            };

            for (i = 0; i < sort.length; i++) {
                num = sort[i];
                // if (num != i) {
                //columnLineAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY]}, 1000, "<>");
                columnTextAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY, "r", 90]},
                    200, "<>");
                //this.columnLine[num].animate(columnLineAnim.delay(500 * (i + sort.length + 1)));
                this.columnText[num].animate(columnTextAnim.delay(100 * (i + sort.length + 1)));

                for (j = 0; j < sort.length; j++) {
                    if (i === sort.length - 1 && j === sort.length - 1) {
                        anim = Raphael.animation({'x': cellWidth * i}, 200, "<>", moveEnd);
                    } else {
                        anim = Raphael.animation({'x': cellWidth * i}, 200, "<>");
                    }
                    rects[j * sort.length + num].animate(anim.delay(100 * (i + sort.length + 1)));
                }
                // }
            }
        }
    };

    Matrix.prototype.render = function (options) {
        if (!this.move) {
            this.canvas.clear();
            this.setOptions(options);
            this.layout();
            this.generatePaths();
        }
    };

    return Matrix;
});