
// constants

var BASE_IMAGE_URL = "/img/notes/";

var NOTES_STAFF_X_OFFSET = 0;
var NOTES_DISTANCE_BETWEEN_NOTES = 10;

var NOTES_BACKGROUND_COLOR = '#e8e8e8';

var NOTES_DEFAULT_COLOR = '#000000';
var NOTES_DEFAULT_STROKE = 1;

var STAFF_COLOR = "#646464";

// five configuration
var NOTES_STAFF_TOP_MARGIN = 10;
var NOTES_STAFF_BOTTOM_MARGIN = 30;
var NOTES_STAFF_SPACE_BETWEEN_LINES = 20;

var NOTES_STAFF_SPACER_SIZE = 6;
var NOTES_EXTRA_TOP_LINES = 6;
var NOTES_EXTRA_BOTTOM_LINES = 3;
var NOTES_NUMBER_OF_LINES = 5;
var NOTES_TOTAL_NUMBER_OF_LINES = NOTES_NUMBER_OF_LINES + NOTES_EXTRA_BOTTOM_LINES + NOTES_EXTRA_TOP_LINES;
var NOTES_STAFF_HEIGHT = NOTES_STAFF_SPACER_SIZE * (NOTES_NUMBER_OF_LINES - 1);
var NOTES_STAFF_Y = NOTES_EXTRA_TOP_LINES * NOTES_STAFF_SPACER_SIZE + NOTES_STAFF_TOP_MARGIN;
var NOTES_STAFF_TOTAL_HEIGHT = ((NOTES_TOTAL_NUMBER_OF_LINES - 1)* NOTES_STAFF_SPACER_SIZE) + NOTES_STAFF_TOP_MARGIN + NOTES_STAFF_BOTTOM_MARGIN;

var DISTANCE_BETWEEN_NOTES = 24;
var ALTERL_PIXEL_WIDTH = 6;
var TIME_PIXEL_WIDTH = DISTANCE_BETWEEN_NOTES;
var CLEF_PIXEL_WIDTH = 20;
var KEY_PIXEL_WIDTH = ALTERL_PIXEL_WIDTH + 1;
var MINIMUM_MEASURE_WIDTH = Math.floor(DISTANCE_BETWEEN_NOTES * 3);
var REST_WHOLE_OFFSET = MINIMUM_MEASURE_WIDTH / 2 - 4;

var SEPARATOR_HEIGHT = 1;
var SEPARATOR_COLOR =   '#A0A0A0';


var MEASURE_INDEX_HEIGHT = 8;
var MEASURE_INDEX_TEXT_HEIGHT = 8;
var MEASURE_INDEX_COLOR = "#0000FF";

var REPEAT_TEXT_HEIGHT = 8;
var REPEAT_COLOR = "#000000";


// beat configuration
var NOTES_BEAT_FONT_SIZE = 16;
var NOTES_BEAT_FONT_STYLE = Font.ITALIC_BOLD;

var NOTE_SHARP = 0x100;
var INVALID_FRET = 63;


// TAB configuration
var TAB_BACKGROUND_COLOR = '#a5a5a5';

var TAB_FONT_HEIGHT = 11;
var TAB_FONT_WIDTH = TAB_FONT_HEIGHT * 0.6; // this is an impiric number
var TAB_FONT_STYLE = Font.ITALIC_BOLD;
//var TAB_Y_IMPIRIC_OFFSET = -(TAB_FONT_HEIGHT);
var TAB_Y_IMPIRIC_OFFSET =  -(6);

var TAB_NUMBER_OF_STRINGS = 6;
var TAB_STAFF_SPACER_SIZE = 10;
var TAB_STAFF_TOP_MARGIN = 25;
var TAB_STAFF_BOTTOM_MARGIN = 10;
var TAB_TYPE_FULL_HEIGHT = 18;
var TAB_TYPE_HALF_HEIGHT = 9;
var TAB_SINGLE_NOTE_BEAM_LENGTH = 8;
var TAB_STAFF_Y = TAB_STAFF_TOP_MARGIN;

// Lyrics
var LYRICS_FONT_SIZE = 10;
var LYRICS_FONT_WIDTH = LYRICS_FONT_SIZE * 0.6;
var LYRICS_FONT_STYLE = Font.PLAIN;
var LYRICS_MARGIN = 2;
var LYRICS_DIV_HEIGHT = LYRICS_FONT_SIZE + LYRICS_MARGIN * 2;
var LYRICS_COLOR =   '#000000';


var _drawForPrint = false;


var _numberOfStrings = TAB_NUMBER_OF_STRINGS;

var _tabTypeY;
var _tabStaffTotalHeight;
var _tabStaffHeight;

function calcTabStaffHeightParams(){

          _tabStaffHeight = TAB_STAFF_SPACER_SIZE * (_numberOfStrings - 1) ;
          _tabTypeY = TAB_STAFF_TOP_MARGIN + _tabStaffHeight + 7;
          var tabTypeTotalHeight = TAB_TYPE_FULL_HEIGHT  + 10;
          _tabStaffTotalHeight = _tabStaffHeight + TAB_STAFF_TOP_MARGIN + TAB_STAFF_BOTTOM_MARGIN + tabTypeTotalHeight;
}


var CHORDS_DIV_HEIGHT = 30;




var TYPE_64TH = 2;
var TYPE_32ND = 3;
var TYPE_16TH = 4;
var TYPE_08TH = 5;
var TYPE_QUARTER = 6;
var TYPE_HALF = 7;
var TYPE_WHOLE = 8;


// stem
var STEM_UNDEFINED = -2;
var STEM_DEFAULT = -1;
var STEM_UP = 0;
var STEM_DOWN = 1;
var STEM_NONE = 2;
var STEM_DOUBLE = 3;




var _currentStem = STEM_DEFAULT;
var _oldStem = _currentStem;

var _parent = 0;
var _areaWidth = 0;
    
var _currentLine = -1;
var _currentType = TYPE_WHOLE;
var _currentVirtualType = _currentType;
var _currentDot = false;
var _stepsWithSharp = new Array(1,1,0,1,1,1,0);
var _tabNotes = null;
var _insideChord = false;
var _key = new Array(0,0,0,0,0,0,0); // minus(-) number for flats, positive numbers for sharps. 0 for natural
var _keyRole = 0; // -1 for flat, +1 for sharp, 0 for natural
var _drawnAltersForTheCurrentMeasure = new Array(0,0,0,0,0,0,0);

var _chordsVisible = true;
var _lyricsVisible = true;
var _notesVisible = true;
var _tabVisible = true;
var _guitarNeckVisible = true;


function graphic(index){

          this._chords =  new jsGraphics("chords" + index);
          //this._lyrics = new jsGraphics("lyrics" + index);
          this._notes =  new jsGraphics("notes" + index);
          this._tab = new jsGraphics("tab" + index);
          this._separator = new jsGraphics("separator" + index);
          this._measureIndex = new jsGraphics("measureIndex" + index);

          if(_drawForPrint){

                    this._chords.setPrintable(_drawForPrint);
             //       this._lyrics.setPrintable(_drawForPrint);
                    this._notes.setPrintable(_drawForPrint);
                    this._tab.setPrintable(_drawForPrint);
                    this._separator.setPrintable(_drawForPrint);
                    this._measureIndex.setPrintable(_drawForPrint);
          }

          this._currentX = 0;
          this._minNextMeasureX = 0;
/*
          // set to lyrics font size
          this._lyrics.setFontSize(LYRICS_FONT_SIZE + 'px');
          this._lyrics.setFontStyle(LYRICS_FONT_STYLE);
          this._lyrics.setColor(LYRICS_COLOR);
*/

          this.clear = function(){
                    
                    if(this._chords != null){
                              this._chords.clear();
                    }
                    /*
                    if(this._lyrics != null){
                              this._lyrics.clear();
                    }
                    */
                    
                    if(this._notes != null){
                              this._notes.clear();
                    }
                    
                    if(this._tab != null){
                              this._tab.clear();
                    }
                    
                    if(this._separator != null){
                              this._separator.clear();
                    }

                    if(this._measureIndex != null){
                              this._measureIndex.clear();
                    }
          }

          this.paint = function(){

                    if(this._chords != null){

                              this._chords.paint();
                    }
/*
                    if(this._lyrics != null){

                              this._lyrics.paint();
                    }
*/
                    if(this._notes != null){

                              this._notes.paint();
                    }

                    if(this._tab != null){

                              this. _tab.paint();
                    }

                    if(this._separator != null){

                              this._separator.paint();
                    }

                    if(this._measureIndex != null){

                              this._measureIndex.paint();
                    }
          }
}

var _currentGraphic = null;

var _currentTabDiv;
//var _currentLyricsDiv;
var _currentNotesDiv;
var _currentChordsDiv;
var _currentSeparatorDiv;
var _currentMeasureIndexDiv;

var _distanceBetweenNotes = DISTANCE_BETWEEN_NOTES;

// BEAM Handling
var BEAM_BEGIN = 0;
var BEAM_CONTINUE = 1;
var BEAM_END = 2;
var BEAM_LINE_WIDTH = 2;

var _insideBeam = false;
var _beamStartX;
var _beamEndX;



/*
 *
 *        SLIDE
 *
 */
var SLIDES_ARRAY_SIZE = 10;
var INVALID_INDEX = -1;

var SLIDE_TYPE_START = 1;
var SLIDE_TYPE_STOP = 2;

var SLIDE_DIRECTION_UP = 0;
var SLIDE_DIRECTION_DOWN = 1;
var SLIDE_DIRECTION_HORIZON = 2;

function Slide(){

          this._tabX = 0;
          this._tabY = 0;
          this._direction = SLIDE_DIRECTION_HORIZON;
          this._tabTextLength = 0;
          this._hasEnd = 1;
          this._fret = INVALID_FRET;
          this._open = false;
}


var _tempSlide = null;
var _slides = null;

function initSlidesArray(){

          var i;

          var array = new Array(SLIDES_ARRAY_SIZE);

          for(i = 0; i < SLIDES_ARRAY_SIZE; i++){

                    array[i] = new Slide();
          }

          return array;
}

function init_slide(){

          _slides = initSlidesArray();
          _tempSlide = initSlidesArray();
}


function draw_slide(slideFrom, slideTo){

          var tabX1 = slideFrom._tabX;
          var tabY1 = slideFrom._tabY;
          var tabX2 = slideTo._tabX;
          var tabY2 = slideTo._tabY;

          var ty1;
          var ty2;

          var tabTextLengthPixels = slideFrom._tabTextLength * TAB_FONT_WIDTH;

          switch(slideTo._direction){

                    case SLIDE_DIRECTION_UP:
                              ty2 = tabY2 + 2;
                              ty1 = tabY1 + TAB_FONT_HEIGHT - 2;
                              break;

                    case SLIDE_DIRECTION_DOWN:
                              ty2 = tabY1 + TAB_FONT_HEIGHT - 2;
                              ty1 = tabY2 + 2;
                              break;

                    case SLIDE_DIRECTION_HORIZON:
                              ty1 = ty2 = tabY2 + 3;
                              break;
          }

          _currentGraphic._tab.drawLine(tabX1 + tabTextLengthPixels, ty1, tabX2 - 2 ,  ty2);
         
}


function copy_slide(source, dest){

          dest ._tabX = source._tabX;
          dest._tabY = source._tabY;
          dest._direction = source._direction;
          dest._tabTextLength = source._tabTextLength;
          dest._hasEnd = source._hasEnd;
}

function handle_slide(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
          var type = getValue(packedElement,2,6,7);
          var number = getValue(packedElement,2,8,11);
          var hasEnd = getValue(packedElement,2,12,12);

          if(type == SLIDE_TYPE_START){

                    if(hasEnd){

                              copy_slide(_tempSlide[number], _slides[number]);
                              _slides[number]._open = true;

                    } else {

                              var slide = new Slide();
                              _tempSlide[number]._tabX += 3;
                              copy_slide(_tempSlide[number], slide);
                              slide._hasEnd = false;
                              slide._tabX += 16;
                              slide._direction = SLIDE_DIRECTION_HORIZON;
                              draw_slide(_tempSlide[number], slide);
                    }

          } else {

                    draw_slide(_slides[number], _tempSlide[number]);
                    _slides[number]._open = false;
          }

          return (offset + 2);
}


function flush_slides(){

          var i;
          var slide1;
          var slide2 = new Slide();

          for(i = 0; i < SLIDES_ARRAY_SIZE; i++){

                    slide1 = _slides[i];
                    if(slide1._open){

                              copy_slide(slide1, slide2);
                              slide2._tabX += 30;

                              draw_slide(slide1, slide2);
                              slide1._tabX = 3;
                              slide1._tabTextLength = 0;
                              slide1._open = false;
                    }
          }
}



function Slur(){

          this._noteX = 0;
          this._noteY = 0;
          this._noteStem = STEM_UP;
          this._tabX = 0;
          this._tabY = 0;
          this._tabStem = STEM_UP;
          this._open = false;
}

var SLURS_ARRAY_SIZE = 15;

var SLUR_TYPE_START = 1;
var SLUR_TYPE_STOP = 2;
var SLUR_TYPE_CONTINUE = 3;

var _tempSlurData = null;
var _slurs = null;

function init_slurs_array(){

          var i;

          var array = new Array(SLURS_ARRAY_SIZE);

          for(i = 0; i < SLURS_ARRAY_SIZE; i++){

                    array[i] = new Slur();
          }

          return array;
}

function init_slurs(){

          _slurs = init_slurs_array();
          _tempSlurData = new Slur();
}

var _forceSlurDown = 0;

function draw_slur(slurFrom, slurTo){

          var noteX1 = slurFrom._noteX;
          var noteY1 = slurFrom._noteY;
          var tabX1 = slurFrom._tabX;
          var tabY1 = slurFrom._tabY;
          var noteX2 = slurTo._noteX;
          var tabX2 = slurTo._tabX;
          var noteStem = slurFrom._noteStem;
          var image;
          var noteOffsetImageY = 0;
          var tabOffsetImageY;
          var widthNote;
          var widthTab;

          widthNote = noteX2 - noteX1 - 12;
          widthTab = tabX2 - tabX1 - 6;

          if(noteStem == STEM_UP || _forceSlurDown > 0){
                    _forceSlurDown--;

                    image = BASE_IMAGE_URL + "slur_down";
                   
                    noteOffsetImageY = 10;
                    tabOffsetImageY = TAB_FONT_HEIGHT;

          } else {
                    
                    image = BASE_IMAGE_URL + "slur_up";
                    tabOffsetImageY = 0;
          }

          if(widthNote > 50){
                    image += "_long";
          }

          image += ".gif";

          _currentGraphic._notes.drawImage(image, noteX1 + 9, noteY1 + noteOffsetImageY, widthNote ,  10);

          _currentGraphic._tab.drawImage(image, tabX1, tabY1 + tabOffsetImageY, widthTab ,  10);
}

function copy_slur(source, dest){

          dest ._noteX = source._noteX;
          dest._noteY = source._noteY;
          dest ._tabX = source._tabX;
          dest._tabY = source._tabY;
          dest ._noteStem = source._noteStem;
          dest._open = source._open;
}

function handle_slur(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
          var number = getValue(packedElement,2,6,11);
          var type = getValue(packedElement,2,12,13);

          switch(type){

                    case SLUR_TYPE_START:
                              copy_slur(_tempSlurData, _slurs[number] );
                              _slurs[number] ._open = true;
                              break;

                    case SLUR_TYPE_STOP:
                              draw_slur(_slurs[number], _tempSlurData);
                              copy_slur(_tempSlurData, _slurs[number] );
                              _slurs[number] ._open = false;
                              break;

                    case SLUR_TYPE_CONTINUE:
                              // NOT SUPPORTED
                              return -1
                              break;
          }

          return (offset + 2);
}


function flush_slurs(){

          var i;
          var slur1;
          var slur2 = new Slur();

          for(i = 0; i < SLURS_ARRAY_SIZE; i++){

                    slur1 = _slurs[i];
                    if(slur1._open){

                              copy_slur(slur1, slur2);
                              slur2._noteX += 30;
                              slur2._tabX += 30;

                              draw_slur(slur1, slur2);
                              slur1._open = false;
                    }
          }
}


// text handling
function Text(value){
          this._value = value;
}

_text = new Array();

function handle_Text(data, offset){

          var index = getValue2(data, offset, 6, 15);
          var length = getValue2(data, offset, 16, 23);

          var from = offset + 3;
          var to = from + length - 1;
          var utftext = new Array();
          var arrayIndex;
          var i;
          
          for(i = from , arrayIndex = 0; i <= to; i++, arrayIndex++){

                    utftext[arrayIndex] = data[i];
          }

          _text[index] = decode_utf8 (utftext);

          return (offset + 3 + length);
}


//var _lyricsExists = false;

function handle_Lyrics(data, offset){

          var packedElement = getReadInt24FromBuffer(data, offset);
          var textIndex = getValue(packedElement,3,6,21);
/*
          _lyricsExists = true;
          
          var words  = _text[textIndex];
          _currentGraphic._lyrics.drawString(words, _currentGraphic._currentX, LYRICS_MARGIN);
*/
          return (offset + 3);
}


var _bendAlter = 0;
var _release = 0;
var _preBend = 0;
var _bendImagePath = BASE_IMAGE_URL + "bend.gif";
var _preBandImagePath = BASE_IMAGE_URL + "pre-bend.gif";
var _bendReleaseImagePath = BASE_IMAGE_URL + "bend_release.gif";

var BEND_QUARTER = "&#188;"
var BEND_HALF = "&#189;"
var BEND_THREE_QUARTERS = "&#190;"
var BEND_FULL = "full";

function handle_bend(data, offset){
          
          var packedElement = getReadInt16FromBuffer(data, offset);
          _bendAlter = getValue(packedElement,2,6,11) * 0.25;
          _release = getValue(packedElement,2,12,12);
          _preBend = getValue(packedElement,2,13,13);

          if(_bendAlter > 0 || _preBend == 1){
                    
                    _forceSlurDown++;
          }

          return (offset + 2);
}

function resetBend(){

          _bendAlter = 0;
          _release = 0;
          _preBend = 0;
}


var NOTEHEAD_NORMAL = 1;
var NOTEHEAD_X = 2;
var NOTEHEAD_SLASH = 3;
var NOTEHEAD_TRIANGLE = 4;
var NOTEHEAD_DIAMOND = 5;
var NOTEHEAD_SQUARE = 6;
var NOTEHEAD_CROSS = 7;
var NOTEHEAD_CIRCLE_X = 8;
var NOTEHEAD_INVERTED_TRIANGLE = 9;
var NOTEHEAD_ARROW_DOWN = 10;
var NOTEHEAD_ARROW_UP = 11;
var NOTEHEAD_SLASHED = 12;
var NOTEHEAD_BACK_SLASHED = 13;
var NOTEHEAD_CLUSTER = 14;
var NOTEHEAD_NONE = 15;

var _currentNoteHead = NOTEHEAD_NORMAL;

function handle_noteHead(data, offset){

          var packedElement = getReadInt16FromBuffer(data, offset);
          
          var value = getValue(packedElement,2,6,9);

          switch(value){

                    case NOTEHEAD_X:
                              _currentNoteHead = NOTEHEAD_X;
                              break;

                    default:
                              _currentNoteHead = NOTEHEAD_NORMAL;
                              break;
          }

          return (offset + 2);
}


function drawDivBackground (jg, div, color) {

          saveGraphicState(jg);

          jg.setColor(color);
    
          jg.fillRect(0, 0, div.offsetWidth, div.offsetHeight);
    
          restoreGraphicState(jg);
}
 
    
function drawStaff (jg, y, width, numberOfLines, lineSpace){
   
          var count;

          saveGraphicState(jg);

          jg.setColor(STAFF_COLOR);

          for(count = 0; count < numberOfLines; count++, y += lineSpace){
    
                    jg.drawLine(0, y, width, y);
          }

          restoreGraphicState(jg);
}

function drawSeparator (width){

          saveGraphicState(_currentGraphic._separator);
          
          _currentGraphic._separator.setColor(SEPARATOR_COLOR);
          _currentGraphic._separator.drawLine(0, 0, width, 0);

          restoreGraphicState(_currentGraphic._separator);
}


function handle_version(data, offset) {

          var packedElement = getReadInt32FromBuffer(data, offset);
          var major = getValue(packedElement,4,8,15);
          var minor = getValue(packedElement,4,16,23);
          var minorMinor = getValue(packedElement,4,24,31);

          if(major != 1 || minor != 1 || minorMinor != 1){
      
                    return -1;
          }
        
          return (offset + 4);
}

var CLEF_G = 0;
var CLEF_F = 1;
var CLEF_TAB = 3;

var _clef_note_offset = 0;
var _clef = CLEF_G;

function setClef(clef){

          _clef = clef;

          switch(_clef){

                    case CLEF_TAB:
                    case CLEF_G:
                              _clef_note_offset = 0;
                              break;

                    case CLEF_F:
                              _clef_note_offset = 12;
                              break;
          }
}

function handle_clef(data, offset) {

          //*** ignore the clef-sign, because it set by the lowest octave while parsing guitar tuning
          //var packedElement = getReadInt16FromBuffer(data, offset);
          // var clef = getValue(packedElement,2,6,8); 

          switch(_clef){
        
                    case CLEF_G:
                              _currentGraphic._notes.drawImage(BASE_IMAGE_URL + "clef_g.gif", _currentGraphic._currentX , NOTES_STAFF_Y - 8);
                              break;

                    case CLEF_F:
                              _currentGraphic._notes.drawImage(BASE_IMAGE_URL + "clef_f.gif", _currentGraphic._currentX , NOTES_STAFF_Y - 4);
                              //                              _currentGraphic._currentX += CLEF_PIXEL_WIDTH;
                              break;
          }

          _currentGraphic._tab.drawImage(BASE_IMAGE_URL + "clef_tab.gif", _currentGraphic._currentX , TAB_STAFF_TOP_MARGIN + 3, 15, _tabStaffHeight - 3);
          _currentGraphic._currentX += CLEF_PIXEL_WIDTH;

          //   _currentGraphic._minNextMeasureX += CLEF_PIXEL_WIDTH;

          return (offset + 2);
}


function handle_key(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
          var type = getValue(packedElement,2,6,6);
          var fifths = getValue(packedElement,2,7,9);
          var mode = getValue(packedElement,2,10,13);

          _keyRole = (type == 1)? (-1):1;
    
          switch(fifths){
        
                    case 0:
                              _key = new Array(0,0,0,0,0,0,0);
                              break;

                    case 1:
                              _key = new Array(0,0,0,_keyRole,0,0,0);
                              break;
            
                    case 2:
                              _key = new Array(_keyRole,0,0,_keyRole,0,0,0);
                              break;
            
                    case 3:
                              _key = new Array(_keyRole,0,0,_keyRole,_keyRole,0,0);
                              break;
            
                    case 4:
                              _key = new Array(_keyRole,_keyRole,0,_keyRole,_keyRole,0,0);
                              break;
            
                    case 5:
                              _key = new Array(_keyRole,_keyRole,0,_keyRole,_keyRole,_keyRole,0);
                              break;
            
                    case 6:
                              _key = new Array(_keyRole,_keyRole,_keyRole,_keyRole,_keyRole,_keyRole,0);
                              break;
            
                    case 7:
                              _key = new Array(_keyRole,_keyRole,_keyRole,_keyRole,_keyRole,_keyRole,_keyRole);
                              break;
          }

          _currentGraphic._currentX += KEY_PIXEL_WIDTH * fifths;

          return (offset + 2);
}


function handle_time(data, offset) {

          var packedElement = getReadInt24FromBuffer(data, offset);
          var beats = getValue(packedElement,3,6,12);
          var beatsType = getValue(packedElement,3,13,17);

          saveGraphicState(_currentGraphic._notes);
        
          var y = NOTES_STAFF_TOP_MARGIN + NOTES_STAFF_SPACER_SIZE * NOTES_EXTRA_TOP_LINES - NOTES_BEAT_FONT_SIZE / 2;
        
          _currentGraphic._notes.setFontSize(NOTES_BEAT_FONT_SIZE + 'px');
          _currentGraphic._notes.setFontStyle(NOTES_BEAT_FONT_STYLE);
    
          _currentGraphic._notes.drawString(beats, _currentGraphic._currentX, y);
          _currentGraphic._notes.drawString(beatsType, _currentGraphic._currentX, y + NOTES_BEAT_FONT_SIZE);
    
          restoreGraphicState(_currentGraphic._notes);

          _currentGraphic._currentX += _distanceBetweenNotes;
          _currentGraphic._minNextMeasureX += _distanceBetweenNotes;
    
          return (offset + 3);
}


function MeasureEx(leftBarStyle, leftEndingType,
          leftEndingNumber, leftRepeatTimes,
          rightBarStyle, rightEndingType,
          rightEndingNumber, rightRepeatTimes) {

          this._leftBarStyle = leftBarStyle;
          this._leftEndingType = leftEndingType;
          this._leftEndingNumber = leftEndingNumber;
          this._leftRepeatTimes = leftRepeatTimes;
          this._rightBarStyle = rightBarStyle;
          this._rightEndingType = rightEndingType;
          this._rightEndingNumber = rightEndingNumber;
          this._rightRepeatTimes = rightRepeatTimes;
}

var _currentMeasureEx = null;
var _currentMeasureIndex = 0;

function draw_measure_index(){


          if(_currentGraphic != null){
                    
                    _currentGraphic._measureIndex.setFontSize(MEASURE_INDEX_TEXT_HEIGHT + 'px');
                    _currentGraphic._measureIndex.setColor(MEASURE_INDEX_COLOR);

                    _currentMeasureIndex++;
                    _currentGraphic._measureIndex.drawString("" + _currentMeasureIndex, _currentGraphic._currentX, 0);
          }
}

function handle_measure(data, offset) {

          var needToDraw = true;

          if(_currentGraphic._currentX < _currentGraphic._minNextMeasureX){

                    _currentGraphic._currentX = _currentGraphic._minNextMeasureX;
          }

          if(_currentMeasureEx != null){

                    draw_measureEx(false);
                    needToDraw = false;
                    _currentMeasureEx = null;
          }

          draw_measure_index();

          _drawnAltersForTheCurrentMeasure = new Array(0,0,0,0,0,0,0);

          if(needToDraw){

                    _currentGraphic._notes.drawLine(_currentGraphic._currentX, NOTES_STAFF_Y, _currentGraphic._currentX, NOTES_STAFF_Y + NOTES_STAFF_SPACER_SIZE * (NOTES_NUMBER_OF_LINES-1));
                    _currentGraphic._tab.drawLine(_currentGraphic._currentX, TAB_STAFF_Y, _currentGraphic._currentX, TAB_STAFF_Y + TAB_STAFF_SPACER_SIZE * (_numberOfStrings-1));
          }

          _currentGraphic._minNextMeasureX = _currentGraphic._currentX + MINIMUM_MEASURE_WIDTH;
          _currentGraphic._currentX += _distanceBetweenNotes / 2;


          return (offset + 1);
}

function flushMeasureEx(){

          if(_currentMeasureEx != null){

                    if(_currentGraphic._currentX < _currentGraphic._minNextMeasureX){

                              _currentGraphic._currentX = _currentGraphic._minNextMeasureX;
                    }
          
                    draw_measureEx(false);
                    _currentMeasureEx = null;

                    return true;
          }

          return false;
}

function draw_barLine(thickness, extraXOffset){

          var x = _currentGraphic._currentX + extraXOffset;

          saveGraphicState(_currentGraphic._notes);
          _currentGraphic._notes.setStroke(thickness);
          _currentGraphic._notes.drawLine(x, NOTES_STAFF_Y, x, NOTES_STAFF_Y + NOTES_STAFF_SPACER_SIZE * (NOTES_NUMBER_OF_LINES-1));
          restoreGraphicState(_currentGraphic._notes);

          saveGraphicState(_currentGraphic._tab);
          _currentGraphic._tab.setStroke(thickness);
          _currentGraphic._tab.drawLine(x, TAB_STAFF_Y, x, TAB_STAFF_Y + TAB_STAFF_SPACER_SIZE * (_numberOfStrings-1));
          restoreGraphicState(_currentGraphic._tab);
}


function draw_barRepeat(extraXOffset, repeatTimes){

          var mid;
          var x = _currentGraphic._currentX + extraXOffset;

          saveGraphicState(_currentGraphic._notes);

          if(repeatTimes > 2){
                    
                    _currentGraphic._notes.setFontSize(REPEAT_TEXT_HEIGHT + 'px');
                    _currentGraphic._notes.setColor(REPEAT_COLOR);
                    _currentGraphic._notes.drawString("x" + repeatTimes, x - 6, NOTES_STAFF_Y - REPEAT_TEXT_HEIGHT * 2);

                    _currentGraphic._tab.setFontSize(REPEAT_TEXT_HEIGHT + 'px');
                    _currentGraphic._tab.setColor(REPEAT_COLOR);
                    _currentGraphic._tab.drawString("x" + repeatTimes, x - 6, TAB_STAFF_Y - REPEAT_TEXT_HEIGHT * 2);
          }


          _currentGraphic._notes.setStroke(2);
          mid = NOTES_STAFF_Y + (NOTES_STAFF_SPACER_SIZE * (NOTES_NUMBER_OF_LINES-1) / 2);
          _currentGraphic._notes.drawLine(x, mid - 3, x, mid - 3);
          _currentGraphic._notes.drawLine(x, mid + 3, x, mid + 3);
          restoreGraphicState(_currentGraphic._notes);

          saveGraphicState(_currentGraphic._tab);
          _currentGraphic._tab.setStroke(2);
          mid = TAB_STAFF_Y + (TAB_STAFF_SPACER_SIZE * (_numberOfStrings-1)) / 2;
          _currentGraphic._tab.drawLine(x, mid - 8, x, mid - 8);
          _currentGraphic._tab.drawLine(x, mid + 8, x, mid + 8);
          restoreGraphicState(_currentGraphic._tab);
}


function draw_bar(style, repeastTimes, isLeftBar){

          var extraXOffset = 0;
          var ret = 0;

          if(repeastTimes > 0 && !isLeftBar){

                    draw_barRepeat(extraXOffset, repeastTimes);
                    extraXOffset += 3;
          }

          var line1Thickness = 0;
          var line2Thickness = 0;

          switch(style){
        
                    case 1:// = regular
                    case 9: // = tick
                    case 10: // = short
                              line1Thickness = 1;
                              line2Thickness = 0;
                              break;
            
                    case 2: // =light-heavy
                              line1Thickness = 1;
                              line2Thickness = 2;
                              break;
    
                    case 3: // = heavy-light
                              line1Thickness = 2;
                              line2Thickness = 1;
                              break;
    
                    case 4: // = light-light
                              line1Thickness = 1;
                              line2Thickness = 1;
                              break;
    
                    case 5: // = heavy
                              line1Thickness = 2;
                              line2Thickness = 0;
                              break;
    
                    case 6: // = heavy-heavy
                              line1Thickness = 2;
                              line2Thickness = 2;
                              break;
    
                    case 7: // = dotted
                    case 8: // = dashed
                              line1Thickness = Stroke.DOTTED;
                              line2Thickness = 0;
                              break;
    
                    case 11: // = none
                              line1Thickness = 0;
                              line2Thickness = 0;
                              break;
          }

          if(line1Thickness != 0){

                    draw_barLine(line1Thickness, extraXOffset);
                    ret = extraXOffset;
                    extraXOffset += line1Thickness + 2;
          }

          if(line2Thickness != 0){

                    draw_barLine(line2Thickness, extraXOffset);
                    ret = extraXOffset;
                    extraXOffset += line2Thickness + 2;
          }

          if(repeastTimes > 0 && isLeftBar){

                    draw_barRepeat(extraXOffset, repeastTimes);
                    ret = extraXOffset;
          }

          return (style == 1)? 0:ret;
}


function draw_ending(endingType, endingNumber, isLeftBar){

          var length = MINIMUM_MEASURE_WIDTH / 3 * 2;
          var notes_y1 = NOTES_STAFF_Y - 6;
          var notes_y2 = NOTES_STAFF_Y - 25;
          var tab_y1 = TAB_STAFF_Y - 6;
          var tab_y2 = TAB_STAFF_Y - 25;
          var x = _currentGraphic._currentX;

          if(endingType == 1 && isLeftBar){

                    _currentGraphic._notes.drawLine(x, notes_y1, x, notes_y2);
                    _currentGraphic._notes.drawLine(x, notes_y2, x + length , notes_y2);
                    _currentGraphic._notes.drawString("" + endingNumber, x + 5, notes_y2);

                    _currentGraphic._tab.drawLine(x, tab_y1, x, tab_y2);
                    _currentGraphic._tab.drawLine(x, tab_y2, x + length , tab_y2);
                    _currentGraphic._tab.drawString("" + endingNumber, x + 5, tab_y2);


          } else if (endingType == 2 && !isLeftBar){

                    _currentGraphic._notes.drawLine(x, notes_y1, x, notes_y2);
                    _currentGraphic._notes.drawLine(x, notes_y2, x - length, notes_y2);
                    _currentGraphic._notes.drawString("" + endingNumber, x - 8 , notes_y2);

                    _currentGraphic._tab.drawLine(x, tab_y1, x, tab_y2);
                    _currentGraphic._tab.drawLine(x, tab_y2, x - length, tab_y2);
                    _currentGraphic._tab.drawString("" + endingNumber, x - 8 , tab_y2);

          } else {

}

}

function draw_measureEx(isLeftBar){

          var barStyle;
          var endingType;
          var endingNumber;
          var repeatTimes;
          var extraXOffset;


          if(isLeftBar){

                    draw_measure_index();
        
                    barStyle = _currentMeasureEx._leftBarStyle;
                    endingType = _currentMeasureEx._leftEndingType;
                    endingNumber = _currentMeasureEx._leftEndingNumber;
                    repeatTimes = _currentMeasureEx._leftRepeatTimes;

          } else {

                    barStyle = _currentMeasureEx._rightBarStyle;
                    endingType = _currentMeasureEx._rightEndingType;
                    endingNumber = _currentMeasureEx._rightEndingNumber;
                    repeatTimes = _currentMeasureEx._rightRepeatTimes;
          }

          if(endingType != 0){

                    draw_ending(endingType, endingNumber, isLeftBar);
          }

          extraXOffset = draw_bar(barStyle, repeatTimes , isLeftBar);

          return extraXOffset;
}


function handle_measureEx(data, offset){

          var packedElement;

          var leftBarStyle;
          var leftEndingType;
          var leftEndingNumber;
          var leftRepeatTimes;
          var rightBarStyle;
          var rightEndingType;
          var rightEndingNumber;
          var rightRepeatTimes;
          var extraXOffset = 0;

          if(_currentMeasureEx != null){

                    extraXOffset = draw_measureEx(false);
                    _currentGraphic._currentX += extraXOffset;
          }

          if(_currentGraphic._currentX < _currentGraphic._minNextMeasureX){

                    _currentGraphic._currentX = _currentGraphic._minNextMeasureX;
          }

          _drawnAltersForTheCurrentMeasure = new Array(0,0,0,0,0,0,0);


          // handle left
          
          packedElement = getReadInt24FromBuffer(data, offset);

          leftBarStyle = getValue(packedElement, 3, 6, 9);
          leftEndingType = getValue(packedElement, 3, 10, 12);
          leftEndingNumber = getValue(packedElement, 3, 13, 16);
          leftRepeatTimes = getValue(packedElement, 3, 17, 22);


          // handle right
          rightBarStyle = getValue2(data, offset, 23, 26);
          rightEndingType = getValue2(data, offset, 27, 29);
          rightEndingNumber = getValue2(data, offset, 30, 33);
          rightRepeatTimes = getValue2(data, offset, 34, 39);

          _currentMeasureEx = new MeasureEx(leftBarStyle, leftEndingType, leftEndingNumber,
                    leftRepeatTimes, rightBarStyle, rightEndingType, rightEndingNumber,
                    rightRepeatTimes);

          extraXOffset = draw_measureEx(true);

          _currentGraphic._minNextMeasureX = _currentGraphic._currentX + MINIMUM_MEASURE_WIDTH;// + extraXOffset;
          _currentGraphic._currentX += DISTANCE_BETWEEN_NOTES / 3 + extraXOffset;

          return (offset + 5);
}


function handle_type(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
        
          _currentType = getValue(packedElement,2,6,9);
          _currentDot = getValue(packedElement,2,10,10);

          _currentVirtualType = _currentType;

          return (offset + 2);
}



function draw_pitch( pitch, alter, alterType) {

          var imageURL = BASE_IMAGE_URL;
          var imageOffsetY = 0;
          var alterOffsetY = 0;
          var stemSelection = STEM_UP;
    
          pitch += _clef_note_offset;

          switch(_currentVirtualType){
        
                    case TYPE_64TH:
                              imageURL += "64th_";
                              break;
            
                    case TYPE_32ND:
                              imageURL += "32nd_";
                              break;
            
                    case TYPE_16TH:
                              imageURL += "16th_";
                              break;
            
                    case TYPE_08TH:
                              imageURL += "08th_";
                              break;
            
                    case TYPE_QUARTER:
                              imageURL += "quarter_";
                              break;
            
                    case TYPE_HALF:
                              imageURL += "half_";
                              break;
            
                    case TYPE_WHOLE:
                              imageURL += "whole";
                              break;
          }

          if(_currentStem == STEM_UNDEFINED){
        
                    if(pitch < 34){
            
                              _currentStem = STEM_UP;
            
                    } else {
            
                              _currentStem = STEM_DOWN;
                    }
          }

          if(_currentVirtualType != TYPE_WHOLE){
        
                    if(_currentStem == STEM_DEFAULT){
        
                              if(pitch < 34){
            
                                        imageURL += "stem_up";
                                        imageOffsetY = -24;
                                        alterOffsetY = -24;
                                        stemSelection = STEM_UP;
        
                              } else {
            
                                        imageURL += "stem_down";
                                        alterOffsetY = -24;
                                        stemSelection = STEM_DOWN;
                              }
        
                    } else if (_currentStem == STEM_UP){
        
                              imageURL += "stem_up";
                              imageOffsetY = -24;
                              alterOffsetY = -24;
                              stemSelection = STEM_UP;
        
                    } else if (_currentStem == STEM_DOWN){
        
                              imageURL += "stem_down";
                              alterOffsetY = -24;
                              stemSelection = STEM_DOWN;
                    }
        
          } else {
        
                    imageOffsetY = -24;
                    alterOffsetY = -24;
                    stemSelection = STEM_UP;
          }

          if(_currentNoteHead == NOTEHEAD_X){

                    imageURL += "_x"
          }
    
          imageURL += ".gif";

          var y;

          // draw extra lines if needed

          y = 0;

          if(pitch <= 28){

                    if(_currentStem == STEM_DOWN){

                              imageOffsetY = -24;
                    }

                    y = NOTES_STAFF_Y + (38 - pitch) * 3 + imageOffsetY;

                    if(pitch % 2 == 1){

                              y -= 3;
                    }

          } else if (pitch >= 40) {

                    y = NOTES_STAFF_Y + (38 - pitch) * 3;

                    if(pitch % 2 == 1){

                              y += 3;
                    }
          }

          if(y != 0){

                    _currentGraphic._notes.drawImage(BASE_IMAGE_URL + "staff_five.gif", _currentGraphic._currentX - 3 , y);
          }


          // draw the note
          y = NOTES_STAFF_Y - 2 + (38 - pitch) * 3 + imageOffsetY ;

          _currentGraphic._notes.drawImage(imageURL, _currentGraphic._currentX , y);

          // handle slur data
          _tempSlurData._noteX = _currentGraphic._currentX;
          _tempSlurData._noteY = NOTES_STAFF_Y - 8 + (38 - pitch) * 3;
          _tempSlurData._noteStem = stemSelection;


          // draw dot
          if(_currentDot){

                    y = NOTES_STAFF_Y + (38 - pitch) * 3 - 3;
                    var x = _currentGraphic._currentX + 10;
                    saveGraphicState(_currentGraphic._notes);
                    _currentGraphic._notes.setStroke(2);
                    _currentGraphic._notes.drawLine(x, y, x, y);
                    restoreGraphicState(_currentGraphic._notes);
          }

          // draw alter
          var step = pitch % 7;

          if(alter != 0 && _currentNoteHead != NOTEHEAD_X){  // the pitch is altered
                    // if the step is not altered by default for this
                    // key and the alter sign is new for the current measure then
                    // draw the sign
                    var drawAlter = (_key[step] == 0) &&
                    ((_drawnAltersForTheCurrentMeasure[step] == 0) ||
                              (_insideChord == true)
                              );

                    if(drawAlter){

                              if(alterType == 0){

                                        _drawnAltersForTheCurrentMeasure[step] = 1;
                                        imageURL = BASE_IMAGE_URL + "sharp.gif";
                                        alterOffsetY += 2;

                              } else {

                                        _drawnAltersForTheCurrentMeasure[step] = -1;
                                        imageURL = BASE_IMAGE_URL + "flat.gif";
                              }
                    } else {

                              imageURL = "";
                    }

          } else if (_drawnAltersForTheCurrentMeasure[step] != 0 && _currentNoteHead != NOTEHEAD_X){
                    // the pitch is not altered, however was altered previously in
                    // the current measure, so paint "natural"" sign
                    _drawnAltersForTheCurrentMeasure[step] = 0;
                    imageURL = BASE_IMAGE_URL + "natural.gif";
        
          } else {
                    // no need to draw alter
                    imageURL = "";
          }

          if(imageURL.length > 0){

                    y = NOTES_STAFF_Y + (38 - pitch) * 3  +  alterOffsetY;

                    _currentGraphic._notes.drawImage(imageURL, _currentGraphic._currentX - 6 , y);
          }

}

    

function handle_pitch(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
          var value = getValue(packedElement,2,6,12);
          var alterType = getValue(packedElement,2,13,13);
          var alter = getValue(packedElement,2,14,15);

          draw_pitch( value, alter, alterType)

          return (offset + 2);
}

function handle_firstInChordPitch(data, offset){
    
          handle_pitch(data, offset);
}

function handle_lastInChordPitch(data, offset){

          _currentVirtualType = _currentType;
    
          handle_pitch(data, offset);
}



function handle_stem(data, offset) {

          var packedElement = getReadInt8FromBuffer(data, offset);
          _currentStem = getValue(packedElement,1,6,7);

          return (offset + 1);
}

function handle_beam(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
          //          var number = getValue(packedElement,2,6,9); //  currently not supported
          var type = getValue(packedElement,2,10,13);

          _insideBeam = (type == BEAM_BEGIN) ? true:false;

          if(_insideBeam){
                    
                    _beamStartX = _currentGraphic._currentX;
          } else {
                    
                    if(_currentType <= TYPE_08TH ) {

                              draw_tabBeam(_beamStartX, _beamEndX);
                    }
          }

          return (offset + 2);
}

function setChordStemDirection(isBeginOfChord){
    
          if(isBeginOfChord){

                    _oldStem = _currentStem;
                    _currentStem = STEM_UNDEFINED;

          } else {
            
                    _currentStem = _oldStem;
          }
    
}


function handle_rest(data, offset) {

          // var packedElement = getReadInt8FromBuffer(data, offset);

          var imageURL = BASE_IMAGE_URL;
          var imageOffsetY = 0;
          var dotOffsetY = 9;
    
          switch(_currentType){
        
                    case TYPE_64TH:
                              imageURL += "rest_64th.gif";
                              break;
            
                    case TYPE_32ND:
                              imageURL += "rest_32th.gif";
                              break;
            
                    case TYPE_16TH:
                              imageURL += "rest_16th.gif";
                              break;
            
                    case TYPE_08TH:
                              imageURL += "rest_eighth.gif";
                              break;
            
                    case TYPE_QUARTER:
                              imageURL += "rest_quarter.gif";
                              imageOffsetY = -10;
                              break;
            
                    case TYPE_HALF:
                              imageURL += "rest_half.gif";
                              imageOffsetY = -8;
                              break;
            
                    case TYPE_WHOLE:
                              _currentGraphic._currentX += (_currentGraphic._minNextMeasureX - _currentGraphic._currentX) / 2 - 12;
                              imageURL += "rest_whole.gif";
                              imageOffsetY = -7;
                              break;
          }


          var y;
    
          // draw the note
          y = NOTES_STAFF_Y + imageOffsetY ;
          _currentGraphic._notes.drawImage(imageURL, _currentGraphic._currentX , y);

          // draw dot
          if(_currentDot){

                    y = NOTES_STAFF_Y + dotOffsetY;
                    var x = _currentGraphic._currentX + 18;
                    saveGraphicState(_currentGraphic._notes);
                    _currentGraphic._notes.setStroke(2);
                    _currentGraphic._notes.drawLine(x, y, x, y);
                    restoreGraphicState(_currentGraphic._notes);
          }

          if(_currentType != TYPE_WHOLE){
                    _currentGraphic._currentX += _distanceBetweenNotes;
          }

          return (offset + 1);
}



function calcTabUppestBeamOffsetY(){
          
          var lastY;
          var factor = 0;

          switch(_currentType){

                    case TYPE_64TH:
                              factor = 4;
                              break;

                    case TYPE_32ND:
                              factor = 3;
                              break;

                    case TYPE_16TH:
                              factor = 2;
                              break;

                    case TYPE_08TH:
                              factor = 1;
                              break;
          }

          lastY = BEAM_LINE_WIDTH  * factor * 2;
          
          return lastY;
}

function draw_tabBeam(startX, endX){

          var y = _tabTypeY + TAB_TYPE_FULL_HEIGHT;
          var lastY = y - calcTabUppestBeamOffsetY();

          saveGraphicState(_currentGraphic._tab);

          _currentGraphic._tab.setStroke(BEAM_LINE_WIDTH);

          endX -= 1; // i reduce the length i 1 pixel in order to baypass "sofDraw"  bug

          if(endX - startX < TAB_SINGLE_NOTE_BEAM_LENGTH){

                    endX = startX + TAB_SINGLE_NOTE_BEAM_LENGTH
          }
          
          for(; y >  lastY; y -= BEAM_LINE_WIDTH * 2){

                    _currentGraphic._tab.drawLine(startX, y, endX, y);
          }

          restoreGraphicState(_currentGraphic._tab);
}

function draw_tabDot(x, y) {

          saveGraphicState(_currentGraphic._tab);
          _currentGraphic._tab.setStroke(2);
          _currentGraphic._tab.drawLine(x, y, x, y);
          restoreGraphicState(_currentGraphic._tab);
}

function draw_tab(string, fret) {

          var y;

          // draw string index OR 'X' '
          saveGraphicState(_currentGraphic._tab);
          _currentGraphic._tab.setFontSize(TAB_FONT_HEIGHT + 'px');
          _currentGraphic._tab.setFontStyle(TAB_FONT_STYLE);
          y = TAB_STAFF_TOP_MARGIN + (string-1) * TAB_STAFF_SPACER_SIZE + TAB_Y_IMPIRIC_OFFSET;

          var text;

          if(_currentNoteHead == NOTEHEAD_X){

                    text = "x";

          } else {

                    text = fret.toString();
          }

          _currentGraphic._tab.drawString(text, _currentGraphic._currentX, y);

          restoreGraphicState(_currentGraphic._tab);

          var fretIndexTextLength = Number(text.length);

          // draw bend
          if(_bendAlter > 0 || _preBend == 1){

                    //        var x1 = _currentGraphic._currentX + TAB_FONT_WIDTH * fretIndexTextLength;
                    var x1 = _currentGraphic._currentX + TAB_FONT_WIDTH;
                    var y1 = TAB_STAFF_Y - 22;
                    var width;
                    var image;
                    var h = (y - y1) + 3;

                    if(_preBend == 1){

                              width = 7;
                              image = _preBandImagePath;
                    } else {

                              width = _distanceBetweenNotes - 5;

                              if(_release == 1){
                                        image = _bendReleaseImagePath;
                              } else {

                                        image = _bendImagePath;
                              }
                    }
                    _currentGraphic._tab.drawImage(image, x1,  y1,  width  ,  h);

                    var alterText = "";
                    var full = Math.floor(_bendAlter);
                    var rest = _bendAlter - full;
                    var alterTextLength = 0;

                    if(full == 1 && rest == 0){
                              alterText += BEND_FULL;
                              alterTextLength += 4;
                    } else if(full >= 1){
                              alterText += full;
                              alterTextLength += 1;
                    }

                    if(rest == 0.25){

                              alterText += BEND_QUARTER;
                              alterTextLength += 1;
                    } else if (rest == 0.5){

                              alterText += BEND_HALF;
                              alterTextLength += 1;
                    } else if(rest == 0.75){

                              alterText += BEND_THREE_QUARTERS;
                              alterTextLength += 1;
                    }

                    x1 += width - (alterTextLength * TAB_FONT_WIDTH) /2;
                    _currentGraphic._tab.drawString(alterText, x1, y1 - TAB_FONT_HEIGHT - 2);
          }



          // handle slide positions
          _tempSlide[string]._direction = (_tempSlide[string]._fret < fret)? SLIDE_DIRECTION_UP:SLIDE_DIRECTION_DOWN;
          _tempSlide[string]._fret = fret;
          _tempSlide[string]._tabX = _currentGraphic._currentX;
          _tempSlide[string]._tabY = y + 5;
          _tempSlide[string]._tabTextLength = fretIndexTextLength;

          var textLengthPixels = fretIndexTextLength * TAB_FONT_HEIGHT / 2;

          // handle slur positions
          _tempSlurData._tabX = _currentGraphic._currentX + textLengthPixels;
          _tempSlurData._tabY = y;


          // draw tab type
          var length = 0;
                    
          if(_currentType >= TYPE_WHOLE) {

                    length = 0;

          } else if(_currentType == TYPE_HALF){

                    length = TAB_TYPE_HALF_HEIGHT;
                    y = _tabTypeY + length;
                    
          } else {

                    length = TAB_TYPE_FULL_HEIGHT ;
                    y = _tabTypeY;
          }

          if(length > 0){
                    
                    _currentGraphic._tab.drawLine(_currentGraphic._currentX, y, _currentGraphic._currentX, y + length);
          }

          if(!_insideBeam && _currentType <= TYPE_08TH){

                    draw_tabBeam(_currentGraphic._currentX,  _currentGraphic._currentX + TAB_SINGLE_NOTE_BEAM_LENGTH);
          }
                    
          if(_currentDot){

                    y += length;
                              
                    var yOffset = calcTabUppestBeamOffsetY() ;
                    if(yOffset > 0){

                              y  -= (yOffset);
                    }

                    var x = _currentGraphic._currentX + 3;
                    draw_tabDot(x, y);
          }
}


function draw_Technical(string, fret) {

          var pitch = _tabNotes[string][fret];
          var alter = (pitch & NOTE_SHARP) != 0;
    
          pitch &= ~NOTE_SHARP;
    
          // draw the tab data
          draw_tab(string, fret);

          // draw the pitch
          draw_pitch(pitch, alter, 0);
}

function handle_technical(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
          var string = getValue(packedElement,2,6,8);
          var fret = getValue(packedElement,2,9,14);

          if(string != 0){
  
                    draw_Technical(string, fret);

                    if(_insideChord == false){

                              _beamEndX = _currentGraphic._currentX;
                              _currentGraphic._currentX += _distanceBetweenNotes;
                    }
          }

          resetBend();
          
          return (offset + 2);
}

function handle_firstInChordTechnical(data, offset){

          _insideChord = true;
    
          setChordStemDirection(_insideChord)
    
          if(_currentType <= TYPE_08TH){
     
                    _currentVirtualType = TYPE_QUARTER;
          }
    
          return handle_technical(data, offset);
}


function handle_lastInChordTechnical(data, offset){

          _currentVirtualType = _currentType;

          var ret = handle_technical(data, offset);

          _insideChord = false;

          setChordStemDirection(_insideChord)
          
          _beamEndX = _currentGraphic._currentX;
          _currentGraphic._currentX += _distanceBetweenNotes;

          return ret;
}


var _notesToLetters = new Array("C", "D", "E","F","G","A","B");
var GUITAR_TUNING_DIV_HEIGHT = 50;
var GUITAR_TUNING_TOP_MARGIN = 10;
var GUITAR_TUNING_LEFT_MARGIN =20;
var GUITAR_TUNING_SPACE =65;

var GUITAR_TUNING_FONT_HEIGHT = 15;
var GUITAR_TUNING_FONT_STYLE = Font.BOLD;
var GUITAR_TUNING_CIRCLE_DIAMETER = GUITAR_TUNING_FONT_HEIGHT * 1.15 ;

function normalize_note(note) {

          var sharp = note & NOTE_SHARP;
          note &= ~NOTE_SHARP; // removing "sharp" sign
          note %= 7; // normalize note
          note |= sharp; // adding sharp sign again
          return note;
}

function handle_guitarTuning(data, offset) {

          var octave;
          var lowestOctave = 127;

          _currentLine++;
          var guitarTuningDiv = createNewDiv("guitar_tuning", GUITAR_TUNING_DIV_HEIGHT, true);
          _parent.appendChild(guitarTuningDiv);
          var gr = new jsGraphics("guitar_tuning" + _currentLine);

          gr.setFontSize(GUITAR_TUNING_FONT_HEIGHT + 'px');
          gr.setFontStyle(GUITAR_TUNING_FONT_STYLE);


          _numberOfStrings = getValue2(data, offset, 6, 13);
          calcTabStaffHeightParams();
        
          _tabNotes = new Array(7);
    
          var stringIndex;
          var fretIndex;
          var note;
          var sharp;
          var bit = 14;
          var inSharp = false;
          var x = GUITAR_TUNING_LEFT_MARGIN;


          gr.drawString("Guitar Tuning: ", x, GUITAR_TUNING_TOP_MARGIN);
          x +=150;
          
          for(stringIndex = 1; stringIndex <= _numberOfStrings; stringIndex++, bit += 8){

                    _tabNotes[stringIndex] = new Array(INVALID_FRET);
                    inSharp = false;
                    octave = getValue2(data, offset, bit, bit+3) * 7;
                    note = getValue2(data, offset, bit+ 4, bit+ 6);
                    sharp = getValue2(data, offset, bit+ 7, bit+ 7);

                    var tuningString = stringIndex + " =" +_notesToLetters[note];
                    if(sharp == 1){

                              tuningString += "#";
                              inSharp = true;
                    }

                    tuningString += "  ";
                    
                    gr.drawEllipse(x, GUITAR_TUNING_TOP_MARGIN+2, GUITAR_TUNING_CIRCLE_DIAMETER, GUITAR_TUNING_CIRCLE_DIAMETER);
                    gr.drawString(tuningString, x + 3, GUITAR_TUNING_TOP_MARGIN);

                    x += GUITAR_TUNING_SPACE;

                    if(lowestOctave > octave){
                              lowestOctave = octave;
                    }

                    for(fretIndex = 0; fretIndex < INVALID_FRET; fretIndex++){
        
                              if(_stepsWithSharp[note] == 0){
                
                                        _tabNotes[stringIndex][fretIndex] = octave + note;
                                        note++;
                
                                        inSharp = false;
                
                              } else if( !inSharp){
                
              
                                        _tabNotes[stringIndex][fretIndex] = octave + note;
                
                                        inSharp = true;
                
                              } else {
              
                                        _tabNotes[stringIndex][fretIndex] = (octave + note) | NOTE_SHARP;
                                        note++;
                                        inSharp = false;
                              }

                              if(note == 7){
                
                                        note = 0;
                                        octave += 7;
                              }
                    }
          }

          if(lowestOctave >= 21){
                    setClef(CLEF_G);
          } else {
                    setClef(CLEF_F);
          }

          gr.paint();

          var length = ((_numberOfStrings * 8) + 14) / 8;
          if(length != Math.floor(length)){
                    length = Math.floor(length) + 1;
          }
    
          return (offset + length);
}

function standardChordDefinition(index, nameOffset, numberOfStrings, 
          lastValidString, fretsArray, fingersArray, reverseDraw) {

          this._index = index;
          this._nameOffset = nameOffset;
          this._fretsArray = fretsArray;
          this._fingersArray = fingersArray;
          this._numberOfStrings = numberOfStrings;
          this._lastValidString = lastValidString;
          this._reverseDraw = reverseDraw;
}

var _chordsDefinitionArray = new Array();

function handle_standardChordDefinition(data, offset){
  
          var index = getValue2(data, offset, 7, 14);
          var nameOffset = getValue2(data, offset, 15, 30);
          var lastValidString = getValue2(data, offset, 31, 38);
          var reverseDraw = getValue2(data, offset, 39, 39);
          var numberOfStrings = getValue2(data, offset, 40, 47);
          var fretsArray = new Array(numberOfStrings);
          var fingersArray = new Array(numberOfStrings);
  
          var i;
          var internalOffset = offset + 6;
  
          for(i = 0 ; i < numberOfStrings; i++, internalOffset++){
      
                    fingersArray[i] =  getValue2(data, internalOffset, 0, 2);
                    fretsArray[i] = getValue2(data, internalOffset, 3, 7);
          }
  
          var chord = new standardChordDefinition(index, nameOffset, numberOfStrings, lastValidString, fretsArray, fingersArray, reverseDraw);
  
          _chordsDefinitionArray[index] = chord;
  
          return (offset + 6 + numberOfStrings);
} 


var _lastChord = -1;
var _chordsExists = false;

function draw_chordName(chord){

          if(_lastChord != chord._index){
                    _chordsExists = true;
                    var chordName  = _text[chord._nameOffset];
                    _currentGraphic._chords.drawString(chordName, _currentGraphic._currentX, 10);
                    _lastChord = chord._index;
          }
}

function handle_standardChord(data, offset){
    
          var packedElement = getReadInt16FromBuffer(data, offset);
          var chordIndex = getValue(packedElement,2,6,11);
          var stumming = getValue(packedElement,2,12,13);
          var drawNotes = getValue(packedElement,2,14,14);
    
          var chord = _chordsDefinitionArray[chordIndex];

          if(_currentNoteHead != NOTEHEAD_X){
                    draw_chordName(chord);
          }

          if(drawNotes){

                    _insideChord = true;
                    setChordStemDirection(_insideChord);

                    if(_currentType <= TYPE_08TH){
     
                              _currentVirtualType = TYPE_QUARTER;
                    }

                    var string;
                    var fret;

                    if(! chord._reverseDraw){

                              for(string = 0; string < chord._numberOfStrings; string++){
    
                                        fret = chord._fretsArray[string];
                                        if(fret <= 22){

                                                  if(string == chord._lastValidString){
                
                                                            _currentVirtualType = _currentType;
                                                  }
            
                                                  draw_Technical(string + 1, fret);
                                        }
                              }
                    } else {

                              var lastValidString = 0;

                              for(string = 0;string < chord._numberOfStrings;string++){

                                        if(chord._fretsArray[string] != INVALID_FRET){

                                                  lastValidString = string;
                                                  break;
                                        }
                              }

                              string = Number(chord._numberOfStrings) - 1;

                              for(; string >= 0; string--){

                                        fret = chord._fretsArray[string];
                                        if(fret <= 22){

                                                  if(string == lastValidString){

                                                            _currentVirtualType = _currentType;
                                                  }

                                                  draw_Technical(string + 1, fret);
                                        }
                              }
                    }

                    _insideChord = false;
    
                    setChordStemDirection(_insideChord);

                    _beamEndX = _currentGraphic._currentX;
                    _currentGraphic._currentX += _distanceBetweenNotes;
          }
          
          return (offset + 2);
}

function flush_line(){

          flush_slurs();
          flush_slides();

          if(_currentGraphic != null){
                    
                    _currentGraphic.paint();
          }
}


function isVisibleDiv(divType){

          if(divType =="tab"){
                    return _tabVisible;
          } else if(divType == "notes"){
                    return _notesVisible;
          } else if(divType == "chords"){
                    return _chordsVisible;
          } else if(divType =="lyrics"){
                    return _lyricsVisible;
          } else if(divType == "guitarNeck"){
                    return _guitarNeckVisible;
          }

          return true;
}

function createNewDiv(name, height, visible) {
    
          var newDiv = document.createElement('div');
          var divIdName = name + _currentLine;
    
          newDiv.setAttribute('id',divIdName);

          newDiv.style.position='relative';
          if(visible){
                    newDiv.style.display= isVisibleDiv(name)? 'block':'none';
          } else {
                    newDiv.style.display= 'none';
          }
          
          newDiv.style.width = '' + _areaWidth + 'px';
          newDiv.style.height = '' + height + 'px';
          newDiv.style.margin = "0px 10px 0px 4px";

          if(!_drawForPrint){
                    if(Math.floor(_currentLine / 2) == _currentLine / 2){

                              newDiv.style.background = "#ffffff";
                    } else {

                              newDiv.style.background = "#fff9ea";
                    }
          }
    
          return newDiv;
}

function createNewLine() {

          flush_line();
          
          _currentLine++;

          _currentMeasureIndexDiv = createNewDiv("measureIndex", MEASURE_INDEX_HEIGHT, true);
          _parent.appendChild(_currentMeasureIndexDiv);
/*
          _currentLyricsDiv = createNewDiv("lyrics", LYRICS_DIV_HEIGHT, false);
          _parent.appendChild(_currentLyricsDiv);
*/
          _currentNotesDiv = createNewDiv("notes", NOTES_STAFF_TOTAL_HEIGHT, true);
          _parent.appendChild(_currentNotesDiv);

          _currentChordsDiv = createNewDiv("chords", CHORDS_DIV_HEIGHT, false);
          _parent.appendChild(_currentChordsDiv);

          _currentTabDiv = createNewDiv("tab", _tabStaffTotalHeight, true);
          _parent.appendChild(_currentTabDiv);
    
          _currentSeparatorDiv = createNewDiv("separator", SEPARATOR_HEIGHT, true);
          _parent.appendChild(_currentSeparatorDiv);

          _currentGraphic = new graphic(_currentLine);

          // draw notes staff
          //    drawDivBackground(_currentGraphic._notes, _currentNotesDiv, NOTES_BACKGROUND_COLOR);
          drawStaff(_currentGraphic._notes, NOTES_STAFF_Y, _areaWidth, NOTES_NUMBER_OF_LINES, NOTES_STAFF_SPACER_SIZE);
    
    
          // draw tab staff
          //    drawDivBackground(_currentGraphic._tab, _currentTabDiv, TAB_BACKGROUND_COLOR);
          drawStaff( _currentGraphic._tab, TAB_STAFF_TOP_MARGIN, _areaWidth, _numberOfStrings, TAB_STAFF_SPACER_SIZE);

          drawSeparator(_areaWidth);
}

function handle_line(data, offset) {

          var packedElement = getReadInt16FromBuffer(data, offset);
          var distance = getValue(packedElement,2,6,12);

          if(_currentGraphic != null){

                    _currentGraphic._currentX = _areaWidth - 6;
                    if(!flushMeasureEx()){

                              _currentGraphic._notes.drawLine(_areaWidth, NOTES_STAFF_Y, _areaWidth, NOTES_STAFF_Y + NOTES_STAFF_SPACER_SIZE * (NOTES_NUMBER_OF_LINES-1));
                              _currentGraphic._tab.drawLine(_areaWidth, TAB_STAFF_Y, _areaWidth, TAB_STAFF_Y + TAB_STAFF_SPACER_SIZE * (_numberOfStrings-1));
                    }
          }

          _distanceBetweenNotes = distance;
    
          createNewLine();

          _needToBreak = true;

          return (offset + 2);
}

function handle_dummy(data, offset)  {
        
          return offset + 1;
}


var functionsArray = new Array(

          this.handle_dummy,
          this.handle_version,
          this.handle_clef,
          this.handle_key,
          this.handle_time,
          this.handle_measure,
          this.handle_type,
          this.handle_pitch,
          this.handle_firstInChordPitch,
          this.handle_lastInChordPitch,
          this.handle_stem,
          this.handle_beam,
          this.handle_rest,
          this.handle_slur,
          this.handle_technical,
          this.handle_firstInChordTechnical,
          this.handle_lastInChordTechnical,
          this.handle_guitarTuning,
          this.handle_standardChordDefinition,
          this.handle_standardChord,
          this.handle_line,
          this.handle_measureEx,
          this.handle_slide,
          this.handle_bend,
          this.handle_noteHead,
          this.handle_Text,
          this.handle_Lyrics
          );


var _data;
var _dataLength;
var _currentOffset;
var _needToBreak = false;
var _busy = false;
var _callbackFunc = null;

function _parse(){

          var type;
          var b;
          var ret;

          while(_currentOffset < _dataLength) {

                    b = _data[_currentOffset];
                    type = getValue(b, 1, 0, 5);
                    ret = functionsArray[type](_data, _currentOffset);
                    if(ret == -1){
                              // incorrent version
                              return -1;
                    }

                    _currentOffset = ret;

                    if(_needToBreak){

                              _needToBreak = false;

                              setTimeout('_parse()', 50);
                              return 0;
                    }
          }

          flushMeasureEx();
          flush_line();
          
          _needToBreak = false;
          _busy = false;

          if(_callbackFunc != null){
                    _callbackFunc();
          }

          return 0;
}


var _lyrics = null;

function draw_lyrics(){

          if(_lyrics == null){

                    return;
          }

          var div = document.createElement('div');
          div.setAttribute('id','lyrics');

          div.style.position='relative';
          div.style.display= (_lyricsVisible)? 'block':'none';
          div.style.width = '' + (_areaWidth - 100 /* padding in #lyrics element is 1--px, so we have to reduce it here*/) + 'px';

          div.innerHTML = _lyrics;

          _parent.appendChild(div);

          _lyrics = null;
}


function VGTDraw(parent, lyrics) {

          _parent = parent
    
          _areaWidth = sofGetElementWidth(_parent) - 25;
          _currentLine = -1;
          _busy = false;
          _lyrics = lyrics;

          this.setDrawForPrint = function(){

                    _drawForPrint  =true;
                    TAB_Y_IMPIRIC_OFFSET = (-12);
          }

          this.isExists = function (divType){

                    if(divType == "chords"){

                              return _chordsExists;
                    } else if(divType == "lyrics"){
                              
                              return true;
                    }
    
                    return true;
          }

          this.getDivVisible = function(divType){

                    if(divType == "chords"){
                              return _chordsVisible;
                    } else if(divType == "lyrics"){
                              return _lyricsVisible;
                    } else if(divType == "notes"){
                              return _notesVisible;
                    } else if(divType == "tab"){
                              return _tabVisible;
                    } else if(divType == "guitarNeck"){
                              return _guitarNeckVisible;
                    }

                    return false;
          }


          this.isBusy = function(){

                    return _busy;
          }

          this.removeDivByType = function(divType) {

                    var lineIndex;
                    var divName;
        
                    for(lineIndex = 0; lineIndex <= _currentLine; lineIndex++){

                              divName = divType + lineIndex;
            
                              var ele = document.getElementById(divName);
                              if(ele != null){
                
                                        _parent.removeChild(ele);
                              }
                    }
          }

          this.clear = function(){
    
                    _currentStem = STEM_DEFAULT;
                    _currentNoteHead = NOTEHEAD_NORMAL;
                    _chordsExists = false;
                //    _lyricsExists = false;
                    _lastChord = -1;

                    if(_currentGraphic != null){

                              _currentGraphic.clear();
                              _currentGraphic = null;
                    }

                    this.removeDivByType("chords");
     //               this.removeDivByType("lyrics");
                    this.removeDivByType("notes");
                    this.removeDivByType("tab");
                    this.removeDivByType("guitarNeck");
                    this.removeDivByType("separator");
                    this.removeDivByType("measureIndex");
                    this.removeDivByType("guitar_tuning");


                    init_slide();
                    init_slurs();
                    
                    _currentMeasureEx = null;
                    _currentMeasureIndex = 0;
          }
    
          this.parseAsync = function(data, callbackFunc) {

                    if(_busy){
                              // busy, so return "busy"
                              return -2;
                    }

                    this.clear();
                    
                    var type = getValue(data[0], 1, 0, 5);
                    if(type != 1){
                              // must be a "version" tag"
                              return -1;
                    }

                    if(handle_version(data, 0) == -1){
                              // invalid version
                              return -1;
                    }


                    _busy = true;

                    draw_lyrics();


                    _data = data;
                    _dataLength = data.length;
                    _currentOffset = 4;
                    _callbackFunc = callbackFunc;

                    _parse();

                    return 0;
          }

          this.parse = function(data) {

                    var type;
                    var offset = 0;
                    var dataLength = data.length;
                    var b;

                    calcTabStaffHeightParams();
                    
                    this.clear();

                    draw_lyrics();
                    
                    while(offset < dataLength) {

                              b = data[offset];
                              type = getValue(b, 1, 0, 5);

                              offset = functionsArray[type](data, offset);
                              if(offset == -1){
                                        // version error -> exit
                                        return -1;
                              }
                    }

                    flushMeasureEx();
                    flush_line();

                    return 0;
          }

          this._internaleShowDiv = function(divId, show){

                    var ele = document.getElementById(divId);
                    if(ele != null){

                              if(show) {

                                        ele.style.display = "block";
                              } else {

                                        ele.style.display = "none";
                              }
                    }
          }

          this.showDives = function(divType, show) {

                    var lineIndex;
                    var divName;

                    if(divType == "chords"){
                              _chordsVisible = show;
                    } else if(divType == "notes"){
                              _notesVisible = show;
                    } else if(divType == "tab"){
                              _tabVisible = show;
                    } else if(divType == "guitarNeck"){
                              _guitarNeckVisible = show;
                    } else if (divType == "lyrics"){

                              _lyricsVisible = show;
                              this._internaleShowDiv('lyrics', show);
                              return;
                    }

                    for(lineIndex = 0; lineIndex <= _currentLine; lineIndex++){

                              divName = divType + lineIndex;
                              this._internaleShowDiv(divName, show);
                    }
          }
}
