/*
 * station-clock.js
 *
 * Copyright (c) 2010 Ruediger Appel
 * ludi at mac dot com
 *
 * Date: 2010-08-08
 * Version: 1.0.0
 *
 * 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
 *
 *   @see http://www.3quarks.com/en/StationClock/
 */


// Implicitly create default StationClock object.

// Set manifest constants.

// clock body
StationClock.NoBody         = 0;
StationClock.SmallWhiteBody = 1;
StationClock.RoundBody      = 2;
StationClock.RoundGreenBody = 3;
StationClock.SquareBody     = 4;

// stroke dial
StationClock.NoDial               = 0;
StationClock.GermanHourStrokeDial = 1;
StationClock.GermanStrokeDial     = 2;
StationClock.AustriaStrokeDial    = 3;
StationClock.SwissStrokeDial      = 4;

// clock hour hand
StationClock.PointedHourHand = 1;
StationClock.BarHourHand     = 2;
StationClock.SwissHourHand   = 3;

// clock minute hand
StationClock.PointedMinuteHand = 1;
StationClock.BarMinuteHand     = 2;
StationClock.SwissMinuteHand   = 3;

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

// clock boss
StationClock.NoBoss    = 0;
StationClock.BlackBoss = 1;
StationClock.RedBoss   = 2;

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

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


function StationClock(clockId) {
  this.clockId = clockId;
  this.radius  = 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.SwissStrokeDial;

  // clock hands
  this.hourHand          = StationClock.SwissHourHand;
  this.minuteHand        = StationClock.SwissMinuteHand;
  this.secondHand        = StationClock.SwissSecondHand;
  this.handShadowColor   = "rgba(0,0,0,0.3)";
  this.handShadowOffsetX = 0.03;
  this.handShadowOffsetY = 0.03;
  this.handShadowBlur    = 0.04;

  // 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 behaviour
  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;
        }
      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, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.75, 0.07);
            } else {
              this.strokeLine(context, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.92, 0.026);
            }
            break;
          case StationClock.AustriaStrokeDial:
            if ((i % 5) == 0) {
              this.fillPolygon(context, 'rgb(0,0,0)', -0.04, -1.0, 0.04, -1.0, 0.03, -0.78, -0.03, -0.78);
            } else {
              this.strokeLine(context, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.94, 0.02);
            }
            break;
          case StationClock.GermanStrokeDial:
            if ((i % 15) == 0) {
              this.strokeLine(context, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.70, 0.08);
            } else if ((i % 5) == 0) {
              this.strokeLine(context, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.76, 0.08);
            } else {
              this.strokeLine(context, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.92, 0.036);
            }
            break;
          case StationClock.GermanHourStrokeDial:
            if ((i % 15) == 0) {
              this.strokeLine(context, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.70, 0.10);
            } else if ((i % 5) == 0) {
              this.strokeLine(context, 'rgb(0,0,0)', 0.0, -1.0, 0.0, -0.74, 0.08);
            }
            break;
        }
        context.restore();
      }

      // get current time
      var time    = sim.caseDate;  // Set in sim.js.
      var millis  = time.getMilliseconds() / 1000.0;
      var seconds = time.getSeconds();
      var minutes = time.getMinutes();
      var hours   = time.getHours();

      // 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, 'rgb(0,0,0)', -0.05, -0.6, 0.05, -0.6, 0.05, 0.15, -0.05, 0.15);
          break;
        case StationClock.PointedHourHand:
          this.fillPolygon(context, 'rgb(0,0,0)', 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, 'rgb(0,0,0)', -0.05, -0.6, 0.05, -0.6, 0.065, 0.26, -0.065, 0.26);
          break;
      }
      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, 'rgb(0,0,0)', -0.05, -0.9, 0.05, -0.9, 0.05, 0.15, -0.05, 0.15);
          break;
        case StationClock.PointedMinuteHand:
          this.fillPolygon(context, 'rgb(0,0,0)', 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, 'rgb(0,0,0)', -0.035, -0.93, 0.035, -0.93, 0.05, 0.25, -0.05, 0.25);
          break;
      }
      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, 'rgb(200,0,0)', -0.006, -0.92, 0.006, -0.92, 0.028, 0.23, -0.028, 0.23);
          break;
        case StationClock.HoleShapedSecondHand:
          this.fillPolygon(context, 'rgb(200,0,0)', 0.0, -0.9, 0.011, -0.889, 0.01875, -0.6, -0.01875, -0.6, -0.011, -0.889);
          this.fillPolygon(context, 'rgb(200,0,0)', 0.02, -0.4, 0.025, 0.22, -0.025, 0.22, -0.02, -0.4);
          this.strokeCircle(context, 'rgb(200,0,0)', 0, -0.5, 0.083, 0.066);
          break;
        case StationClock.NewHoleShapedSecondHand:
          this.fillPolygon(context, 'rgb(200,0,0)', 0.0, -0.95, 0.015, -0.935, 0.0187, -0.65, -0.0187, -0.65, -0.015, -0.935);
          this.fillPolygon(context, 'rgb(200,0,0)', 0.022, -0.45, 0.03, 0.27, -0.03, 0.27, -0.022, -0.45);
          this.strokeCircle(context, 'rgb(200,0,0)', 0, -0.55, 0.085, 0.07);
          break;
        case StationClock.SwissSecondHand:
          this.strokeLine(context, 'rgb(200,0,0)', 0.0, -0.6, 0.0, 0.35, 0.026);
          this.fillCircle(context, 'rgb(200,0,0)', 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;
        }
        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();
  }
}


