// UpcClock class, October 2001
//
// A very nifty UPC symbol that is actually a clock. The time is displayed with
// UPC digits (hereinafter referred to as "zebras," just for kicks)
// representing the digits from 0 to 9.
//
// Please note that every UPC displayed is in fact a completely valid UPC that
// complies with all known UPC rules in every way. I spent quite a bit of time
// researching UPCs, so it's all accurate to the best of my knowledge.
//
// Source: http://www.howstuffworks.com/upc.htm

import aLibrary.*;
import java.awt.*;

public class UpcClock extends Clock {

  /*
   * CLASS VARIABLES
   */

  private UpcDigit[] zebra = new UpcDigit[12];  // our various zebras
  private AImage[] numeral = new AImage[12]; // for the numerals below the UPC

  /*
   * CONSTRUCTOR
   */

  public UpcClock(int x, int y, int w, int h) {
  // Creates a new UpcClock based on (x, y, w, h) coordinates.
  //
  // Preconditions:
  //   none
  //
  // Postconditions:
  //   A new UpcClock is created at (x, y) with width w and height h.
  //   The various zebras are instantiated and placed on this.

    super(x, y, w, h);  // call Clock constructor

    double xUnit = 7d * w / 117d;
    // A factor made into one number to simplify the following expression a bit.

    double thisShiftFactor, nextShiftFactor;
    int thisX, nextX, thisHeight;
    // Four numbers that come in handy in placing the zebras and other stuff.

    for (int i = 0; i < 12; i++) {
      thisShiftFactor = (i > 5) ? 5d * xUnit / 7d : 0d;
      nextShiftFactor = (i > 5) ? 5d * xUnit / 7d : 0d;
      thisX = (int)(((i + 2) * xUnit) + thisShiftFactor + 0.5);
      nextX = (int)(((i + 3) * xUnit) + nextShiftFactor + 0.5);
      thisHeight = (int)((i % 11 != 0) ? 6d/7d * h + 0.5 : 13d/14d * h + 0.5);
      zebra[i] = new UpcDigit(thisX, 0, nextX - thisX, thisHeight);
//      zebra[i].setNumber(i % 10, i > 5);
      zebra[i].place(this);
    }
    // Create and place all zebras on this clock.

    double[] xLoc = {11d/7d, 13d/7d, 57d/7d, 59/7d, 103d/7d, 105d/7d};
    ARectangle mark;
    for (int i = 0; i < 6; i++) {
      thisX = (int)((xLoc[i] * xUnit) + 0.5);
      nextX = (int)(((xLoc[i] + 1d/7d) * xUnit) + 0.5);
      mark = new ARectangle(thisX, 0, nextX - thisX, (int)(13d/14d * h + 0.5));
      mark.setToFill();
      mark.place(this);
    }
    // Create and place the end and center descriptors.

    numeral[0] = new AImage((int)(0.6 * xUnit + 0.5), (int)(33d/35d * h + 0.5),
      (int)(0.4 * xUnit + 0.5), (int)(2d/35d * h + 0.5));
    numeral[0].place(this);
    numeral[11] = new AImage((int)(110d/7d * xUnit + 0.5),
      (int)(33d/35d * h + 0.5), (int)(0.4 * xUnit + 0.5),
      (int)(2d/35d * h + 0.5));
    numeral[11].place(this);
    // Create and place first and last digit (small).

    for (int i = 1; i < 11; i++) {
      thisShiftFactor = (i > 5) ? 5d/7d * xUnit : 0d;
      nextShiftFactor = (i + 1 > 5) ? 5d/7d * xUnit : 0d;
      thisX = (int)(((i + 2.1) * xUnit) + thisShiftFactor + 0.5);
      nextX = (int)(((i + 3.1) * xUnit) + nextShiftFactor + 0.5);
      numeral[i] = new AImage(thisX, (int)(31d/35d * h + 0.5),
        (int)(0.8 * xUnit + 0.5), (int)(0.8 * h / 7d + 0.5));
      numeral[i].place(this);
    }
    // Create and place remaining ten digits.

    setDescription("UPC clock");
    // a nice default description

  }

  /*
   * UPDATE METHODS
   */

  public void showTime(double time) {
  // Sets this clock to show time, but does not repaint. We must override this
  // method to inherit from Clock.
  //
  // Preconditions:
  //   time >= 0
  //
  // Postconditions:
  //   This UpcClock is redrawn to show time.

    int[] thisDigit = new int[12];
    // An array to store the digits of this UPC.

    for (int i = 0; i < 6; i++)
      thisDigit[i] = Timekeeper.getDigit(time, i, true);
    // Figure out the first 6 digits (the time).

    for (int i = 10; i > 5; i--) {
      thisDigit[i] = (int)(time) % 10;
      time /= 10;
    }
    // Figure out the next 5 digits (number of seconds since midnight).

    int sum = 0;
    for (int i = 0; i < 11; i++)
      sum += thisDigit[i] * ((i % 2 == 0) ? 3 : 1);
    thisDigit[11] = (10 - (sum % 10)) % 10;
    // Calculate the checksum (the last digit).
    // See http://www.howstuffworks.com/upc.htm for a more detailed explanation.

    for (int i = 0; i < 12; i++) {
      zebra[i].setNumber(thisDigit[i], i > 5);
      numeral[i].setImage(thisDigit[i] + ".gif");
    }
    // Update all zebras and numerals.

  }

}