package jxl.write.biff;

import common.Assert;
import common.Logger;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import jxl.Range;
import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.biff.ByteData;
import jxl.biff.CellReferenceHelper;
import jxl.biff.CountryCode;
import jxl.biff.Fonts;
import jxl.biff.FormattingRecords;
import jxl.biff.IndexMapping;
import jxl.biff.IntegerHelper;
import jxl.biff.RangeImpl;
import jxl.biff.WorkbookMethods;
import jxl.biff.drawing.Drawing;
import jxl.biff.drawing.DrawingGroup;
import jxl.biff.drawing.DrawingGroupObject;
import jxl.biff.drawing.Origin;
import jxl.biff.formula.ExternalSheet;
import jxl.format.Colour;
import jxl.format.RGB;
import jxl.read.biff.BOFRecord;
import jxl.read.biff.NameRecord;
import jxl.read.biff.SupbookRecord;
import jxl.read.biff.WorkbookParser;
import jxl.write.WritableCell;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

public class WritableWorkbookImpl extends WritableWorkbook implements ExternalSheet, WorkbookMethods {
  private static Logger logger = Logger.getLogger(WritableWorkbookImpl.class);
  
  private FormattingRecords formatRecords;
  
  private File outputFile;
  
  private ArrayList sheets;
  
  private Fonts fonts;
  
  private ExternalSheetRecord externSheet;
  
  private ArrayList supbooks;
  
  private ArrayList names;
  
  private HashMap nameRecords;
  
  private SharedStrings sharedStrings;
  
  private boolean closeStream;
  
  private boolean wbProtected;
  
  private WorkbookSettings settings;
  
  private ArrayList rcirCells;
  
  private DrawingGroup drawingGroup;
  
  private Styles styles;
  
  private boolean containsMacros;
  
  private ButtonPropertySetRecord buttonPropertySet;
  
  private CountryRecord countryRecord;
  
  public WritableWorkbookImpl(OutputStream os, boolean cs, WorkbookSettings ws) throws IOException {
    this.outputFile = new File(os, ws, null);
    this.sheets = new ArrayList();
    this.sharedStrings = new SharedStrings();
    this.nameRecords = new HashMap();
    this.closeStream = cs;
    this.wbProtected = false;
    this.containsMacros = false;
    this.settings = ws;
    this.rcirCells = new ArrayList();
    this.styles = new Styles();
    WritableWorkbook.ARIAL_10_PT.uninitialize();
    WritableWorkbook.HYPERLINK_FONT.uninitialize();
    WritableWorkbook.NORMAL_STYLE.uninitialize();
    WritableWorkbook.HYPERLINK_STYLE.uninitialize();
    WritableWorkbook.HIDDEN_STYLE.uninitialize();
    DateRecord.defaultDateFormat.uninitialize();
    WritableFonts wf = new WritableFonts(this);
    this.fonts = wf;
    WritableFormattingRecords wfr = new WritableFormattingRecords(this.fonts, this.styles);
    this.formatRecords = wfr;
  }
  
  public WritableWorkbookImpl(OutputStream os, Workbook w, boolean cs, WorkbookSettings ws) throws IOException {
    WorkbookParser wp = (WorkbookParser)w;
    WritableWorkbook.ARIAL_10_PT.uninitialize();
    WritableWorkbook.HYPERLINK_FONT.uninitialize();
    WritableWorkbook.NORMAL_STYLE.uninitialize();
    WritableWorkbook.HYPERLINK_STYLE.uninitialize();
    WritableWorkbook.HIDDEN_STYLE.uninitialize();
    DateRecord.defaultDateFormat.uninitialize();
    this.closeStream = cs;
    this.sheets = new ArrayList();
    this.sharedStrings = new SharedStrings();
    this.nameRecords = new HashMap();
    this.fonts = wp.getFonts();
    this.formatRecords = wp.getFormattingRecords();
    this.wbProtected = false;
    this.settings = ws;
    this.rcirCells = new ArrayList();
    this.styles = new Styles();
    this.outputFile = new File(os, ws, wp.getCompoundFile());
    this.containsMacros = false;
    if (!ws.getPropertySetsDisabled())
      this.containsMacros = wp.containsMacros(); 
    if (wp.getCountryRecord() != null)
      this.countryRecord = new CountryRecord(wp.getCountryRecord()); 
    if (wp.getExternalSheetRecord() != null) {
      this.externSheet = new ExternalSheetRecord(wp.getExternalSheetRecord());
      SupbookRecord[] readsr = wp.getSupbookRecords();
      this.supbooks = new ArrayList(readsr.length);
      for (int i = 0; i < readsr.length; i++)
        this.supbooks.add(new SupbookRecord(readsr[i], this.settings)); 
    } 
    if (wp.getDrawingGroup() != null)
      this.drawingGroup = new DrawingGroup(wp.getDrawingGroup()); 
    if (this.containsMacros && wp.getButtonPropertySet() != null)
      this.buttonPropertySet = new ButtonPropertySetRecord(wp.getButtonPropertySet()); 
    if (!this.settings.getNamesDisabled()) {
      NameRecord[] na = wp.getNameRecords();
      this.names = new ArrayList(na.length);
      for (int i = 0; i < na.length; i++) {
        if (na[i].isBiff8()) {
          NameRecord n = new NameRecord(na[i], i);
          this.names.add(n);
          String name = n.getName();
          this.nameRecords.put(name, n);
        } else {
          logger.warn("Cannot copy Biff7 name records - ignoring");
        } 
      } 
    } 
    copyWorkbook(w);
    if (this.drawingGroup != null)
      this.drawingGroup.updateData(wp.getDrawingGroup()); 
  }
  
  public WritableSheet[] getSheets() {
    WritableSheet[] sheetArray = new WritableSheet[getNumberOfSheets()];
    for (int i = 0; i < getNumberOfSheets(); i++)
      sheetArray[i] = getSheet(i); 
    return sheetArray;
  }
  
  public String[] getSheetNames() {
    String[] sheetNames = new String[getNumberOfSheets()];
    for (int i = 0; i < sheetNames.length; i++)
      sheetNames[i] = getSheet(i).getName(); 
    return sheetNames;
  }
  
  public Sheet getReadSheet(int index) {
    return (Sheet)getSheet(index);
  }
  
  public WritableSheet getSheet(int index) {
    return this.sheets.get(index);
  }
  
  public WritableSheet getSheet(String name) {
    boolean found = false;
    Iterator i = this.sheets.iterator();
    WritableSheet s = null;
    while (i.hasNext() && !found) {
      s = i.next();
      if (s.getName().equals(name))
        found = true; 
    } 
    return found ? s : null;
  }
  
  public int getNumberOfSheets() {
    return this.sheets.size();
  }
  
  public void close() throws IOException, JxlWriteException {
    this.outputFile.close(this.closeStream);
  }
  
  public void setOutputFile(File fileName) throws IOException {
    FileOutputStream fos = new FileOutputStream(fileName);
    this.outputFile.setOutputFile(fos);
  }
  
  private WritableSheet createSheet(String name, int index, boolean handleRefs) {
    WritableSheet w = new WritableSheetImpl(name, this.outputFile, this.formatRecords, this.sharedStrings, this.settings, this);
    int pos = index;
    if (index <= 0) {
      pos = 0;
      this.sheets.add(0, w);
    } else if (index > this.sheets.size()) {
      pos = this.sheets.size();
      this.sheets.add(w);
    } else {
      this.sheets.add(index, w);
    } 
    if (handleRefs && this.externSheet != null)
      this.externSheet.sheetInserted(pos); 
    if (this.supbooks != null && this.supbooks.size() > 0) {
      SupbookRecord supbook = this.supbooks.get(0);
      if (supbook.getType() == SupbookRecord.INTERNAL)
        supbook.adjustInternal(this.sheets.size()); 
    } 
    return w;
  }
  
  public WritableSheet createSheet(String name, int index) {
    return createSheet(name, index, true);
  }
  
  public void removeSheet(int index) {
    int pos = index;
    if (index <= 0) {
      pos = 0;
      this.sheets.remove(0);
    } else if (index >= this.sheets.size()) {
      pos = this.sheets.size() - 1;
      this.sheets.remove(this.sheets.size() - 1);
    } else {
      this.sheets.remove(index);
    } 
    if (this.externSheet != null)
      this.externSheet.sheetRemoved(pos); 
    if (this.supbooks != null && this.supbooks.size() > 0) {
      SupbookRecord supbook = this.supbooks.get(0);
      if (supbook.getType() == SupbookRecord.INTERNAL)
        supbook.adjustInternal(this.sheets.size()); 
    } 
    if (this.names != null && this.names.size() > 0)
      for (int i = 0; i < this.names.size(); i++) {
        NameRecord n = this.names.get(i);
        int oldRef = n.getSheetRef();
        if (oldRef == pos + 1) {
          n.setSheetRef(0);
        } else if (oldRef > pos + 1) {
          if (oldRef < 1)
            oldRef = 1; 
          n.setSheetRef(oldRef - 1);
        } 
      }  
  }
  
  public WritableSheet moveSheet(int fromIndex, int toIndex) {
    fromIndex = Math.max(fromIndex, 0);
    fromIndex = Math.min(fromIndex, this.sheets.size() - 1);
    toIndex = Math.max(toIndex, 0);
    toIndex = Math.min(toIndex, this.sheets.size() - 1);
    WritableSheet sheet = this.sheets.remove(fromIndex);
    this.sheets.add(toIndex, sheet);
    return sheet;
  }
  
  public void write() throws IOException {
    WritableSheetImpl wsi = null;
    for (int i = 0; i < getNumberOfSheets(); i++) {
      wsi = (WritableSheetImpl)getSheet(i);
      wsi.checkMergedBorders();
    } 
    if (!this.settings.getRationalizationDisabled())
      rationalize(); 
    BOFRecord bof = new BOFRecord(BOFRecord.workbookGlobals);
    this.outputFile.write((ByteData)bof);
    InterfaceHeaderRecord ihr = new InterfaceHeaderRecord();
    this.outputFile.write((ByteData)ihr);
    MMSRecord mms = new MMSRecord(0, 0);
    this.outputFile.write((ByteData)mms);
    InterfaceEndRecord ier = new InterfaceEndRecord();
    this.outputFile.write((ByteData)ier);
    WriteAccessRecord wr = new WriteAccessRecord();
    this.outputFile.write((ByteData)wr);
    CodepageRecord cp = new CodepageRecord();
    this.outputFile.write((ByteData)cp);
    DSFRecord dsf = new DSFRecord();
    this.outputFile.write((ByteData)dsf);
    TabIdRecord tabid = new TabIdRecord(getNumberOfSheets());
    this.outputFile.write((ByteData)tabid);
    if (this.containsMacros) {
      ObjProjRecord objproj = new ObjProjRecord();
      this.outputFile.write((ByteData)objproj);
    } 
    if (this.buttonPropertySet != null)
      this.outputFile.write((ByteData)this.buttonPropertySet); 
    FunctionGroupCountRecord fgcr = new FunctionGroupCountRecord();
    this.outputFile.write((ByteData)fgcr);
    WindowProtectRecord wpr = new WindowProtectRecord(false);
    this.outputFile.write((ByteData)wpr);
    ProtectRecord pr = new ProtectRecord(this.wbProtected);
    this.outputFile.write((ByteData)pr);
    PasswordRecord pw = new PasswordRecord(null);
    this.outputFile.write((ByteData)pw);
    Prot4RevRecord p4r = new Prot4RevRecord(false);
    this.outputFile.write((ByteData)p4r);
    Prot4RevPassRecord p4rp = new Prot4RevPassRecord();
    this.outputFile.write((ByteData)p4rp);
    Window1Record w1r = new Window1Record();
    this.outputFile.write((ByteData)w1r);
    BackupRecord bkr = new BackupRecord(false);
    this.outputFile.write((ByteData)bkr);
    HideobjRecord ho = new HideobjRecord(false);
    this.outputFile.write((ByteData)ho);
    NineteenFourRecord nf = new NineteenFourRecord(false);
    this.outputFile.write((ByteData)nf);
    PrecisionRecord pc = new PrecisionRecord(false);
    this.outputFile.write((ByteData)pc);
    RefreshAllRecord rar = new RefreshAllRecord(false);
    this.outputFile.write((ByteData)rar);
    BookboolRecord bb = new BookboolRecord(true);
    this.outputFile.write((ByteData)bb);
    this.fonts.write(this.outputFile);
    this.formatRecords.write(this.outputFile);
    if (this.formatRecords.getPalette() != null)
      this.outputFile.write((ByteData)this.formatRecords.getPalette()); 
    UsesElfsRecord uer = new UsesElfsRecord();
    this.outputFile.write((ByteData)uer);
    int[] boundsheetPos = new int[getNumberOfSheets()];
    Sheet sheet = null;
    int j;
    for (j = 0; j < getNumberOfSheets(); j++) {
      boundsheetPos[j] = this.outputFile.getPos();
      WritableSheet writableSheet = getSheet(j);
      BoundsheetRecord br = new BoundsheetRecord(writableSheet.getName());
      if (writableSheet.getSettings().isHidden())
        br.setHidden(); 
      if (((WritableSheetImpl)this.sheets.get(j)).isChartOnly())
        br.setChartOnly(); 
      this.outputFile.write((ByteData)br);
    } 
    if (this.countryRecord == null) {
      CountryCode lang = CountryCode.getCountryCode(this.settings.getExcelDisplayLanguage());
      if (lang == CountryCode.UNKNOWN) {
        logger.warn("Unknown country code " + this.settings.getExcelDisplayLanguage() + " using " + CountryCode.USA.getCode());
        lang = CountryCode.USA;
      } 
      CountryCode region = CountryCode.getCountryCode(this.settings.getExcelRegionalSettings());
      this.countryRecord = new CountryRecord(lang, region);
      if (region == CountryCode.UNKNOWN) {
        logger.warn("Unknown country code " + this.settings.getExcelDisplayLanguage() + " using " + CountryCode.UK.getCode());
        region = CountryCode.UK;
      } 
    } 
    this.outputFile.write((ByteData)this.countryRecord);
    if (this.externSheet != null) {
      for (j = 0; j < this.supbooks.size(); j++) {
        SupbookRecord supbook = this.supbooks.get(j);
        this.outputFile.write((ByteData)supbook);
      } 
      this.outputFile.write((ByteData)this.externSheet);
    } 
    if (this.names != null)
      for (j = 0; j < this.names.size(); j++) {
        NameRecord n = this.names.get(j);
        this.outputFile.write((ByteData)n);
      }  
    if (this.drawingGroup != null)
      this.drawingGroup.write(this.outputFile); 
    this.sharedStrings.write(this.outputFile);
    EOFRecord eof = new EOFRecord();
    this.outputFile.write((ByteData)eof);
    boolean sheetSelected = false;
    WritableSheetImpl wsheet = null;
    int k;
    for (k = 0; k < getNumberOfSheets() && !sheetSelected; k++) {
      wsheet = (WritableSheetImpl)getSheet(k);
      if (wsheet.getSettings().isSelected())
        sheetSelected = true; 
    } 
    if (!sheetSelected) {
      wsheet = (WritableSheetImpl)getSheet(0);
      wsheet.getSettings().setSelected(true);
    } 
    for (k = 0; k < getNumberOfSheets(); k++) {
      this.outputFile.setData(IntegerHelper.getFourBytes(this.outputFile.getPos()), boundsheetPos[k] + 4);
      wsheet = (WritableSheetImpl)getSheet(k);
      wsheet.write();
    } 
  }
  
  private void copyWorkbook(Workbook w) {
    int numSheets = w.getNumberOfSheets();
    this.wbProtected = w.isProtected();
    Sheet s = null;
    WritableSheetImpl ws = null;
    for (int i = 0; i < numSheets; i++) {
      s = w.getSheet(i);
      ws = (WritableSheetImpl)createSheet(s.getName(), i, false);
      ws.copy(s);
    } 
  }
  
  public void copySheet(int s, String name, int index) {
    WritableSheet sheet = getSheet(s);
    WritableSheetImpl ws = (WritableSheetImpl)createSheet(name, index);
    ws.copy(sheet);
  }
  
  public void copySheet(String s, String name, int index) {
    WritableSheet sheet = getSheet(s);
    WritableSheetImpl ws = (WritableSheetImpl)createSheet(name, index);
    ws.copy(sheet);
  }
  
  public void setProtected(boolean prot) {
    this.wbProtected = prot;
  }
  
  private void rationalize() {
    IndexMapping fontMapping = this.formatRecords.rationalizeFonts();
    IndexMapping formatMapping = this.formatRecords.rationalizeDisplayFormats();
    IndexMapping xfMapping = this.formatRecords.rationalize(fontMapping, formatMapping);
    WritableSheetImpl wsi = null;
    for (int i = 0; i < this.sheets.size(); i++) {
      wsi = this.sheets.get(i);
      wsi.rationalize(xfMapping, fontMapping, formatMapping);
    } 
  }
  
  public String getExternalSheetName(int index) {
    int supbookIndex = this.externSheet.getSupbookIndex(index);
    SupbookRecord sr = this.supbooks.get(supbookIndex);
    int firstTab = this.externSheet.getFirstTabIndex(index);
    if (sr.getType() == SupbookRecord.INTERNAL) {
      WritableSheet ws = getSheet(firstTab);
      return ws.getName();
    } 
    if (sr.getType() == SupbookRecord.EXTERNAL) {
      String name = sr.getFileName() + sr.getSheetName(firstTab);
      return name;
    } 
    return "[UNKNOWN]";
  }
  
  public String getLastExternalSheetName(int index) {
    int supbookIndex = this.externSheet.getSupbookIndex(index);
    SupbookRecord sr = this.supbooks.get(supbookIndex);
    int lastTab = this.externSheet.getLastTabIndex(index);
    if (sr.getType() == SupbookRecord.INTERNAL) {
      WritableSheet ws = getSheet(lastTab);
      return ws.getName();
    } 
    if (sr.getType() == SupbookRecord.EXTERNAL)
      Assert.verify(false); 
    return "[UNKNOWN]";
  }
  
  public BOFRecord getWorkbookBof() {
    return null;
  }
  
  public int getExternalSheetIndex(int index) {
    if (this.externSheet == null)
      return index; 
    Assert.verify((this.externSheet != null));
    int firstTab = this.externSheet.getFirstTabIndex(index);
    return firstTab;
  }
  
  public int getLastExternalSheetIndex(int index) {
    if (this.externSheet == null)
      return index; 
    Assert.verify((this.externSheet != null));
    int lastTab = this.externSheet.getLastTabIndex(index);
    return lastTab;
  }
  
  public int getExternalSheetIndex(String sheetName) {
    if (this.externSheet == null) {
      this.externSheet = new ExternalSheetRecord();
      this.supbooks = new ArrayList();
      this.supbooks.add(new SupbookRecord(getNumberOfSheets(), this.settings));
    } 
    boolean found = false;
    Iterator i = this.sheets.iterator();
    int sheetpos = 0;
    WritableSheetImpl s = null;
    while (i.hasNext() && !found) {
      s = i.next();
      if (s.getName().equals(sheetName)) {
        found = true;
        continue;
      } 
      sheetpos++;
    } 
    if (found) {
      SupbookRecord supbook = this.supbooks.get(0);
      Assert.verify((supbook.getType() == SupbookRecord.INTERNAL && supbook.getNumberOfSheets() == getNumberOfSheets()));
      return this.externSheet.getIndex(0, sheetpos);
    } 
    int closeSquareBracketsIndex = sheetName.lastIndexOf(']');
    int openSquareBracketsIndex = sheetName.lastIndexOf('[');
    if (closeSquareBracketsIndex == -1 || openSquareBracketsIndex == -1)
      return -1; 
    String worksheetName = sheetName.substring(closeSquareBracketsIndex + 1);
    String workbookName = sheetName.substring(openSquareBracketsIndex + 1, closeSquareBracketsIndex);
    String path = sheetName.substring(0, openSquareBracketsIndex);
    String fileName = path + workbookName;
    boolean supbookFound = false;
    SupbookRecord externalSupbook = null;
    int supbookIndex = -1;
    for (int ind = 0; ind < this.supbooks.size() && !supbookFound; ind++) {
      externalSupbook = this.supbooks.get(ind);
      if (externalSupbook.getType() == SupbookRecord.EXTERNAL && externalSupbook.getFileName().equals(fileName)) {
        supbookFound = true;
        supbookIndex = ind;
      } 
    } 
    if (!supbookFound) {
      externalSupbook = new SupbookRecord(fileName, this.settings);
      supbookIndex = this.supbooks.size();
      this.supbooks.add(externalSupbook);
    } 
    int sheetIndex = externalSupbook.getSheetIndex(worksheetName);
    return this.externSheet.getIndex(supbookIndex, sheetIndex);
  }
  
  public int getLastExternalSheetIndex(String sheetName) {
    if (this.externSheet == null) {
      this.externSheet = new ExternalSheetRecord();
      this.supbooks = new ArrayList();
      this.supbooks.add(new SupbookRecord(getNumberOfSheets(), this.settings));
    } 
    boolean found = false;
    Iterator i = this.sheets.iterator();
    int sheetpos = 0;
    WritableSheetImpl s = null;
    while (i.hasNext() && !found) {
      s = i.next();
      if (s.getName().equals(sheetName)) {
        found = true;
        continue;
      } 
      sheetpos++;
    } 
    if (!found)
      return -1; 
    SupbookRecord supbook = this.supbooks.get(0);
    Assert.verify((supbook.getType() == SupbookRecord.INTERNAL && supbook.getNumberOfSheets() == getNumberOfSheets()));
    return this.externSheet.getIndex(0, sheetpos);
  }
  
  public void setColourRGB(Colour c, int r, int g, int b) {
    this.formatRecords.setColourRGB(c, r, g, b);
  }
  
  public RGB getColourRGB(Colour c) {
    return this.formatRecords.getColourRGB(c);
  }
  
  public String getName(int index) {
    Assert.verify((index >= 0 && index < this.names.size()));
    NameRecord n = this.names.get(index);
    return n.getName();
  }
  
  public int getNameIndex(String name) {
    NameRecord nr = (NameRecord)this.nameRecords.get(name);
    return (nr != null) ? nr.getIndex() : -1;
  }
  
  void addRCIRCell(CellValue cv) {
    this.rcirCells.add(cv);
  }
  
  void columnInserted(WritableSheetImpl s, int col) {
    int externalSheetIndex = getExternalSheetIndex(s.getName());
    for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) {
      CellValue cv = i.next();
      cv.columnInserted((Sheet)s, externalSheetIndex, col);
    } 
  }
  
  void columnRemoved(WritableSheetImpl s, int col) {
    int externalSheetIndex = getExternalSheetIndex(s.getName());
    for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) {
      CellValue cv = i.next();
      cv.columnRemoved((Sheet)s, externalSheetIndex, col);
    } 
  }
  
  void rowInserted(WritableSheetImpl s, int row) {
    int externalSheetIndex = getExternalSheetIndex(s.getName());
    for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) {
      CellValue cv = i.next();
      cv.rowInserted((Sheet)s, externalSheetIndex, row);
    } 
  }
  
  void rowRemoved(WritableSheetImpl s, int row) {
    int externalSheetIndex = getExternalSheetIndex(s.getName());
    for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) {
      CellValue cv = i.next();
      cv.rowRemoved((Sheet)s, externalSheetIndex, row);
    } 
  }
  
  public WritableCell findCellByName(String name) {
    NameRecord nr = (NameRecord)this.nameRecords.get(name);
    if (nr == null)
      return null; 
    NameRecord.NameRange[] ranges = nr.getRanges();
    int sheetIndex = getExternalSheetIndex(ranges[0].getExternalSheet());
    WritableSheet s = getSheet(sheetIndex);
    WritableCell cell = s.getWritableCell(ranges[0].getFirstColumn(), ranges[0].getFirstRow());
    return cell;
  }
  
  public Range[] findByName(String name) {
    NameRecord nr = (NameRecord)this.nameRecords.get(name);
    if (nr == null)
      return null; 
    NameRecord.NameRange[] ranges = nr.getRanges();
    Range[] cellRanges = new Range[ranges.length];
    for (int i = 0; i < ranges.length; i++)
      cellRanges[i] = (Range)new RangeImpl(this, getExternalSheetIndex(ranges[i].getExternalSheet()), ranges[i].getFirstColumn(), ranges[i].getFirstRow(), getLastExternalSheetIndex(ranges[i].getExternalSheet()), ranges[i].getLastColumn(), ranges[i].getLastRow()); 
    return cellRanges;
  }
  
  void addDrawing(DrawingGroupObject d) {
    if (this.drawingGroup == null)
      this.drawingGroup = new DrawingGroup(Origin.WRITE); 
    this.drawingGroup.add(d);
  }
  
  void removeDrawing(Drawing d) {
    Assert.verify((this.drawingGroup != null));
    this.drawingGroup.remove((DrawingGroupObject)d);
  }
  
  DrawingGroup getDrawingGroup() {
    return this.drawingGroup;
  }
  
  public String[] getRangeNames() {
    String[] n = new String[this.names.size()];
    for (int i = 0; i < this.names.size(); i++) {
      NameRecord nr = this.names.get(i);
      n[i] = nr.getName();
    } 
    return n;
  }
  
  Styles getStyles() {
    return this.styles;
  }
  
  public void addNameArea(String name, WritableSheet sheet, int firstCol, int firstRow, int lastCol, int lastRow) {
    if (this.names == null)
      this.names = new ArrayList(); 
    int externalSheetIndex = getExternalSheetIndex(sheet.getName());
    NameRecord nr = new NameRecord(name, this.names.size(), externalSheetIndex, firstRow, lastRow, firstCol, lastCol);
    this.names.add(nr);
    this.nameRecords.put(name, nr);
  }
  
  WorkbookSettings getSettings() {
    return this.settings;
  }
  
  public WritableCell getWritableCell(String loc) {
    WritableSheet s = getSheet(CellReferenceHelper.getSheet(loc));
    return s.getWritableCell(loc);
  }
}
