/*!
 * station-clock.js
 *
 * Copyright (c) 2010 Ruediger Appel
 * ludi at mac dot com
 *
 * Date: 2016-02-16
 * Version: 1.0.1
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Known Issues:
 *
 *   Shadows for some second hands is not on one layer
 * 
 * Thanks to Paul Schröfl for the Wiener Würfeluhr
 */

// clock body (Uhrgehäuse)
StationClock.NoBody         = 0;
StationClock.SmallWhiteBody = 1;
StationClock.RoundBody      = 2;
StationClock.RoundGreenBody = 3;
StationClock.SquareBody     = 4;
StationClock.ViennaBody     = 5;

// stroke dial (Zifferblatt)
StationClock.NoDial               = 0;
StationClock.GermanHourStrokeDial = 1;
StationClock.GermanStrokeDial     = 2;
StationClock.AustriaStrokeDial    = 3;
StationClock.SwissStrokeDial      = 4;
StationClock.ViennaStrokeDial     = 5;

//clock hour hand (Stundenzeiger)
StationClock.PointedHourHand = 1;
StationClock.BarHourHand     = 2;
StationClock.SwissHourHand   = 3;
StationClock.ViennaHourHand  = 4;

//clock minute hand (Minutenzeiger)
StationClock.PointedMinuteHand = 1;
StationClock.BarMinuteHand     = 2;
StationClock.SwissMinuteHand   = 3;
StationClock.ViennaMinuteHand  = 4;

//clock second hand (Sekundenzeiger)
StationClock.NoSecondHand            = 0;
StationClock.BarSecondHand           = 1;
StationClock.HoleShapedSecondHand    = 2;
StationClock.NewHoleShapedSecondHand = 3;
StationClock.SwissSecondHand         = 4;

// clock boss (Zeigerabdeckung)
StationClock.NoBoss     = 0;
StationClock.BlackBoss  = 1;
StationClock.RedBoss    = 2;
StationClock.ViennaBoss = 3;

// minute hand behavoir
StationClock.CreepingMinuteHand        = 0;
StationClock.BouncingMinuteHand        = 1;
StationClock.ElasticBouncingMinuteHand = 2;

// second hand behavoir
StationClock.CreepingSecondHand        = 0;
StationClock.BouncingSecondHand        = 1;
StationClock.ElasticBouncingSecondHand = 2;
StationClock.OverhastySecondHand       = 3;


function StationClock(clockId) {
  this.clockId = clockId; 
  this.radius  = 0;

  // hour offset
  this.hourOffset = 0;
  
  // clock body
  this.body              = StationClock.RoundBody;
  this.bodyShadowColor   = "rgba(0,0,0,0.5)";
  this.bodyShadowOffsetX = 0.03;
  this.bodyShadowOffsetY = 0.03;
  this.bodyShadowBlur    = 0.06;
  
  // body dial
  this.dial              = StationClock.GermanStrokeDial;
  this.dialColor         = 'rgb(60,60,60)';
  
  // clock hands
  this.hourHand          = StationClock.PointedHourHand;
  this.minuteHand        = StationClock.PointedMinuteHand;
  this.secondHand        = StationClock.HoleShapedSecondHand;
  this.handShadowColor   = 'rgba(0,0,0,0.3)';
  this.handShadowOffsetX = 0.03;
  this.handShadowOffsetY = 0.03;
  this.handShadowBlur    = 0.04;
	
	// clock colors
	this.hourHandColor     = 'rgb(0,0,0)';
	this.minuteHandColor   = 'rgb(0,0,0)';
	this.secondHandColor   = 'rgb(200,0,0)';
  
  // clock boss
  this.boss              = StationClock.NoBoss;
  this.bossShadowColor   = "rgba(0,0,0,0.2)";
  this.bossShadowOffsetX = 0.02;
  this.bossShadowOffsetY = 0.02;
  this.bossShadowBlur    = 0.03;
  
  // hand behavoir
  this.minuteHandBehavoir = StationClock.CreepingMinuteHand;
  this.secondHandBehavoir = StationClock.OverhastySecondHand;
  
  // hand animation
  this.minuteHandAnimationStep = 0;
  this.secondHandAnimationStep = 0;
  this.lastMinute = 0;
  this.lastSecond = 0;
};

StationClock.prototype.draw = function() {
  var clock = document.getElementById(this.clockId);
  if (clock) {
    var context = clock.getContext('2d');
    if (context) {
      this.radius = 0.75 * (Math.min(clock.width, clock.height) / 2);
      
      // clear canvas and set new origin
      context.clearRect(0, 0, clock.width, clock.height);
      context.save();
      context.translate(clock.width / 2, clock.height / 2);
      
      // draw body
      if (this.body != StationClock.NoStrokeBody) {
        context.save();
        switch (this.body) {
    		  case StationClock.SmallWhiteBody:
    		    this.fillCircle(context, "rgb(255,255,255)", 0, 0, 1);
    		    break;
          case StationClock.RoundBody:
            this.fillCircle(context, "rgb(255,255,255)", 0, 0, 1.1);
            context.save();
            this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur);
            this.strokeCircle(context, "rgb(0,0,0)", 0, 0, 1.1, 0.07);
            context.restore();
            break;
          case StationClock.RoundGreenBody:
            this.fillCircle(context, "rgb(235,236,212)", 0, 0, 1.1);
            context.save();
            this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur);
            this.strokeCircle(context, "rgb(180,180,180)", 0, 0, 1.1, 0.2);
            context.restore();
            this.strokeCircle(context, "rgb(29,84,31)", 0, 0, 1.15, 0.1);
            context.save();
            this.setShadow(context, "rgba(235,236,212,100)", -0.02, -0.02, 0.09);
            this.strokeCircle(context, 'rgb(76,128,110)', 0, 0, 1.1, 0.08);
            context.restore();
            break;
    		  case StationClock.SquareBody:
            context.save();
            this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur);
            this.fillSquare(context, 'rgb(237,235,226)', 0, 0, 2.4);
            this.strokeSquare(context, 'rgb(38,106,186)', 0, 0, 2.32, 0.16);
            context.restore();
      			context.save();
      			this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur);
            this.strokeSquare(context, 'rgb(42,119,208)', 0, 0, 2.24, 0.08);
      			context.restore();
      			break;
          case StationClock.ViennaBody:
            context.save();
            this.fillSymmetricPolygon(context, 'rgb(156,156,156)', [[-1.2,1.2],[-1.2,-1.2]],0.1);
            this.fillPolygon(context, 'rgb(156,156,156)', 0,1.2 , 1.2,1.2 , 1.2,0);
            this.fillCircle(context, 'rgb(255,255,255)', 0, 0, 1.05, 0.08);
            this.strokeCircle(context, 'rgb(0,0,0)', 0, 0, 1.05, 0.01);
            this.strokeCircle(context, 'rgb(100,100,100)', 0, 0, 1.1, 0.01);
            this.fillPolygon(context, 'rgb(100,100,100)', 0.45,1.2 , 1.2,1.2 , 1.2,0.45);
            this.fillPolygon(context, 'rgb(170,170,170)', 0.45,-1.2 , 1.2,-1.2 , 1.2,-0.45);
            this.fillPolygon(context, 'rgb(120,120,120)', -0.45,1.2 , -1.2,1.2 , -1.2,0.45);
            this.fillPolygon(context, 'rgb(200,200,200)', -0.45,-1.2 , -1.2,-1.2 , -1.2,-0.45);
            this.strokeSymmetricPolygon(context, 'rgb(156,156,156)', [[-1.2,1.2],[-1.2,-1.2]],0.01);
            this.fillPolygon(context, 'rgb(255,0,0)', 0.05,-0.6 , 0.15,-0.6 , 0.15,-0.45 , 0.05,-0.45);
            this.fillPolygon(context, 'rgb(255,0,0)', -0.05,-0.6 , -0.15,-0.6 , -0.15,-0.45 , -0.05,-0.45);
            this.fillPolygon(context, 'rgb(255,0,0)', 0.05,-0.35 , 0.15,-0.35 , 0.15,-0.30 ,  0.10,-0.20 , 0.05,-0.20);
            this.fillPolygon(context, 'rgb(255,0,0)', -0.05,-0.35 , -0.15,-0.35 , -0.15,-0.30 ,  -0.10,-0.20 , -0.05,-0.20);
            context.restore();
            break;
            }
          context.restore();
      }
      
      // draw dial
      for (var i = 0; i < 60; i++) {
        context.save();
        context.rotate(i * Math.PI / 30);
        switch (this.dial) {
          case StationClock.SwissStrokeDial:
            if ((i % 5) == 0) {
              this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.75, 0.07);
            } else {
              this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.92, 0.026);
            }
            break;
          case StationClock.AustriaStrokeDial:
            if ((i % 5) == 0) {
              this.fillPolygon(context, this.dialColor, -0.04, -1.0, 0.04, -1.0, 0.03, -0.78, -0.03, -0.78);
            } else {
              this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.94, 0.02);
            }
            break;
          case StationClock.GermanStrokeDial:
          	if ((i % 15) == 0) {
              this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.70, 0.08);
          	} else if ((i % 5) == 0) {
              this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.76, 0.08);
          	} else {
          	  this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.92, 0.036);
          	}
          	break;
          case StationClock.GermanHourStrokeDial:
          	if ((i % 15) == 0) {
              this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.70, 0.10);
          	} else if ((i % 5) == 0) {
              this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.74, 0.08);
          	}
          	break;
         case StationClock.ViennaStrokeDial:
            if ((i % 15) == 0) {
              this.fillPolygon(context, this.dialColor, 0.7,-0.1, 0.6,0, 0.7,0.1,  1,0.03,  1,-0.03);
            } else if ((i % 5) == 0) {
              this.fillPolygon(context, this.dialColor, 0.85,-0.06, 0.78,0, 0.85,0.06,  1,0.03,  1,-0.03);
            }
            this.fillCircle(context, this.dialColor, 0.0, -1.0, 0.03);
            break;
        }
        context.restore();
      }

      // get current time
      var time    = new Date();
      var millis  = time.getMilliseconds() / 1000.0;
      var seconds = time.getSeconds();
      var minutes = time.getMinutes();
      var hours   = time.getHours() + this.hourOffset;

      // draw hour hand
      context.save();
      context.rotate(hours * Math.PI / 6 + minutes * Math.PI / 360);
      this.setShadow(context, this.handShadowColor, this.handShadowOffsetX, this.handShadowOffsetY, this.handShadowBlur);
      switch (this.hourHand) {
        case StationClock.BarHourHand:
          this.fillPolygon(context, this.hourHandColor, -0.05, -0.6, 0.05, -0.6, 0.05, 0.15, -0.05, 0.15);
      	  break;
        case StationClock.PointedHourHand:
          this.fillPolygon(context, this.hourHandColor, 0.0, -0.6,  0.065, -0.53, 0.065, 0.19, -0.065, 0.19, -0.065, -0.53);
          break;
        case StationClock.SwissHourHand:
          this.fillPolygon(context, this.hourHandColor, -0.05, -0.6, 0.05, -0.6, 0.065, 0.26, -0.065, 0.26);
          break;
        case StationClock.ViennaHourHand:
          this.fillSymmetricPolygon(context, this.hourHandColor, [[-0.02,-0.72],[-0.08,-0.56],[-0.15,-0.45],[-0.06,-0.30],[-0.03,0],[-0.1,0.2],[-0.05,0.23],[-0.03,0.2]]);
      }
      context.restore();
      
      // draw minute hand
      context.save();
      switch (this.minuteHandBehavoir) {
        case StationClock.CreepingMinuteHand:
          context.rotate((minutes + seconds / 60) * Math.PI / 30);
  	      break;
        case StationClock.BouncingMinuteHand:
          context.rotate(minutes * Math.PI / 30);
  	      break;
        case StationClock.ElasticBouncingMinuteHand:
          if (this.lastMinute != minutes) {
            this.minuteHandAnimationStep = 3;
            this.lastMinute = minutes;
          }
          context.rotate((minutes + this.getAnimationOffset(this.minuteHandAnimationStep)) * Math.PI / 30);
          this.minuteHandAnimationStep--;
          break;
      }
      this.setShadow(context, this.handShadowColor, this.handShadowOffsetX, this.handShadowOffsetY, this.handShadowBlur);
      switch (this.minuteHand) {
        case StationClock.BarMinuteHand:
          this.fillPolygon(context, this.minuteHandColor, -0.05, -0.9, 0.035, -0.9, 0.035, 0.23, -0.05, 0.23);
      	  break;
        case StationClock.PointedMinuteHand:
          this.fillPolygon(context, this.minuteHandColor, 0.0, -0.93,  0.045, -0.885, 0.045, 0.23, -0.045, 0.23, -0.045, -0.885);
        	break;
        case StationClock.SwissMinuteHand:
        	this.fillPolygon(context, this.minuteHandColor, -0.035, -0.93, 0.035, -0.93, 0.05, 0.25, -0.05, 0.25);
        	break;
        case StationClock.ViennaMinuteHand:
          this.fillSymmetricPolygon(context, this.minuteHandColor, [[-0.02,-0.98],[-0.09,-0.7],[-0.03,0],[-0.05,0.2],[-0.01,0.4]]);
      }
      context.restore();
      
      // draw second hand
      context.save();
      switch (this.secondHandBehavoir) {
        case StationClock.OverhastySecondHand:
          context.rotate(Math.min((seconds + millis) * (60.0 / 58.5), 60.0) * Math.PI / 30);
          break;
        case StationClock.CreepingSecondHand:
          context.rotate((seconds + millis) * Math.PI / 30);
          break;
        case StationClock.BouncingSecondHand:
          context.rotate(seconds * Math.PI / 30);
          break;
        case StationClock.ElasticBouncingSecondHand:
          if (this.lastSecond != seconds) {
            this.secondHandAnimationStep = 3;
            this.lastSecond = seconds;
          }
          context.rotate((seconds + this.getAnimationOffset(this.secondHandAnimationStep)) * Math.PI / 30);
          this.secondHandAnimationStep--;
          break;
      }
      this.setShadow(context, this.handShadowColor, this.handShadowOffsetX, this.handShadowOffsetY, this.handShadowBlur);
      switch (this.secondHand) {
        case StationClock.BarSecondHand:
      	  this.fillPolygon(context, this.secondHandColor, -0.006, -0.92, 0.006, -0.92, 0.028, 0.23, -0.028, 0.23);
      	  break;
        case StationClock.HoleShapedSecondHand:
          this.fillPolygon(context, this.secondHandColor, 0.0, -0.9, 0.011, -0.889, 0.01875, -0.6, -0.01875, -0.6, -0.011, -0.889);
          this.fillPolygon(context, this.secondHandColor, 0.02, -0.4, 0.025, 0.22, -0.025, 0.22, -0.02, -0.4);
          this.strokeCircle(context, this.secondHandColor, 0, -0.5, 0.083, 0.066);
      	  break;
        case StationClock.NewHoleShapedSecondHand:
          this.fillPolygon(context, this.secondHandColor, 0.0, -0.95, 0.015, -0.935, 0.0187, -0.65, -0.0187, -0.65, -0.015, -0.935);
          this.fillPolygon(context, this.secondHandColor, 0.022, -0.45, 0.03, 0.27, -0.03, 0.27, -0.022, -0.45);
          this.strokeCircle(context, this.secondHandColor, 0, -0.55, 0.085, 0.07);
      	  break;
        case StationClock.SwissSecondHand:
      	  this.strokeLine(context, this.secondHandColor, 0.0, -0.6, 0.0, 0.35, 0.026);
      	  this.fillCircle(context, this.secondHandColor, 0, -0.64, 0.1);
          break;
        case StationClock.ViennaSecondHand:
          this.strokeLine(context, this.secondHandColor, 0.0, -0.6, 0.0, 0.35, 0.026);
          this.fillCircle(context, this.secondHandColor, 0, -0.64, 0.1);
          break;
      }
      context.restore();
      
      // draw clock boss
      if (this.boss != StationClock.NoBoss) {
        context.save();
        this.setShadow(context, this.bossShadowColor, this.bossShadowOffsetX, this.bossShadowOffsetY, this.bossShadowBlur);
        switch (this.boss) {
    		  case StationClock.BlackBoss:
    		    this.fillCircle(context, 'rgb(0,0,0)', 0, 0, 0.1);
    		    break;
    		  case StationClock.RedBoss:
    		    this.fillCircle(context, 'rgb(220,0,0)', 0, 0, 0.06);
    		    break;
          case StationClock.ViennaBoss:
            this.fillCircle(context, 'rgb(0,0,0)', 0, 0, 0.07);
            break;
        }
        context.restore();
      }
      
      context.restore();
    }
  }
};

StationClock.prototype.getAnimationOffset = function(animationStep) {
  switch (animationStep) {
    case 3: return  0.2;
    case 2: return -0.1;
    case 1: return  0.05;
  }
  return 0;
};

StationClock.prototype.setShadow = function(context, color, offsetX, offsetY, blur) {
  if (color) {
  	context.shadowColor   = color;
  	context.shadowOffsetX = this.radius * offsetX;
  	context.shadowOffsetY = this.radius * offsetY;
  	context.shadowBlur    = this.radius * blur;
  }
};

StationClock.prototype.fillCircle = function(context, color, x, y, radius) {
  if (color) {
    context.beginPath();
    context.fillStyle = color;
    context.arc(x * this.radius, y * this.radius, radius * this.radius, 0, 2 * Math.PI, true);
    context.fill();
  }
};

StationClock.prototype.strokeCircle = function(context, color, x, y, radius, lineWidth) {
  if (color) {
    context.beginPath();
    context.strokeStyle = color;
    context.lineWidth = lineWidth * this.radius;
    context.arc(x * this.radius, y * this.radius, radius * this.radius, 0, 2 * Math.PI, true);
    context.stroke();
  }
};

StationClock.prototype.fillSquare = function(context, color, x, y, size) {
  if (color) {
    context.fillStyle = color;
    context.fillRect((x - size / 2) * this.radius, (y -size / 2) * this.radius, size * this.radius, size * this.radius);
  }
};

StationClock.prototype.strokeSquare = function(context, color, x, y, size, lineWidth) {
  if (color) {
    context.strokeStyle = color;
    context.lineWidth = lineWidth * this.radius;
    context.strokeRect((x - size / 2) * this.radius, (y -size / 2) * this.radius, size * this.radius, size * this.radius);
  }
};

StationClock.prototype.strokeLine = function(context, color, x1, y1, x2, y2, width) {
  if (color) {
	  context.beginPath();
	  context.strokeStyle = color;
	  context.moveTo(x1 * this.radius, y1 * this.radius);
	  context.lineTo(x2 * this.radius, y2 * this.radius);
	  context.lineWidth = width * this.radius;
	  context.stroke();
  }
};

StationClock.prototype.fillPolygon = function(context, color, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5) {
  if (color) {
	  context.beginPath();
	  context.fillStyle = color;
	  context.moveTo(x1 * this.radius, y1 * this.radius);
	  context.lineTo(x2 * this.radius, y2 * this.radius);
	  context.lineTo(x3 * this.radius, y3 * this.radius);
	  context.lineTo(x4 * this.radius, y4 * this.radius);
	  if ((x5 != undefined) && (y5 != undefined)) {
	    context.lineTo(x5 * this.radius, y5 * this.radius);
	  }
	  context.lineTo(x1 * this.radius, y1 * this.radius);
	  context.fill();
  }
};

StationClock.prototype.fillSymmetricPolygon = function(context, color, points) {
    context.beginPath();
    context.fillStyle = color;
    context.moveTo(points[0][0] * this.radius, points[0][1] * this.radius);
    for (var i = 1; i < points.length; i++) {
      context.lineTo(points[i][0] * this.radius, points[i][1] * this.radius);
    }
    for (var i = points.length - 1; i >= 0; i--) {
      context.lineTo(0 - points[i][0] * this.radius, points[i][1] * this.radius);
    }
    context.lineTo(points[0][0] * this.radius, points[0][1] * this.radius);
    context.fill();
};

StationClock.prototype.strokeSymmetricPolygon = function(context, color, points, width) {
    context.beginPath();
    context.strokeStyle = color;
    context.moveTo(points[0][0] * this.radius, points[0][1] * this.radius);
    for (var i = 1; i < points.length; i++) {
      context.lineTo(points[i][0] * this.radius, points[i][1] * this.radius);
    }
    for (var i = points.length - 1; i >= 0; i--) {
      context.lineTo(0 - points[i][0] * this.radius, points[i][1] * this.radius);
    }
    context.lineTo(points[0][0] * this.radius, points[0][1] * this.radius);
    context.lineWidth = width * this.radius;
    context.stroke();
};