if( typeof gCLOOKTRAINDRAWER == 'undefined' ){
  var gCLOOKTRAINDRAWER = 'defined';
  /**
   * @brief General class to show Trains
   *
   * - uses Canvas to draw trains
   * - draws on swap page
   * - usage: initialize / multiple draw / show / multiple draw / show / multiple draw / show / ...
   * - needs own div container at initialization
   **/
  var gLookTrainDrawerReferenceCount = gLookTrainDrawerReferenceCount?gLookTrainDrawerReferenceCount:0;
  var CLookTrainDrawer = Class.create();
  CLookTrainDrawer.prototype= {

    // public interface
    traindiameter : 4,
    maxtextwidth:160,

    // constructor
    initialize: function() {
      this.refcount = gLookTrainDrawerReferenceCount;
      gLookTrainDrawerReferenceCount++;
    },

    // initialization
    init: function( aTrainDivContainer, aTextDivContainer, aWidth, aHeight ) {
      this.width = aWidth;
      this.height = aHeight;
      this.initializeCanvasDiv(aTrainDivContainer);
      this.initializeTextDiv(aTextDivContainer);
    },

    setMouseManager: function( aMouseManager ){
      this.mousemanager = aMouseManager;
    },
    setTransformer: function( aTransformer ){
      this.transformer = aTransformer;
    },

    getTransformer: function(){
      if( typeof this.transformer == 'undefined' ) alert('CLookTrainDrawer::getTransformer(): No Transformer available');
      return this.transformer;
    },

    // swap the current divs
    show: function (){
       this.currenttraindiv.display = 'block';
       this.currenttextdiv.display = 'block';
       if( this.currenttraindiv == this.traindiv1 ){
         this.currenttraindiv = this.traindiv2;
       } else {
         if( this.currenttraindiv != this.traindiv2 ) alert( "something's wrong" );
         this.currenttraindiv = this.traindiv1;
       }
       if( this.currenttextdiv == this.textdiv1 ){
         this.currenttextdiv = this.textdiv2;
       } else {
         if( this.currenttextdiv != this.textdiv2 ) alert( "something's wrong" );
         this.currenttextdiv = this.textdiv1;
       }
       this.currenttraindiv.display = 'none';
       this.currenttextdiv.display = 'none';
       CNodes.removeAllChilds( this.currenttraindiv );
       CNodes.removeAllChilds( this.currenttextdiv );
       this.currenttraincanvas = this.addCanvasElem(
             this.getElemName(this.currenttraindiv.id + 'canvas'),
             this.currenttraindiv );
    },
    drawalltrains: function( trains, stops, edges, showvehicledetails ){
      this.stops = stops;
      this.edges = edges;
      var detailstitle = '';
      var detailstext = '';

      for( var i=0; i< trains.length; i++){
        if( trains[i] && trains[i].edgeid && trains[i].passproc ){
          // combine full train name
          detailstitle = '';
          if( trains[i].name )
            detailstitle = trains[i].name;
          if( trains[i].lstopname )
            detailstitle += ' ' + trains[i].lstopname;

          // combine detail information
          detailstext = '';
          if( trains[i].pdep )
            detailstext = trains[i].pdep + ': ';
          if( trains[i].pstopname ) {
            if( trains[i].passproc == 0 )
              detailstext += 'Stopping at ';
            detailstext += ' ' + trains[i].pstopname + '<br>';
          }
          if( trains[i].narr )
            detailstext = trains[i].narr + ': ';
          if( trains[i].nstopname )
            detailstext += ' ' + trains[i].nstopname;
          if( trains[i].delay ){
            detailstext += '<br>Delay: ' + trains[i].delay;
          if( trains[i].delay == 1 )
            detailstext += ' minute';
          else
            detailstext += ' minutes';
          }
          this.drawtrainonedge( trains[i],
                                detailstitle,
                                detailstext,
                                showvehicledetails );
        }
      }
    },
    drawtrainonedge: function (train,name,text,showvehicledetails){
      var percent = parseInt(train.passproc);
      var mx = ( (100-percent) * ( this.stops[this.edges[train.edgeid].id1].x ) +
                 percent * ( this.stops[this.edges[train.edgeid].id2].x ) ) / 100;
      var my = ( (100-percent) * ( this.stops[this.edges[train.edgeid].id1].y ) +
                 percent * ( this.stops[this.edges[train.edgeid].id2].y ) ) / 100;
      var x = this.getTransformer().getX(mx,my);
      var y = this.getTransformer().getY(mx,my);
      if( showvehicledetails == true ){
        this.drawtraindetails(x,y,name,text);
      }
      this.drawtrain(x,y, train);
    },

    // draws a train to current not shown train canvas
    drawtrain: function (x,y,train){
      if (this.currenttraincanvas.getContext){
        var ctx = this.currenttraincanvas.getContext('2d');

        ctx.save();
        ctx.translate(x,y);

        ctx.beginPath();
        ctx.fillStyle = help.getproductclasscolour( train.prodclass );
        ctx.arc(0,0,this.traindiameter,0,Math.PI*2.0,true);
        ctx.fill();

        ctx.beginPath();
        ctx.lineWidth = "1";
        ctx.strokeStyle = "rgba(0,0,0,1)";
        ctx.arc(0,0,this.traindiameter,0,Math.PI*2.0,true);
        ctx.stroke();

        if( train.direction && train.direction != '' ){
          ctx.rotate(Math.PI*2.0*(32-train.direction)/32);
          ctx.beginPath();
          ctx.strokeStyle = "rgba(0,0,0,1)";
          ctx.lineWidth = "1";
          ctx.moveTo(this.traindiameter-1,0);
          ctx.lineTo(0,this.traindiameter-1);
          ctx.lineTo(0,-this.traindiameter+1);
          ctx.lineTo(this.traindiameter-1,0);
          ctx.stroke();

          ctx.beginPath();
          ctx.fillStyle = "rgba(255,255,255,1)";
          if( train.delay ){
            if( train.delay < 2 ){
              ctx.fillStyle = "rgba(0,255,0,1)";
            } else if( train.delay <= 10 ){
              ctx.fillStyle = "rgba(255,255,0,1)";
            } else{
              ctx.fillStyle = "rgba(255,0,0,1)";
            }
          }
          ctx.lineWidth = "1";
          ctx.moveTo(this.traindiameter-1,0);
          ctx.lineTo(0,this.traindiameter-1);
          ctx.lineTo(0,-this.traindiameter+1);
          ctx.lineTo(this.traindiameter-1,0);
          ctx.fill();
        }
        ctx.restore();
      }
    },

    // draws train details to current not shown text div container
    drawtraindetails: function(x,y,name,text){
      var p = this.calcdetailsposition( x,y );

      if (this.currenttraincanvas.getContext){
        var ctx = this.currenttraincanvas.getContext('2d');
        ctx.save();
        ctx.translate(x,0);

        ctx.beginPath();
        ctx.lineWidth = "1";
        ctx.strokeStyle = "rgba(100,100,100,0.5)";
        ctx.fillStyle = "rgba(200,200,200,0.5)";
        ctx.moveTo(p.boxdist,p.y-7);
        ctx.quadraticCurveTo(p.boxdist,p.y,-this.traindiameter,y);
        ctx.quadraticCurveTo(p.boxdist,p.y,p.boxdist,p.y + 2*14-4);
        ctx.fill();

        ctx.beginPath();
        ctx.lineWidth = "1";
        ctx.strokeStyle = "rgba(100,100,100,0.5)";
        ctx.fillStyle = "rgba(200,200,200,0.5)";
        ctx.moveTo(p.boxdist,p.y-7);
        ctx.quadraticCurveTo(p.boxdist,p.y,-this.traindiameter,y);
        ctx.quadraticCurveTo(p.boxdist,p.y,p.boxdist,p.y + 2*14-4);
        ctx.stroke();

        ctx.restore();
      }
      this.currenttextdiv.innerHTML = this.currenttextdiv.innerHTML +
        '<div class="look_info" style="' +
        'top:' + Math.round(p.y - 8) + 'px;' +
        'left:' + Math.round(p.leftpx) + 'px;' +
        '">' +
          '<div class="look_info_title" style="' +
          'width:' + this.maxtextwidth + 'px;' +
          '">' + name + '</div>' +
          '<div class="look_info_body" style="' +
          'width:' + this.maxtextwidth + 'px;' +
          '">' + text + '</div>' +
        '</div>';
    },

    // private part

    /**
     * @brief Calculatation of position of details box
     *
     * Please overload as you want.
     * Default behavier: Details directly right of the main div; Top value as
     * y-value of the train
     */
    calcdetailsposition: function(x,y){
      var retval = new Object();
      retval.boxdist = this.width - x;
      retval.leftpx = this.width;
      retval.y = y;
      return retval;
    },

    initializeCanvasDiv: function( divcontainer ) {
      this.traindiv1 = this.addDivElem( this.getElemName( 'TrainDiv_1' ), divcontainer );
      this.traindiv2 = this.addDivElem( this.getElemName( 'TrainDiv_2' ), divcontainer );
      this.traindiv2.display = 'none';
      this.currenttraindiv = this.traindiv1;
      this.currenttraincanvas = this.addCanvasElem(
            this.getElemName(this.currenttraindiv.id + 'canvas'),
            this.currenttraindiv );
    },

    initializeTextDiv: function( divcontainer ) {
      this.textdiv1 = this.addDivElem( this.getElemName( 'TextDiv_1' ), divcontainer );
      this.textdiv2 = this.addDivElem( this.getElemName( 'TextDiv_2' ), divcontainer );
      this.textdiv2.display = 'none';
      this.currenttextdiv = this.textdiv1;
    },

    getDivElemStandard: function( divname ){
      var elem = CNodes.getElem( 'div', divname, 'absolute', 'block',
                               0, 0, this.width, this.height );
      return elem;
    },

    addDivElem: function( divname, parentelem ){
      if ( $( divname ) ){
        CNodes.removeAllChilds( $( divname ) );
  //      alert( 'Element with id="' + divname + '" already existing');
      } else {
        var elem = this.getDivElemStandard( divname );
        parentelem.appendChild(elem);
      }
      return $( divname );
    },

    getCanvasElemStandard: function( canvasname ){
      var elem = CNodes.getElem( 'canvas', canvasname, 'absolute', 'block',
                               0, 0, this.width, this.height );
      elem.appendChild(document.createTextNode("CANVAS NOT AVAILABLE"));
      return elem;
    },

    addCanvasElem: function( canvasname, parentelem ){
      if ( $( canvasname ) ){
        alert( 'Element with id="' + canvasname + '" already existing');
      } else {
        var elem = this.getCanvasElemStandard( canvasname );
        parentelem.appendChild(elem);
        if( typeof G_vmlCanvasManager != 'undefined' ) G_vmlCanvasManager.initElement(elem);
      }
      return $( canvasname );
    },

    getElemName: function( shortname ){
      return 'CLookTrainDrawer_' + this.refcount + '_' + shortname;
    }

  }

  /**
   * @brief Special class to show Trains
   *
   * It draws trains i.e. for netspider. Difference to normal TrainDrawer:
   * - the details boxes appear left and right of the main div
   **/
  var CLookTrainDrawerLeftAndRightBesideMainDiv = Class.create();
  CLookTrainDrawerLeftAndRightBesideMainDiv.prototype = Object.extend(new CLookTrainDrawer(), {
    // constructor
    initialize: function() {
      this.refcount = gLookTrainDrawerReferenceCount;
      gLookTrainDrawerReferenceCount++;
    },

    /**
     * @brief Calculatation of position of details box
     *
     * I.e. Netspiders have there train details boxes left and right directly besides the
     * main divcontainer
     */
    calcdetailsposition: function(x,y){
      var retval = new Object();
      retval.boxdist = this.width - x;
      retval.leftpx = this.width;
      retval.y = (y - this.height/2) * 1.1 + this.height/2;
      if( x < this.width/ 2 ){
        retval.boxdist = -x;
        retval.leftpx = -this.maxtextwidth;
      }
      return retval;
    }
  });


  /**
   * @brief Special class to show Trains
   *
   * It draws trains i.e. for perllines. Difference to normal TrainDrawer:
   * - the details boxes appear left and right close by the trains
   **/
  var CLookTrainDrawerLeftAndRightCloseBy = Class.create();
  CLookTrainDrawerLeftAndRightCloseBy.prototype = Object.extend(new CLookTrainDrawer(), {
    // constructor
    initialize: function() {
      this.refcount = gLookTrainDrawerReferenceCount;
      gLookTrainDrawerReferenceCount++;
    },

    /**
     * @brief Calculatation of position of details box
     *
     * I.e. Perllines have there train details boxes left and right close by the trains
     */
    calcdetailsposition: function(x,y){
      var retval = new Object();
      retval.boxdist = this.maxtextwidth;
      retval.leftpx = x + this.maxtextwidth;
      retval.y = y;
      if( x < this.width/ 2 ){
        retval.boxdist = -this.maxtextwidth;
        retval.leftpx = x-2*this.maxtextwidth;
      }
      return retval;
    },
    drawtrainonedge: function (train,name,text,showvehicledetails){
      var mx1 = parseInt( this.stops[this.edges[train.edgeid].id1].x );
      var my1 = parseInt( this.stops[this.edges[train.edgeid].id1].y );
      var mx2 = parseInt( this.stops[this.edges[train.edgeid].id2].x );
      var my2 = parseInt( this.stops[this.edges[train.edgeid].id2].y );
      var edgex = parseInt( this.edges[train.edgeid].col );
      var percent = parseInt(train.passproc);
      var my = ( (100-percent) * ( my1 ) +
                 percent * ( my2 ) ) / 100;
      var stepy = my2 - my1;
      var mx = 0.0;
      if( stepy == 1 ){
        mx = (100-percent) * ( mx1 ) +
                   percent * ( mx2 );
        mx /= 100;
      } else if( percent*stepy < 100 ){
        mx = ( (100-percent*stepy) * ( mx1 ) ) / stepy +
                        percent * ( edgex );
        mx *=stepy;
        mx /= 100;
      } else if( (100-percent)*stepy < 100 ){
        mx = ( (100-(100-percent)*stepy) * ( mx2 ) ) / stepy +
                        (100-percent) * ( edgex );
        mx *=stepy;
        mx /= 100;
      } else {
        mx = edgex;
      }
      var x = this.getTransformer().getX(mx,my);
      var y = this.getTransformer().getY(mx,my);

      this.drawtraindetails(x,y,name,text)
      this.drawtrain(x,y, train);
    }

  });

  /**
   * @brief Concrete class to show Trains on a Map
   **/
  var CLookTrainDrawerMap = Class.create();
  CLookTrainDrawerMap.prototype = Object.extend(new CLookTrainDrawer(), {
    // constructor
    initialize: function() {
      this.refcount = gLookTrainDrawerReferenceCount;
      gLookTrainDrawerReferenceCount++;
      this.traindiameter = 5;
      this.maxdrawtime = 700;
    },
    drawalltrains: function( trains, shownames, showdestination ){
      if( this.mousemanager ) this.mousemanager.removeKind('1_look_train')
      var before = (new Date()).getTime();
      for( var i=0, len = trains.length; i < len; ++i){
        if( (new Date()).getTime() - before > this.maxdrawtime ){
//          CLookStatus.show( i+' of '+len+' vehicles shown' );
          return;
        }
        if( trains[i] && trains[i].prodclass && trains[i].prodclass != '' ){
          trains[i].calcx = Math.round(this.getTransformer().getX(trains[i].x,trains[i].y));
          trains[i].calcy = Math.round(this.getTransformer().getY(trains[i].x,trains[i].y));
          this.drawtrain( trains[i].calcx, trains[i].calcy, trains[i] );
          this.drawmouseover( trains[i].calcx, trains[i].calcy, trains[i] );
        }
      }
      if( shownames == true || showdestination == true ){
        for( var i=0, len = trains.length; i < len; ++i){
          if( (new Date()).getTime() - before > this.maxdrawtime ){
//            CLookStatus.show( 'Names of '+i+' from '+len+' vehicles shown' );
            return;
          }
          if( trains[i] && trains[i].name && trains[i].lstopname ){
            this.drawtraintext( trains[i].calcx, trains[i].calcy,
                                (shownames == true && trains[i].name ? trains[i].name : '') +
                                (shownames == true && showdestination == true ? ' ' : '') +
                                (showdestination == true && trains[i].lstopname ? trains[i].lstopname : '') );
          }
        }
      }
//      CLookStatus.show( len+' vehicles shown' );
    },
    drawmouseover: function( x, y, train ){
      if( this.mousemanager ){
      var title = train.name + ' ' + train.lstopname;

      if( train.delay ){

       var delay = parseInt(train.delay);

       if( delay < 2 )
        title += ' <span style=\"color:#080;font-size:x-small;\">(+' + delay + ')</span>';
       else if( delay <= 10 )
        title += ' <span style=\"color:#880;font-size:x-small;\">(+' + delay + ')</span>';
       else
         title += ' <span style=\"color:#f00;font-size:x-small;\">(+' + delay + ')</span>';
      }
        this.mousemanager.addElem({kind:'1_look_train',
                                   x:x,y:y,
                                   radius:10,
                                   title:title,
                                   titleClassName:'look_popup_title_train',
                                   train:train
                                   });
      }else alert('Mousemanager missing');
    },

    drawtraintext: function( x, y, text ){
      var elem = CNodes.getElem( 'div', 'traintext', 'absolute', 'block',
                                x + this.traindiameter, y - this.traindiameter,
                                '', '' );
      elem.className = "look_traintext";
      elem.innerHTML = '<nobr>' + text + '</nobr>';
      this.currenttextdiv.appendChild( elem );
    }
  });
}

