// ReSharper disable InconsistentNaming
//last update 2016-11-03

window.HtmlTable = window.HtmlTable || (function ($) {

    function HtmlTable() { }

    HtmlTable.prototype.init = init;

    function init(settings) {

        settings = settings || {};

        $(function () {
			
			//CONSTANTS
			var HEADER_COL_WIDTH = 15; //in px
			
            var x2js;
            var cellMatrix;
			var showHeaders = true;
			var showGrid = true;
			var columnLabels =
			["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
			
			var headerColumnWidth = 0;

            $('sheet').each(function (index, sheet) {

                x2js = x2js || new X2JS();

                var placeholder = $(sheet);
                var json = x2js.xml_str2json(placeholder.text());
                var table = json.t;

                table.r = toArray(table.r);

                for (var i = 0; i < table.r.length; i++) {
                    table.r[i].c = toArray(table.r[i].c);
                }

                var totalWidth = 0, columnWidths = [];

                for (var j = 0; j < table.r[0].c.length; j++) {
                    totalWidth += +table.r[0].c[j]._w;
                }
				
				if(showHeaders) {
					totalWidth += HEADER_COL_WIDTH;
				}

                for (var j = 0; j < table.r[0].c.length; j++) {
                    columnWidths[j] = table.r[0].c[j]._w * 100 / totalWidth;
                }
				
				if(showHeaders) {
					//TODO: increase table size width & height
					headerColumnWidth = HEADER_COL_WIDTH * 100 / totalWidth;
				}
				
				
                var domTable = $('<table>').addClass('html-table');

                if (table._fnt) {
                    domTable.css('font-family', table._fnt);
                }

                if (table._bc) {
                    //domTable.css('border-color', table._bc.replace('0x', '#'));
                }

                initCellMatrix(table);
				
				
				if(showHeaders) {
					//add columns
					 var domRow = $('<tr>');
					 domRow.css('height', 22);
					 var row = table.r[0];
					 
					 var domCell = $('<th>');
					 domCell.attr('scope', 'col');
 					 domCell.attr('class', 'head-axis');
					 domCell.css({'width': '22px' });
					 domCell.html("&nbsp;").appendTo(domRow);
					 
                     for (var j = 0; j < row.c.length; j++) {

                         var domCell = $('<th>');
                         var cell = row.c[j];
						 // <th scope="col">B</th>
                         domCell.css({ 'width': columnWidths[j] + '%' });
	 					 domCell.attr('scope', 'col');
						 var label = columnLabels[j % columnLabels.length];

                         domCell.html(label).appendTo(domRow);
                     }
					 domTable.append(domRow);
				}
				

                for (var i = 0; i < table.r.length; i++) {

                    var domRow = $('<tr>');
                    var row = table.r[i];

                    if (row._h) {
                        domRow.css('height', row._h);
                    }
					
					if(showHeaders) {
						 var domCell = $('<td>');
						 var rowNumber = i + 1;
	  					 domCell.attr('class', 'row-index');
						 domCell.html(rowNumber).appendTo(domRow);
					}

                    for (var j = 0; j < row.c.length; j++) {

                        var domCell = $('<td>');
                        var cell = row.c[j];

                        domCell.css({ 'width': columnWidths[j] + '%' });

                        if (cell._fnt) {
                            domCell.css('font-family', cell._fnt);
							if(cell._fnt.indexOf("SuperScript") != -1) {
								cell.isSuperScript = true;
							}
                        }

                        if (cell._fontSize) {
							//skip font size when we have word processor row
							if(cell._typ != 4) {
                            	domCell.css('font-size', cell._fontSize + 'px');
							}
                        }

                        if (cell._bld) {
                            domCell.css('font-weight', 'bold');
                        }

                        if (cell._itl) {
                            domCell.css('font-style', 'italic');
                        }

                        if (cell._undln) {
                            domCell.css('text-decoration', 'underline');
                        }

                        if (cell._fontCol) {
                            domCell.css('color', parseHexColor(cell._fontCol));
                        }

                        var opts = cell._algn ? parseAlignmentOptions(cell._algn) : getDefaultAlignmentOptions(cell);

                        domCell.css({ 'vertical-align': opts.vertical, 'text-align': opts.horizontal });

                        if (cell._bgc) {
                            domCell.css('background-color', parseHexColor(cell._bgc));
                        }
						
                        // dp attribute can contain 0
                        if (cell._dp !== undefined) {
                            cell.text = isNaN(cell.text) ? cell.text : roundNumber(cell.text, parseFloat(cell._dp));
                        }
						
						if(cell._res !== undefined) {
							cell.text = cell._res;
						}
						
						if(cell._ind !== undefined) {
							domCell.css('text-indent' , parseInt(cell._ind) + 'px')
						}

                        setBorderStyles(domCell, cellMatrix.matrix[i][j]);
                        domCell.html(handleCellText(cell, cell.text)).appendTo(domRow);
                    }

                    domTable.append(domRow);
                }

                table.merge.m = toArray(table.merge.m);

                if (table.merge.m && table.merge.m.length) {

                    var surplusCells = [];

                    for (var k = 0; k < table.merge.m.length; k++) {

                        var merge = table.merge.m[k];
                        var rows = merge._rows.split(',');
                        var cols = merge._cols.split(',');
                        var baseCell, extraWidth = 0;
						
						var offsetI = 0;
						var offsetJ = 0;
						
						if(showHeaders) {
							offsetI = 1;
							offsetJ = 1;
						}
						
                        for (var i = 0; i < rows.length; i++) {
                            for (var j = 0; j < cols.length; j++) {

                                var mergedCell = domTable.find('tr:eq(' + (parseInt(rows[i]) + offsetI) + ') td:eq(' + (parseInt(cols[j]) + offsetJ) + ')');

                                if (i === 0 && j === 0) {

                                    baseCell = mergedCell;

                                    if (rows.length > 1) {
                                        mergedCell.attr('rowspan', rows.length);
                                    }

                                    if (cols.length > 1) {
                                        mergedCell.attr('colspan', cols.length);
                                    }

                                } else {

                                    if (i === 0) {
                                        extraWidth += parseInt(mergedCell.css('width'), 10);
                                    }

                                    surplusCells.push(mergedCell);
                                }
                            }
                        }

                        if (extraWidth) {
                            baseCell.css('width', parseInt(baseCell.css('width'), 10) + extraWidth + '%');
                        }
                    }

                    for (var k = 0; k < surplusCells.length; k++) {
                        surplusCells[k].remove();
                    }
                }

                placeholder.replaceWith(domTable);
            });

            if (Object.prototype.toString.call(settings.complete) === '[object Function]') {
                settings.complete();
            }

            function decodeCellText(cellText) {

                var result;

                // some encoded characters can't be decoded by decodeURIComponent
                // so unescape is used for such cases
                try {

                    result = decodeURIComponent(cellText);

                } catch (e1) {

                    try {

                        result = unescape(cellText);

                    } catch (e2) {

                        result = cellText;
                    }
                }

                return result;
            }
			
		    function convertPtoDIV (match, p1, p2, p3, offset, string) {
		     if(p1) {
		      return "<DIV" + p2
		     } else if(p3) {
		      return "</DIV>"
		     }
		    }

            // handle cell text: decode it ..
            // and replace a few attributes with more appropriate ones
            function handleCellText(cell, cellText) {

                var decodedText,
                    listOfSizes,
                    uniqueListOfSizes,
                    sizeValue;

                if(!cellText){
                  return cellText;
                }

                decodedText = decodeCellText(cellText);
				
				if(cell._typ == 4) {
					console.log(decodedText);
				}

                // there is a problem with font sizes, the code below fixes it
                // find attrs like: SIZE="12"
                listOfSizes = decodedText.match(/(SIZE=")([0-9]*)(")/gi);

                if (listOfSizes) {

                  // uniqueListOfSizes = listOfSizes.filter(function(value, index, self){
                  //   return self.indexOf(value) === index;
                  // });

                  listOfSizes.forEach(function(element){
                    sizeValue = parseInt(element.match((/"([^']+)"/))[1]);
                    decodedText = decodedText.replace(element, 'style="font-size:' + sizeValue + 'px;"');
                  });
                }
				
				//replace <p> with <div>
				var regexPtoDIV = /(<\s*p)([\s>])|(<\s*\/\s*p\s*>)/gi;
				decodedText = decodedText.replace(regexPtoDIV, convertPtoDIV);
				
                return decodedText;
            }

            function toArray(input) {

                return Array.isArray(input) ? input : [input || {}];
            }

            function pad(n, width) {

                return n.length >= width ? n : new Array(width - n.length + 1).join('0') + n;
            }

            function parseHexColor(input) {

                return '#' + pad(parseInt(input, 10).toString(16), 6);
            }

            function parseAlignmentOptions(input) {

                var opts = {};

                switch (input.charAt(0)) {
                    case 't':
                        opts.vertical = 'top';
                        break;
                    case 'm':
                        opts.vertical = 'middle';
                        break;
                    case 'b':
                        opts.vertical = 'bottom';
                        break;
                }

                switch (input.charAt(1)) {
                    case 'l':
                        opts.horizontal = 'left';
                        break;
                    case 'm':
                        opts.horizontal = 'center';
                        break;
                    case 'r':
                        opts.horizontal = 'right';
                        break;
                }

                return opts;
            }

            function getDefaultAlignmentOptions(cell) {

                var opts = {};
				var input = cell.text;

                opts.vertical = 'top';

                opts.horizontal = isNaN(input) ? 'left' : 'right';
				
				if(cell._res) {
					opts.horizontal = isNaN(cell._res) ? 'left' : 'right';
				}

                return opts;
            }

            function initCellMatrix(theTable) {

                cellMatrix = new CellMatrix(theTable.r.length, theTable.r[0].c.length);

                for (var i = 0; i < theTable.r.length; i++) {
                    var r1 = theTable.r[i];
                    for (var j = 0; j < r1.c.length; j++) {
						r1.c[j].bc = theTable._bc;
                        cellMatrix.addCell(r1.c[j]);
                    }
                }

                cellMatrix.calculateBorderSizes(null);

            }

            function setBorderStyles(cell, cellFromMatrix) {
				var extraTickness = 0;
				
				if(!showGrid) {
					cell.css('border-top-width', cellFromMatrix.top + 'px');
					cell.css('border-right-width', cellFromMatrix.right + 'px');
					cell.css('border-bottom-width', cellFromMatrix.bottom + 'px');
					cell.css('border-left-width', cellFromMatrix.left + 'px');
					cell.css('border-color', cellFromMatrix.borderColor);
				}
				

				
				if(cellFromMatrix.top == "3") {
					cell.css('border-top-width', cellFromMatrix.top + 'px');
					cell.css('border-top-style', 'double');
					cell.css('border-top-color', cellFromMatrix.borderColor);
				} else if(cellFromMatrix.top == "1" || cellFromMatrix.top == "2"){
					cell.css('border-top-color', cellFromMatrix.borderColor);
                	cell.css('border-top-width', (parseInt(cellFromMatrix.top) + extraTickness) + 'px');
					// cell.css('border-top-style', 'solid');
				}
				
				if(cellFromMatrix.right == "3") {
					cell.css('border-right-width', cellFromMatrix.right + 'px');
					cell.css('border-right-style', 'double');
					cell.css('border-right-color', cellFromMatrix.borderColor);
				} else if(cellFromMatrix.right == "1" || cellFromMatrix.right == "2"){
					cell.css('border-right-color', cellFromMatrix.borderColor);
                	cell.css('border-right-width', (parseInt(cellFromMatrix.right) + extraTickness) + 'px');
					// cell.css('border-right-style', 'solid');
				}

				if(cellFromMatrix.bottom == "3") {
					cell.css('border-bottom-width', cellFromMatrix.bottom + 'px');
					cell.css('border-bottom-style', 'double');
					cell.css('border-bottom-color', cellFromMatrix.borderColor);
				} else if(cellFromMatrix.bottom == "1" || cellFromMatrix.bottom == "2"){
					cell.css('border-bottom-color', cellFromMatrix.borderColor);
                	cell.css('border-bottom-width', (parseInt(cellFromMatrix.bottom) + extraTickness) + 'px');
					// cell.css('border-bottom-style', 'solid');
				}

				
				if(cellFromMatrix.left == "3") {
					cell.css('border-left-width', cellFromMatrix.left + 'px');
					cell.css('border-left-style', 'double');
					cell.css('border-left-color', cellFromMatrix.borderColor);
				} else if(cellFromMatrix.left == "1" || cellFromMatrix.left == "2"){
					cell.css('border-left-color', cellFromMatrix.borderColor);
                	cell.css('border-left-width', (parseInt(cellFromMatrix.left) + extraTickness) + 'px');
					// cell.css('border-left-style', 'solid');
				}
            }

            function createArray(length) {

                var arr = new Array(length || 0),
                    i = length;

                if (arguments.length > 1) {
                    var args = Array.prototype.slice.call(arguments, 1);
                    while (i--) {
                        arr[length - 1 - i] = createArray.apply(this, args);
                    }
                }

                return arr;
            }

            function CellMatrix(rowsCount, columnsCount) {

                var currentX = 0;
                var currentY = 0;

                var that = this;

                that.matrix = createArray(rowsCount, columnsCount);

                function nextElement() {

                    if (currentX === columnsCount - 1 && currentY === rowsCount - 1) {
                        return -1;
                    }

                    if (currentX === columnsCount - 1) {

                        currentX = 0;
                        currentY++;
                        return;
                    }

                    currentX++;
                }

                that.addCell = function (cell) {
                    that.matrix[currentY][currentX] = new Cell(cell);
                    nextElement();
                };

                that.calculateBorderSizes = function (cell) {

					for (var i = 0; i < rowsCount; i++) {
                        for (var j = 0; j < columnsCount; j++) {
                            cell = that.matrix[i][j];

                            if(i == 0) {
                                cell.topBorderSize = cell.top;
                            } else if(i == rowsCount - 1) {
                                cell.bottomBorderSize = cell.bottom;
                            } else {
                                cell.bottomBorderSize = Math.max(cell.bottom, that.matrix[i+1][j].top);
                            }
                            if (j == 0) {
                                cell.leftBorderSize = cell.left;
                            } else if (j == columnsCount - 1) {
                                cell.rightBorderSize = cell.right;
                            } else {
                                cell.rightBorderSize = Math.max(cell.right, that.matrix[i][j+1].left);
                            }
                        }
					}
                };
            }

            function Cell(cell) {

                var borderSizesAsString = cell._bd;

                if (!borderSizesAsString) {
                    borderSizesAsString = "0000";
                }

                this.top = borderSizesAsString.charAt(0);
                this.right = borderSizesAsString.charAt(1);
                this.bottom = borderSizesAsString.charAt(2);
                this.left = borderSizesAsString.charAt(3);
				
				
				var borderColorAsString = cell.bc;
                if (!borderColorAsString) {
                    borderColorAsString = "0x000";

                }
				this.borderColor = borderColorAsString.replace('0x', '#');
				//patch the case when border color is black
				if(this.borderColor == "#0") {
					this.borderColor = "rgb(0,0,0)";
				} else {
					this.borderColor = convertToValidHEXColor(borderColorAsString);
				}
            }
			
			function convertToValidHEXColor(colorString) {
				//color has no alpha channel
				var trimmed = colorString;
				if(trimmed.lastIndexOf("0x", 0) === 0) {
					trimmed = trimmed.substring(2);
				}
				var fullColor = "000000";
				
				var finalColor = [];
				var colorLenght = trimmed.length;
				
				for(var i = fullColor.length - 1; i >= 0; i--){
					colorLenght--;
					if(colorLenght >= 0) {
						finalColor[i] = trimmed.charAt(colorLenght);
					} else {
						finalColor[i] = fullColor.charAt(i);
					}
				} 
				
				var newColor = "";
				for(i = 0; i < finalColor.length; i++) {
					newColor += finalColor[i];
				}
				
				return "#" + newColor;
			}

            function roundNumber(number, decimalPoints) {

                var parts = number.split('.');
                var rest = parts[1];

                if (!rest) {
                    rest = '';
                }

                rest = rest.slice(0, decimalPoints);
                var gapSize = decimalPoints - rest.length;

                for (var i = 0; i < gapSize; i++) {
                    rest += '0';
                }

                return !rest ? parts[0] : parts[0] + '.' + rest;
            }

        });
    }

    return new HtmlTable();

})($);
