/*
 * Copyright (c) 2009, Takeyuki Nagao
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 * 
 *  * Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package jp.sourceforge.dvibrowser.dvicore.render;

import jp.sourceforge.dvibrowser.dvicore.DviRect;
import jp.sourceforge.dvibrowser.dvicore.DviResolution;
import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;

public class BinaryImage
{
  private final byte [] buf;
  private final int pitch;
  private final int width;
  private final int height;

  public BinaryImage(int width, int height)
  {
    this.width = width;
    this.height = height;
    this.pitch = (width + 7) >>> 3;
    this.buf = new byte [pitch * height];
  }

  public BinaryImage(byte [] buf, int width, int height)
  {
    this.width = width;
    this.height = height;
    this.pitch = (width + 7) >>> 3;
    this.buf = buf;
    if (buf.length != pitch * height)
      throw new IllegalArgumentException
        ("buffer size mismatch.");
  }

  public byte [] getBuffer() { return buf; }

  public int width() { return width; }
  public int height() { return height; }
  public int pitch() { return pitch; }

  public void fill(int c)
  {
    for (int i=0; i<buf.length; i++)
      buf[i] = (byte) c;
  }

  public int getPixel(int x, int y)
  {
    if (x < 0 || width <= x)
      throw new ArrayIndexOutOfBoundsException
        (String.valueOf(x));
    if (y < 0 || height <= y)
      throw new ArrayIndexOutOfBoundsException
        (String.valueOf(y));

    final int bytePos = (x >>> 3);
    final byte bitMask = (byte)(1 << (7 - (x & 7)));
    return (0 != (buf[bytePos + y * pitch] & bitMask)) ? 1 : 0;
  }

  public void setPixel(int x, int y, int val)
  {
    if (x < 0 || width <= x)
      throw new ArrayIndexOutOfBoundsException
        (String.valueOf(x));
    if (y < 0 || height <= y)
      throw new ArrayIndexOutOfBoundsException
        (String.valueOf(y));

    final int bytePos = (x >>> 3);
    final int bitMask = 1 << (7 - (x & 7));
    byte c = (byte)(buf[bytePos + y * pitch] & ((0xff) ^ bitMask));
    if (val != 0) c |= bitMask;

    buf[bytePos + y * pitch] = c;
  }

  public void dump()
  {
    for (int i=0; i<height; i++) {
      StringBuilder sb = new StringBuilder();
      for (int j=0; j<width; j++) {
        if (0 != getPixel(j, i))
          sb.append("*");
        else 
          sb.append(".");
      }
      System.out.println(sb.toString());
    }
  }



  public BinaryDevice getBinaryDevice(DviResolution res)
  {
    return new BinaryDeviceImpl(res);
  }

  // TDOO: make this a static class
  public final class BinaryDeviceImpl
  extends AbstractDevice
  implements BinaryDevice
  {
    private BinaryDeviceImpl(DviResolution res) {
      super(res);
    }

    public DviRect getBounds() {
      return new DviRect(-point.x, -point.y, width, height);
    }

    public void begin() {
    }
    public void end() {
    }
    
    private int w;
//    private int h;
    private int y;
    private byte [] l_buf;
    private int xx;
    public boolean beginRaster(int w, int h) {
      this.w = w;
//      this.h = h;
      l_buf = new byte [(w + 7) >>> 3];
      y = 0;
      return true;
    }
    public void endRaster() {
    }

    public void beginLine() {
      for (int i=0; i<l_buf.length; i++) l_buf[i] = 0;
      xx = 0;
    }
    public void endLine(int repeat) {
      for (int i=0; i<=repeat; i++) {
        for (int j=0; j<w; j++) {
          int bit = l_buf[(j >>> 3)] & (1 << (7 - (j & 7)));
          if (bit != 0) {
            setPixel(point.x + j, point.y + y, 1);
          }
        }
        y++;
      }
    }
  
    public void putBits(int count, boolean paintFlag) {
      if (paintFlag) {
        while (count-- > 0) {
          l_buf[(xx >>> 3)] |= 1 << (7 - (xx & 7));
          xx++;
        }
      } else {
        xx += count;
      }
    }
  }
}
