function SPlayer(targetContainer, width, height, dataArray, buttonsCode, displayTextArea) {
    this.targetContainer = document.getElementById(targetContainer);
    this.width = width;
    this.height = height;
    this.dataArray = dataArray;
    this.buttonsCode = buttonsCode;
    this.displayTextArea = displayTextArea;
    this.pointSize = 3;
    this.textSpace = this.pointSize + 4;
    this.textSize = 20;
    this.textIndexCoeficient = 0.7;
    this.textYposCoeficient = 0.2;
    
    this.direction = 1;
    
    this.position = 0 // pozice v animaci == cislo snimku
    
    this.init();
    
    this.actText();
}

SPlayer.prototype.init = function() {
    var canvas = document.createElement("canvas");
    canvas.setAttribute("width", this.width);
    canvas.setAttribute("height", this.height);
    this.ctx = canvas.getContext("2d");
    this.ctx.textBaseline = 'bottom'; // set text draw to bottom
    
    this.targetContainer.setAttribute("style", "width: " + (this.width + 2) + "px;");
    this.targetContainer.appendChild(canvas);
    
    this.showControls(this.buttonsCode);
    this.initTextArea();
    
    this.redrawScene();
}

// shows control buttons of animation 0 == no, 1 == jump keyframe FW,BW, 2 == play FW,BW, 3 == all
SPlayer.prototype.showControls = function(controlsID) {
//    alert(controlsID);
    if (controlsID > 0 && controlsID < 4) {
        // init control frame
        var ctrlFrame = document.createElement("div");
        ctrlFrame.setAttribute("class", "controlFrame");
        width = (controlsID < 3) ? 80 : 160;
        
        ctrlFrame.setAttribute("style", "width: "+ width +"px;")
        // init buttons
        // jump BW
        if (controlsID == 1 || controlsID == 3) {
            this.jumpBWB = document.createElement("input");
            this.jumpBWB.setAttribute("type", "button");
//            this.jumpBWB.setAttribute("value", "JBW");
            this.jumpBWB.setAttribute("class", "jumpBW");
    
            this.jumpBWB.addEventListener("click", this.jump(this, 0));
            ctrlFrame.appendChild(this.jumpBWB);
        }
//        // play pause
//        if (controlsID == 2 || controlsID == 3) {
//            this.playBWB = document.createElement("input");
//            this.playBWB.setAttribute("type", "button");
////            this.playBWB.setAttribute("value", "Play<");
//            this.playBWB.setAttribute("class", "playBW");
//    
//            this.playBWB.addEventListener("click", this.playBW(this));
//            ctrlFrame.appendChild(this.playBWB);
//            
//            this.playFWB = document.createElement("input");
//            this.playFWB.setAttribute("type", "button");
////            this.playFWB.setAttribute("value", "Play>");
//            this.playFWB.setAttribute("class", "playFW");
//    
//            this.playFWB.addEventListener("click", this.playFW(this));
//            ctrlFrame.appendChild(this.playFWB);
//            
//            this.pauseB = document.createElement("input");
//            this.pauseB.setAttribute("type", "button");
////            this.pauseB.setAttribute("value", "Pause");
//            this.pauseB.setAttribute("class", "pause");
//            this.pauseB.setAttribute("style", "display: none;");
//    
//            this.pauseB.addEventListener("click", this.pause(this));
//            ctrlFrame.appendChild(this.pauseB);
//        }
        //jump FW
        if (controlsID == 1 || controlsID == 3) {
            this.jumpFWB = document.createElement("input");
            this.jumpFWB.setAttribute("type", "button");
//            this.jumpFWB.setAttribute("value", "JFW");
            this.jumpFWB.setAttribute("class", "jumpFW");
    
            this.jumpFWB.addEventListener("click", this.jump(this, 1));
            ctrlFrame.appendChild(this.jumpFWB);
        }
        this.targetContainer.appendChild(ctrlFrame);
    }
}

SPlayer.prototype.jump = function(Obj, direction) {
    return function() {
        //Obj.pause(Obj)();
        //Obj.lineY = Obj.getNextPosition(Obj.linePoints, Obj.lineY, direction);
//        console.log(Obj.lineY);
        if (direction == 0) {
            if (Obj.position == 0) {
//                Obj.position = 0;
                return;
            } else {
                Obj.position--;
            }
        } else {
            if (Obj.position >= Obj.dataArray.length - 1) {
//                Obj.position = 0;
                return;
            } else {
                Obj.position++;
            }
        }
        Obj.redrawScene();
    }
}

// prekresli celou scenu
SPlayer.prototype.redrawScene = function() {
    this.clearDrawing();
    
    this.drawArrays();
    this.drawLines();
    this.drawPoints();
    this.drawLabels();
    
    this.actText();
}

// vymaze canvas
SPlayer.prototype.clearDrawing = function() {
    this.ctx.clearRect(0, 0, this.width, this.height);
}

// vyplni oblast // nyni neimplementovano protoze nepouzito
SPlayer.prototype.drawArrays = function() {
    for (var i = 0; i < this.dataArray[this.position][3].length; i++) {
//        console.log(this.dataArray[this.position][2][i][1]);
        this.ctx.fillStyle = this.dataArray[this.position][3][i][1];
        this.ctx.beginPath();
        
        var pos = this.dataArray[this.position][3][i][0][0];
        var x = this.dataArray[this.position][0][pos][0];
        var y = this.dataArray[this.position][0][pos][1];
        this.ctx.moveTo(x, y);
        for (var j = 1; j < this.dataArray[this.position][3][i][0].length; j++) {
            pos = this.dataArray[this.position][3][i][0][j];
//            console.log(pos);
            x = this.dataArray[this.position][0][pos][0];
            y = this.dataArray[this.position][0][pos][1];
            
            this.ctx.lineTo(x, y);
        }
        this.ctx.closePath();
        
        this.ctx.fill();
    }
}

// vykresli popisků
SPlayer.prototype.drawLabels = function() {
    var data;
    for (var i = 0; i < this.dataArray[this.position][1].length; i++) {
        data = this.dataArray[this.position][1][i];
        this.drawLabelDesc(data);
    }
}

//vykresleni popisku
SPlayer.prototype.drawLabelDesc = function(point) {
    var x1 = point[0], y1 = point[1];
    if (point[3] == "") {
        var color = "#000";
    } else {
        var color = point[3];
    }
    if (point[2] != "") {
        this.drawText3(x1, y1, point[2], color, this.textSize, point[4]);
    }
}

// vykresli cary
SPlayer.prototype.drawLines = function() {
    var lineData;
    for (var i = 0; i < this.dataArray[this.position][2].length; i++) {
//        var pos = this.dataArray[this.position][1][i][0];
//        var x1 = this.dataArray[this.position][0][pos][0];
//        var y1 = this.dataArray[this.position][0][pos][1];
//        
//        var pos = this.dataArray[this.position][1][i][1];
//        var x2 = this.dataArray[this.position][0][pos][0];
//        var y2 = this.dataArray[this.position][0][pos][1];
//        
//        var color = this.dataArray[this.position][1][i][2] == "" ? undefined : this.dataArray[this.position][1][i][2];
        
        lineData = this.dataArray[this.position][2][i];
        
        var color = lineData[4] == "" ? undefined : lineData[4];
        
        this.drawLine(lineData[0], lineData[1], lineData[2], lineData[3], color, 1, false);
    }
}

// vykresli body s popisky
SPlayer.prototype.drawPoints = function() {
    for (var i = 0; i < this.dataArray[this.position][0].length; i++) {
        this.drawPoint(this.dataArray[this.position][0][i]);
    }
}

//vykresleni popisku bodu
SPlayer.prototype.drawPoint = function(point) {
    var x1 = point[0], y1 = point[1];
    if (point[3] == "") {
        var color = "#000";
    } else {
        var color = point[3];
    }
//    drawLine(x1-3, y1-3, x1+3, y1+3, "#000", 1, ctx);
//    drawLine(x1-3, y1+3, x1+3, y1-3, "#000", 1, ctx);
    this.ctx.beginPath();
    this.ctx.arc(x1, y1, this.pointSize, 0, 2 * Math.PI);
    this.ctx.fillStyle = color;
    this.ctx.fill();
    if (point[2] != "") {
//        this.drawText2(x1+3, y1-10, point[2], color);
        this.drawText3(x1, y1, point[2], color, this.textSize, point[4]); // text, color, textSize, position 1 - 8
    }
}

//vykresleni cary mezi body [x1, y1] a [x2, y2]; opt. mozno zadat barvu a sirku cary
SPlayer.prototype.drawLine = function(x1, y1, x2, y2, color, width, dashed) {
    if (color == undefined) color = "#000";
    if (width == undefined) width = 1;
    if (dashed == undefined) dashed = false;
    
    this.ctx.strokeStyle = color;
    this.ctx.lineWidth = width;
    
    if (dashed) {
        
//        console.log("[" + x1 + ", " + y1 + "], [" + x2 + "," + y2 +"]");
        
        var dashDiagonalLength = 10;
        var dashSpace = 3;
        
        var actLength = dashDiagonalLength;
        
//        var fromX1 = x1;
//        var fromY1 = y1;
        var toX2 = 0;
        var toY2 = 0;
        var maxX = Math.max(x1, x2);
        
        var cdl = Math.sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1)); // celkova delka usecky
        var m = (y2 - y1) / (x2 - x1);
        
        var fromX1 = maxX;
        var fromY1 = m * (fromX1 - x1) + y1;
        
        while (actLength < cdl) {
            toX2 = maxX - ( (Math.abs(x2 - x1)) / cdl ) * actLength;
            toY2 = m * (toX2 - x1) + y1;
            this.ctx.beginPath()
            this.ctx.moveTo(fromX1, fromY1);
            this.ctx.lineTo(toX2, toY2);
            this.ctx.stroke();
//            console.log("[" + fromX1 + ", " + fromY1 + "], [" + toX2 + "," + toY2 +"]");
            actLength += dashSpace;
            fromX1 = maxX - ( (Math.abs(x2 - x1)) / cdl ) * actLength;
            fromY1 = m * (fromX1 - x1) + y1;
            
            actLength += dashDiagonalLength;
        }
    } else {
        
        this.ctx.beginPath()
        this.ctx.moveTo(x1, y1);
        this.ctx.lineTo(x2, y2);
        this.ctx.stroke();
    }
}

SPlayer.prototype.initTextArea = function() {
    if (this.displayTextArea) {
        this.textArea = document.createElement("div");
//    this.textArea.setAttribute("style", "width: " + this.width + "px;");
        this.textArea.setAttribute("class", "textArea");
        this.targetContainer.appendChild(this.textArea);
    }
}

SPlayer.prototype.getActTextID = function() {
    return this.position;
}

SPlayer.prototype.actText = function() {
    if (this.displayTextArea) {
        var textAreaID = this.getActTextID();
        if (textAreaID != this.actTextID) {
            this.actTextID = textAreaID;
            this.actTextByID(textAreaID);
        }
    }
}

SPlayer.prototype.actTextByID = function(id) {
    if (id == null || id == undefined) {
        this.textArea.innerHTML = "";
    } else {
        this.textArea.innerHTML = this.dataArray[id][4];
        
        if (typeof MathJax != "undefined") {
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.textArea]);
        }
    }
}

//SPlayer.prototype.drawText2 = function(x, y, text, color, size) {
//    if (color == undefined) color = "#000";
//    if (size == undefined) size = 20;
//    
//    console.log(x, text);
//    if (text.indexOf("_") == -1) {
//        this.ctx.fillStyle = color;
//        this.ctx.font = "italic " + size +"px serif";
//        this.ctx.fillText(text, x, y);
//        return this.ctx.measureText(text).width;
//    } else {
//        var preText = text.substring(0, text.indexOf("_"));
//        var widthOfText = this.drawText2(x, y, preText, color, size);
//        var indx = text.substring(text.indexOf("_") + 1, text.indexOf("_") + 2);
//        var widthOfIndex = this.drawText2(x+widthOfText, y+5, indx, color, size * 0.7);
//        if (text.length >= text.indexOf("_") + 2) {
//            var aftText = text.substring(text.indexOf("_") + 2);
//            this.drawText2(x + widthOfText + widthOfIndex, y, aftText, color, size);
//        }
//    }
//    
//}

SPlayer.prototype.drawText3 = function(x, y, text, color, size, position) {
    if (color == undefined) color = "#000";
    if (size == undefined) size = this.textSize;
    if (position == undefined) position = 1;
    
//    console.log(x, text);
    this.label = new textLabel(x,y,color,position);
    this.processLabel(this.label, text, size, 0); // posun ve smeru +y pro index
    this.drawLabel(this.label);
//    if (text.indexOf("_") == -1) {
//        this.ctx.fillStyle = color;
//        this.ctx.font = "italic " + size +"px serif";
//        this.ctx.fillText(text, x, y);
//        return this.ctx.measureText(text).width;
//    } else {
//        var preText = text.substring(0, text.indexOf("_"));
//        var widthOfText = this.drawText2(x, y, preText, color, size);
//        var indx = text.substring(text.indexOf("_") + 1, text.indexOf("_") + 2);
//        var widthOfIndex = this.drawText2(x+widthOfText, y+5, indx, color, size * 0.7);
//        if (text.length >= text.indexOf("_") + 2) {
//            var aftText = text.substring(text.indexOf("_") + 2);
//            this.drawText2(x + widthOfText + widthOfIndex, y, aftText, color, size);
//        }
//    }
    
}
SPlayer.prototype.processLabel = function(label, text, size, indexY) {
    if (text.length == 0) {
        return;
    }
    if (text.indexOf("_") == -1) {
        this.ctx.fillStyle = label.color;
        this.ctx.font = "italic " + size +"px serif";
        var width = this.ctx.measureText(text).width;
        
        var part = new textPart(label.width, indexY, text, size, width, size)
        label.width += width; // celkova sirka += sirka casti
        if (label.height == 0) {
            label.height = size;
        } else if (label.height < indexY + label.height) { // size -> label.height ???
            label.height = indexY + label.height;
        }
        label.parts.push(part);
        return;
    }
    else {
        var preText = text.substring(0, text.indexOf("_{")); // pred indexem
        if (preText.length > 0) {
//            console.log("pretext", preText);
            this.processLabel(label, preText, size, indexY);
            
//            var width = this.ctx.measureText(preText).width;
//            var part = new textPart(label.width, indexY, preText, size, width, size)
//            label.width += width; // celkova sirka += sirka casti
//            if (label.height < indexY + size) {
//                label.height = indexY + size;
//            }
        }
        var inStartIndex = text.indexOf("_{") + 2;
        var inEndIndex = text.indexOf("_{") + 2 + $findClosingIndex(text.substr(text.indexOf("_{") + 2), 1, "_{" , "}");
        var inText = text.substring(inStartIndex, inEndIndex);
        if (inText.length > 0) {
//            console.log("intext", inText);
            this.processLabel(label, inText, size * this.textIndexCoeficient, indexY + size * this.textYposCoeficient);
        }
        var afterText = text.substr(inEndIndex + 1);
        if (afterText.length > 0) {
//            console.log("aftertext", afterText);
            this.processLabel(label, afterText, size, indexY);
        }
    }
}
SPlayer.prototype.drawLabel = function(label) {
//    console.log(label);
    switch(label.position) {
        case 1:
            label.x += this.textSpace;
            label.y -= this.textSpace;
            break;
        case 2:
            label.x += this.textSpace;
            label.y += label.height / 2;
            break;
        case 3:
            label.x += this.textSpace;
            label.y += this.textSpace + label.height;
            break;
        case 4:
            label.x -= label.width / 2;
            label.y += this.textSpace + label.height;
            break;
        case 5:
            label.x -= (this.textSpace + label.width);
            label.y += this.textSpace + label.height;
            break;
        case 6:
            label.x -= (this.textSpace + label.width);
            label.y += label.height / 2;
            break;
        case 7:
            label.x -= (this.textSpace + label.width);
            label.y -= this.textSpace;
            break;
        case 8:
            label.x -= label.width / 2;
            label.y -= this.textSpace;
            break;
    }
    var part;
    for (var i = 0; i < label.parts.length; i++) {
        part = label.parts[i];
        this.drawPart(part.text, label.x+part.x, label.y+part.y, label.color, part.size);
    }
}

SPlayer.prototype.drawPart = function(text,x,y,color,size) {
    this.ctx.fillStyle = color;
    this.ctx.font = "italic " + size +"px serif";
    this.ctx.fillText(text, x, y);
}

textLabel = function(x,y,color,position) {
    this.x = x;
    this.y = y;
    this.color = color;
    this.position = position;
    this.width = 0;
    this.height = 0;
    this.parts = [];
}

// class; x,y relative to parent
textPart = function(x,y,text,size,width,height) {
    this.x = x;
    this.y = y;
    this.text = text;
    this.size = size;
    this.width = width;
    this.height = height;
}

// p_{10_{5}}
function $findClosingIndex(text, opened, openSequence, searchSequence) {
    var opSeq = opened; // how many opened seq
    var stub, searchSeq, openSeq, lastClosing;
    for (var i = 0; i < text.length; ) {
        lastClosing = i;
        stub = text.substr(i);
        searchSeq = stub.indexOf(searchSequence);
        openSeq = stub.indexOf(openSequence);
//        console.log(stub, opSeq, searchSeq, openSeq, i);
        
        if (searchSeq == -1) {
            return i;
        }
        
        if (openSeq > -1 && openSeq < searchSeq) { //_{}
            opSeq++;
            i += openSeq + openSequence.length;
        } else { // xxx}_{
            opSeq--;
            i += searchSeq + searchSequence.length;
        }
        
        if (opSeq == 0) {
            return i - searchSequence.length;
        }
    }
}