package jxl.write.biff;

import common.Logger;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import jxl.CellType;
import jxl.biff.ByteData;
import jxl.biff.CellReferenceHelper;
import jxl.biff.IndexMapping;
import jxl.biff.IntegerHelper;
import jxl.biff.Type;
import jxl.biff.WritableRecordData;
import jxl.biff.XFRecord;
import jxl.write.Number;

class RowRecord extends WritableRecordData {
  private static final Logger logger = Logger.getLogger(RowRecord.class);
  
  private byte[] data;
  
  private CellValue[] cells;
  
  private int rowHeight;
  
  private boolean collapsed;
  
  private int rowNumber;
  
  private int numColumns;
  
  private int xfIndex;
  
  private XFRecord style;
  
  private boolean defaultFormat;
  
  private boolean matchesDefFontHeight;
  
  private static final int growSize = 10;
  
  private static final int maxRKValue = 536870911;
  
  private static final int minRKValue = -536870912;
  
  private static int defaultHeightIndicator = 255;
  
  private static int maxColumns = 256;
  
  public RowRecord(int rn) {
    super(Type.ROW);
    this.rowNumber = rn;
    this.cells = new CellValue[0];
    this.numColumns = 0;
    this.rowHeight = defaultHeightIndicator;
    this.collapsed = false;
    this.matchesDefFontHeight = true;
  }
  
  public void setRowHeight(int h) {
    if (h == 0) {
      setCollapsed(true);
      this.matchesDefFontHeight = false;
    } else {
      this.rowHeight = h;
      this.matchesDefFontHeight = false;
    } 
  }
  
  void setRowDetails(int height, boolean mdfh, boolean col, XFRecord xfr) {
    this.rowHeight = height;
    this.collapsed = col;
    this.matchesDefFontHeight = mdfh;
    if (xfr != null) {
      this.defaultFormat = true;
      this.style = xfr;
      this.xfIndex = this.style.getXFIndex();
    } 
  }
  
  public void setCollapsed(boolean c) {
    this.collapsed = c;
  }
  
  public int getRowNumber() {
    return this.rowNumber;
  }
  
  public void addCell(CellValue cv) {
    int col = cv.getColumn();
    if (col >= maxColumns) {
      logger.warn("Could not add cell at " + CellReferenceHelper.getCellReference(cv.getRow(), cv.getColumn()) + " because it exceeds the maximum column limit");
      return;
    } 
    if (col >= this.cells.length) {
      CellValue[] oldCells = this.cells;
      this.cells = new CellValue[Math.max(oldCells.length + 10, col + 1)];
      System.arraycopy(oldCells, 0, this.cells, 0, oldCells.length);
      oldCells = null;
    } 
    this.cells[col] = cv;
    this.numColumns = Math.max(col + 1, this.numColumns);
  }
  
  public void removeCell(int col) {
    if (col >= this.numColumns)
      return; 
    this.cells[col] = null;
  }
  
  public void write(File outputFile) throws IOException {
    outputFile.write((ByteData)this);
  }
  
  public void writeCells(File outputFile) throws IOException {
    ArrayList integerValues = new ArrayList();
    boolean integerValue = false;
    for (int i = 0; i < this.numColumns; i++) {
      integerValue = false;
      if (this.cells[i] != null) {
        if (this.cells[i].getType() == CellType.NUMBER) {
          Number nc = (Number)this.cells[i];
          if (nc.getValue() == (int)nc.getValue() && nc.getValue() < 5.36870911E8D && nc.getValue() > -5.36870912E8D && nc.getCellFeatures() == null)
            integerValue = true; 
        } 
        if (integerValue) {
          integerValues.add(this.cells[i]);
        } else {
          writeIntegerValues(integerValues, outputFile);
          outputFile.write((ByteData)this.cells[i]);
          if (this.cells[i].getType() == CellType.STRING_FORMULA) {
            StringRecord sr = new StringRecord(this.cells[i].getContents());
            outputFile.write((ByteData)sr);
          } 
        } 
      } else {
        writeIntegerValues(integerValues, outputFile);
      } 
    } 
    writeIntegerValues(integerValues, outputFile);
  }
  
  private void writeIntegerValues(ArrayList integerValues, File outputFile) throws IOException {
    if (integerValues.size() == 0)
      return; 
    if (integerValues.size() >= 3) {
      MulRKRecord mulrk = new MulRKRecord(integerValues);
      outputFile.write((ByteData)mulrk);
    } else {
      Iterator i = integerValues.iterator();
      while (i.hasNext())
        outputFile.write((ByteData)i.next()); 
    } 
    integerValues.clear();
  }
  
  public byte[] getData() {
    byte[] data = new byte[16];
    IntegerHelper.getTwoBytes(this.rowNumber, data, 0);
    IntegerHelper.getTwoBytes(this.numColumns, data, 4);
    IntegerHelper.getTwoBytes(this.rowHeight, data, 6);
    int options = 256;
    if (this.collapsed)
      options |= 0x20; 
    if (!this.matchesDefFontHeight)
      options |= 0x40; 
    if (this.defaultFormat) {
      options |= 0x80;
      options |= this.xfIndex << 16;
    } 
    IntegerHelper.getFourBytes(options, data, 12);
    return data;
  }
  
  public int getMaxColumn() {
    return this.numColumns;
  }
  
  public CellValue getCell(int col) {
    return (col >= 0 && col < this.numColumns) ? this.cells[col] : null;
  }
  
  void incrementRow() {
    this.rowNumber++;
    for (int i = 0; i < this.cells.length; i++) {
      if (this.cells[i] != null)
        this.cells[i].incrementRow(); 
    } 
  }
  
  void decrementRow() {
    this.rowNumber--;
    for (int i = 0; i < this.cells.length; i++) {
      if (this.cells[i] != null)
        this.cells[i].decrementRow(); 
    } 
  }
  
  void insertColumn(int col) {
    if (col >= this.numColumns)
      return; 
    if (this.numColumns >= maxColumns) {
      logger.warn("Could not insert column because maximum column limit has been reached");
      return;
    } 
    CellValue[] oldCells = this.cells;
    if (this.numColumns >= this.cells.length - 1) {
      this.cells = new CellValue[oldCells.length + 10];
    } else {
      this.cells = new CellValue[oldCells.length];
    } 
    System.arraycopy(oldCells, 0, this.cells, 0, col);
    System.arraycopy(oldCells, col, this.cells, col + 1, this.numColumns - col);
    for (int i = col + 1; i <= this.numColumns; i++) {
      if (this.cells[i] != null)
        this.cells[i].incrementColumn(); 
    } 
    this.numColumns++;
  }
  
  void removeColumn(int col) {
    if (col >= this.numColumns)
      return; 
    CellValue[] oldCells = this.cells;
    this.cells = new CellValue[oldCells.length];
    System.arraycopy(oldCells, 0, this.cells, 0, col);
    System.arraycopy(oldCells, col + 1, this.cells, col, this.numColumns - col + 1);
    for (int i = col; i < this.numColumns; i++) {
      if (this.cells[i] != null)
        this.cells[i].decrementColumn(); 
    } 
    this.numColumns--;
  }
  
  public boolean isDefaultHeight() {
    return (this.rowHeight == defaultHeightIndicator);
  }
  
  public int getRowHeight() {
    return this.rowHeight;
  }
  
  public boolean isCollapsed() {
    return this.collapsed;
  }
  
  void rationalize(IndexMapping xfmapping) {
    if (this.defaultFormat)
      this.xfIndex = xfmapping.getNewIndex(this.xfIndex); 
  }
  
  XFRecord getStyle() {
    return this.style;
  }
  
  boolean hasDefaultFormat() {
    return this.defaultFormat;
  }
  
  boolean matchesDefaultFontHeight() {
    return this.matchesDefFontHeight;
  }
}
