import java.io.*;
import java.lang.*;
import java.awt.*;
import java.awt.image.*;

public class ImageToBmp
{

  /* file layout variables */

  char letterB;
  char letterM;
  int fileSize;
  short reserved1;
  short reserved2;
  short pixelArrayOffset;
  short reserved3;
  int structSize;
  int imageWidth;
  int imageHeight;
  short nPlanes;
  short bitCount;
  int compression;
  int imageSize;
  int xPixelsPerMeter;
  int yPixelsPerMeter;
  int colorsUsed;
  int colorsImportant;

  int scanLinePadSize;

  int[] pixels;

  byte[] binary;
  int binaryIdx = 0;

  public ImageToBmp(Image image, int width, int height) {
    initStructValues(width, height);
    binary = new byte[fileSize];
    ImageToPixels imageToPixels = new ImageToPixels(image, width, height);
    pixels = imageToPixels.grabPixels();
    saveBinary();
  }

  private void initStructValues (int inputWidth, int inputHeight) {
    int pixelSize = 3;

    scanLinePadSize = inputWidth % 4;
    letterB = 'B';
    letterM = 'M';
    fileSize = 54 + (inputWidth * pixelSize + scanLinePadSize) * inputHeight;
    reserved1 = 0;
    reserved2 = 0;
    pixelArrayOffset = 54;
    reserved3 = 0;
    structSize = 40;
    imageWidth = inputWidth;
    imageHeight = inputHeight;
    nPlanes = 1;
    bitCount = 24;
    compression = 0;
    imageSize = 0;
    xPixelsPerMeter = 0;
    yPixelsPerMeter = 0;
    colorsUsed = 0;
    colorsImportant = 0;
  }

  private void saveByte(byte value) {
    binary[binaryIdx] = value;
    binaryIdx++;
  }

  private void saveShort(short value) {
    saveByte((byte)(value & 0xff));
    saveByte((byte)((value >> 8) & 0xff));
  }

  private void saveInteger(int value) {
    saveByte((byte)(value & 0xff));
    saveByte((byte)((value >> 8) & 0xff));
    saveByte((byte)((value >> 16) & 0xff));
    saveByte((byte)((value >> 24) & 0xff));
  }

  private void savePixel(int value) {
    saveByte((byte)((value >> 16) & 0xff));
    saveByte((byte)((value >> 8) & 0xff));
    saveByte((byte)(value & 0xff));
  }

  private void saveBinary() {
    int heightIdx;
    int widthIdx;
    int padIdx;
    byte padValue = 0;

    saveByte((byte)letterB);
    saveByte((byte)letterM);
    saveInteger(fileSize);
    saveShort(reserved1);
    saveShort(reserved2);
    saveShort(pixelArrayOffset);
    saveShort(reserved3);
    saveInteger(structSize);
    saveInteger(imageWidth);
    saveInteger(imageHeight);
    saveShort(nPlanes);
    saveShort(bitCount);
    saveInteger(compression);
    saveInteger(imageSize);
    saveInteger(xPixelsPerMeter);
    saveInteger(yPixelsPerMeter);
    saveInteger(colorsUsed);
    saveInteger(colorsImportant);

    for (heightIdx = imageHeight - 1; heightIdx >= 0; heightIdx--) {
      for (widthIdx = 0; widthIdx < imageWidth; widthIdx++)
        savePixel(pixels[heightIdx * imageWidth + widthIdx]); 
      if (scanLinePadSize != 0)
        for (padIdx = 0; padIdx < scanLinePadSize; padIdx++)
          saveByte(padValue); 
    }
  }

  public byte[] getBinary() {
    return binary;
  }
}
