package jxl.biff;

import common.Assert;
import common.Logger;
import java.io.IOException;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import jxl.format.Colour;
import jxl.format.RGB;
import jxl.write.biff.File;

public class FormattingRecords {
  private static Logger logger = Logger.getLogger(FormattingRecords.class);
  
  private HashMap formats;
  
  private ArrayList formatsList;
  
  private ArrayList xfRecords;
  
  private int nextCustomIndexNumber;
  
  private Fonts fonts;
  
  private PaletteRecord palette;
  
  private static final int customFormatStartIndex = 164;
  
  private static final int maxFormatRecordsIndex = 441;
  
  private static final int minXFRecords = 21;
  
  public FormattingRecords(Fonts f) {
    this.xfRecords = new ArrayList(10);
    this.formats = new HashMap(10);
    this.formatsList = new ArrayList(10);
    this.fonts = f;
    this.nextCustomIndexNumber = 164;
  }
  
  public final void addStyle(XFRecord xf) throws NumFormatRecordsException {
    if (!xf.isInitialized()) {
      int pos = this.xfRecords.size();
      xf.initialize(pos, this, this.fonts);
      this.xfRecords.add(xf);
    } else if (xf.getXFIndex() >= this.xfRecords.size()) {
      this.xfRecords.add(xf);
    } 
  }
  
  public final void addFormat(DisplayFormat fr) throws NumFormatRecordsException {
    if (fr.isInitialized() && fr.getFormatIndex() >= 441) {
      logger.warn("Format index exceeds Excel maximum - assigning custom number");
      fr.initialize(this.nextCustomIndexNumber);
      this.nextCustomIndexNumber++;
    } 
    if (!fr.isInitialized()) {
      fr.initialize(this.nextCustomIndexNumber);
      this.nextCustomIndexNumber++;
    } 
    if (this.nextCustomIndexNumber > 441) {
      this.nextCustomIndexNumber = 441;
      throw new NumFormatRecordsException();
    } 
    if (fr.getFormatIndex() >= this.nextCustomIndexNumber)
      this.nextCustomIndexNumber = fr.getFormatIndex() + 1; 
    if (!fr.isBuiltIn()) {
      this.formatsList.add(fr);
      this.formats.put(new Integer(fr.getFormatIndex()), fr);
    } 
  }
  
  public final boolean isDate(int pos) {
    XFRecord xfr = this.xfRecords.get(pos);
    if (xfr.isDate())
      return true; 
    FormatRecord fr = (FormatRecord)this.formats.get(new Integer(xfr.getFormatRecord()));
    return (fr == null) ? false : fr.isDate();
  }
  
  public final DateFormat getDateFormat(int pos) {
    XFRecord xfr = this.xfRecords.get(pos);
    if (xfr.isDate())
      return xfr.getDateFormat(); 
    FormatRecord fr = (FormatRecord)this.formats.get(new Integer(xfr.getFormatRecord()));
    if (fr == null)
      return null; 
    return fr.isDate() ? fr.getDateFormat() : null;
  }
  
  public final NumberFormat getNumberFormat(int pos) {
    XFRecord xfr = this.xfRecords.get(pos);
    if (xfr.isNumber())
      return xfr.getNumberFormat(); 
    FormatRecord fr = (FormatRecord)this.formats.get(new Integer(xfr.getFormatRecord()));
    if (fr == null)
      return null; 
    return fr.isNumber() ? fr.getNumberFormat() : null;
  }
  
  FormatRecord getFormatRecord(int index) {
    return (FormatRecord)this.formats.get(new Integer(index));
  }
  
  public void write(File outputFile) throws IOException {
    Iterator i = this.formatsList.iterator();
    FormatRecord fr = null;
    while (i.hasNext()) {
      fr = i.next();
      outputFile.write(fr);
    } 
    i = this.xfRecords.iterator();
    XFRecord xfr = null;
    while (i.hasNext()) {
      xfr = (XFRecord)i.next();
      outputFile.write(xfr);
    } 
    BuiltInStyle style = new BuiltInStyle(16, 3);
    outputFile.write(style);
    style = new BuiltInStyle(17, 6);
    outputFile.write(style);
    style = new BuiltInStyle(18, 4);
    outputFile.write(style);
    style = new BuiltInStyle(19, 7);
    outputFile.write(style);
    style = new BuiltInStyle(0, 0);
    outputFile.write(style);
    style = new BuiltInStyle(20, 5);
    outputFile.write(style);
  }
  
  protected final Fonts getFonts() {
    return this.fonts;
  }
  
  public final XFRecord getXFRecord(int index) {
    return this.xfRecords.get(index);
  }
  
  protected final int getNumberOfFormatRecords() {
    return this.formatsList.size();
  }
  
  public IndexMapping rationalizeFonts() {
    return this.fonts.rationalize();
  }
  
  public IndexMapping rationalize(IndexMapping fontMapping, IndexMapping formatMapping) {
    XFRecord xfr = null;
    for (Iterator it = this.xfRecords.iterator(); it.hasNext(); ) {
      xfr = it.next();
      if (xfr.getFormatRecord() >= 164)
        xfr.setFormatIndex(formatMapping.getNewIndex(xfr.getFormatRecord())); 
      xfr.setFontIndex(fontMapping.getNewIndex(xfr.getFontIndex()));
    } 
    ArrayList newrecords = new ArrayList(21);
    IndexMapping mapping = new IndexMapping(this.xfRecords.size());
    int numremoved = 0;
    int numXFRecords = Math.min(21, this.xfRecords.size());
    int j;
    for (j = 0; j < numXFRecords; j++) {
      newrecords.add(this.xfRecords.get(j));
      mapping.setMapping(j, j);
    } 
    if (numXFRecords < 21) {
      logger.warn("There are less than the expected minimum number of XF records");
      return mapping;
    } 
    for (j = 21; j < this.xfRecords.size(); j++) {
      XFRecord xf = this.xfRecords.get(j);
      boolean duplicate = false;
      Iterator iterator = newrecords.iterator();
      while (iterator.hasNext() && !duplicate) {
        XFRecord xf2 = iterator.next();
        if (xf2.equals(xf)) {
          duplicate = true;
          mapping.setMapping(j, mapping.getNewIndex(xf2.getXFIndex()));
          numremoved++;
        } 
      } 
      if (!duplicate) {
        newrecords.add(xf);
        mapping.setMapping(j, j - numremoved);
      } 
    } 
    for (Iterator i = this.xfRecords.iterator(); i.hasNext(); ) {
      XFRecord xf = i.next();
      xf.rationalize(mapping);
    } 
    this.xfRecords = newrecords;
    return mapping;
  }
  
  public IndexMapping rationalizeDisplayFormats() {
    ArrayList newformats = new ArrayList();
    int numremoved = 0;
    IndexMapping mapping = new IndexMapping(this.nextCustomIndexNumber);
    Iterator i = this.formatsList.iterator();
    DisplayFormat df = null;
    DisplayFormat df2 = null;
    boolean duplicate = false;
    while (i.hasNext()) {
      df = i.next();
      Assert.verify(!df.isBuiltIn());
      Iterator i2 = newformats.iterator();
      duplicate = false;
      while (i2.hasNext() && !duplicate) {
        df2 = i2.next();
        if (df2.equals(df)) {
          duplicate = true;
          mapping.setMapping(df.getFormatIndex(), mapping.getNewIndex(df2.getFormatIndex()));
          numremoved++;
        } 
      } 
      if (!duplicate) {
        newformats.add(df);
        int indexnum = df.getFormatIndex() - numremoved;
        if (indexnum > 441) {
          logger.warn("Too many number formats - using default format.");
          indexnum = 0;
        } 
        mapping.setMapping(df.getFormatIndex(), df.getFormatIndex() - numremoved);
      } 
    } 
    this.formatsList = newformats;
    i = this.formatsList.iterator();
    while (i.hasNext()) {
      df = i.next();
      df.initialize(mapping.getNewIndex(df.getFormatIndex()));
    } 
    return mapping;
  }
  
  public PaletteRecord getPalette() {
    return this.palette;
  }
  
  public void setPalette(PaletteRecord pr) {
    this.palette = pr;
  }
  
  public void setColourRGB(Colour c, int r, int g, int b) {
    if (this.palette == null)
      this.palette = new PaletteRecord(); 
    this.palette.setColourRGB(c, r, g, b);
  }
  
  public RGB getColourRGB(Colour c) {
    if (this.palette == null)
      return c.getDefaultRGB(); 
    return this.palette.getColourRGB(c);
  }
}
