package jxl.biff;

import common.Logger;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import jxl.WorkbookSettings;
import jxl.format.Format;
import jxl.read.biff.Record;

public class FormatRecord extends WritableRecordData implements DisplayFormat, Format {
  public static Logger logger = Logger.getLogger(FormatRecord.class);
  
  private boolean initialized;
  
  private byte[] data;
  
  private int indexCode;
  
  private String formatString;
  
  private boolean date;
  
  private boolean number;
  
  private Format format;
  
  private WorkbookSettings settings;
  
  private static String[] dateStrings = new String[] { "dd", "mm", "yy", "hh", "ss", "m/", "/d" };
  
  private static class BiffType {
    private BiffType() {}
  }
  
  public static final BiffType biff8 = new BiffType();
  
  public static final BiffType biff7 = new BiffType();
  
  FormatRecord(String fmt, int refno) {
    super(Type.FORMAT);
    this.formatString = fmt;
    this.indexCode = refno;
    this.initialized = true;
  }
  
  protected FormatRecord() {
    super(Type.FORMAT);
    this.initialized = false;
  }
  
  protected FormatRecord(FormatRecord fr) {
    super(Type.FORMAT);
    this.initialized = false;
    this.formatString = fr.formatString;
    this.date = fr.date;
    this.number = fr.number;
  }
  
  public FormatRecord(Record t, WorkbookSettings ws, BiffType biffType) {
    super(t);
    byte[] data = getRecord().getData();
    this.indexCode = IntegerHelper.getInt(data[0], data[1]);
    this.initialized = true;
    if (biffType == biff8) {
      int numchars = IntegerHelper.getInt(data[2], data[3]);
      if (data[4] == 0) {
        this.formatString = StringHelper.getString(data, numchars, 5, ws);
      } else {
        this.formatString = StringHelper.getUnicodeString(data, numchars, 5);
      } 
    } else {
      int numchars = data[2];
      byte[] chars = new byte[numchars];
      System.arraycopy(data, 3, chars, 0, chars.length);
      this.formatString = new String(chars);
    } 
    this.date = false;
    this.number = false;
    for (int i = 0; i < dateStrings.length; i++) {
      String dateString = dateStrings[i];
      if (this.formatString.indexOf(dateString) != -1 || this.formatString.indexOf(dateString.toUpperCase()) != -1) {
        this.date = true;
        break;
      } 
    } 
    if (!this.date)
      if (this.formatString.indexOf('#') != -1 || this.formatString.indexOf('0') != -1)
        this.number = true;  
  }
  
  public byte[] getData() {
    this.data = new byte[this.formatString.length() * 2 + 3 + 2];
    IntegerHelper.getTwoBytes(this.indexCode, this.data, 0);
    IntegerHelper.getTwoBytes(this.formatString.length(), this.data, 2);
    this.data[4] = 1;
    StringHelper.getUnicodeBytes(this.formatString, this.data, 5);
    return this.data;
  }
  
  public int getFormatIndex() {
    return this.indexCode;
  }
  
  public boolean isInitialized() {
    return this.initialized;
  }
  
  public void initialize(int pos) {
    this.indexCode = pos;
    this.initialized = true;
  }
  
  protected final String replace(String input, String search, String replace) {
    String fmtstr = input;
    int pos = fmtstr.indexOf(search);
    while (pos != -1) {
      StringBuffer tmp = new StringBuffer(fmtstr.substring(0, pos));
      tmp.append(replace);
      tmp.append(fmtstr.substring(pos + search.length()));
      fmtstr = tmp.toString();
      pos = fmtstr.indexOf(search);
    } 
    return fmtstr;
  }
  
  protected final void setFormatString(String s) {
    this.formatString = s;
  }
  
  public final boolean isDate() {
    return this.date;
  }
  
  public final boolean isNumber() {
    return this.number;
  }
  
  public final NumberFormat getNumberFormat() {
    if (this.format != null && this.format instanceof NumberFormat)
      return (NumberFormat)this.format; 
    try {
      String fs = this.formatString;
      fs = replace(fs, "E+", "E");
      fs = replace(fs, "_)", "");
      fs = replace(fs, "_", "");
      fs = replace(fs, "[Red]", "");
      fs = replace(fs, "\\", "");
      this.format = new DecimalFormat(fs);
    } catch (IllegalArgumentException e) {
      this.format = new DecimalFormat("#.###");
    } 
    return (NumberFormat)this.format;
  }
  
  public final DateFormat getDateFormat() {
    if (this.format != null && this.format instanceof DateFormat)
      return (DateFormat)this.format; 
    String fmt = this.formatString;
    int pos = fmt.indexOf("AM/PM");
    while (pos != -1) {
      StringBuffer stringBuffer = new StringBuffer(fmt.substring(0, pos));
      stringBuffer.append('a');
      stringBuffer.append(fmt.substring(pos + 5));
      fmt = stringBuffer.toString();
      pos = fmt.indexOf("AM/PM");
    } 
    pos = fmt.indexOf("ss.0");
    while (pos != -1) {
      StringBuffer stringBuffer = new StringBuffer(fmt.substring(0, pos));
      stringBuffer.append("ss.SSS");
      pos += 4;
      while (pos < fmt.length() && fmt.charAt(pos) == '0')
        pos++; 
      stringBuffer.append(fmt.substring(pos));
      fmt = stringBuffer.toString();
      pos = fmt.indexOf("ss.0");
    } 
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < fmt.length(); i++) {
      if (fmt.charAt(i) != '\\')
        sb.append(fmt.charAt(i)); 
    } 
    fmt = sb.toString();
    if (fmt.charAt(0) == '[') {
      int end = fmt.indexOf(']');
      if (end != -1)
        fmt = fmt.substring(end + 1); 
    } 
    fmt = replace(fmt, ";@", "");
    char[] formatBytes = fmt.toCharArray();
    for (int j = 0; j < formatBytes.length; j++) {
      if (formatBytes[j] == 'm')
        if (j > 0 && (formatBytes[j - 1] == 'm' || formatBytes[j - 1] == 'M')) {
          formatBytes[j] = formatBytes[j - 1];
        } else {
          int minuteDist = Integer.MAX_VALUE;
          int k;
          for (k = j - 1; k > 0; k--) {
            if (formatBytes[k] == 'h') {
              minuteDist = j - k;
              break;
            } 
          } 
          for (k = j + 1; k < formatBytes.length; k++) {
            if (formatBytes[k] == 'h') {
              minuteDist = Math.min(minuteDist, k - j);
              break;
            } 
          } 
          for (k = j - 1; k > 0; k--) {
            if (formatBytes[k] == 'H') {
              minuteDist = j - k;
              break;
            } 
          } 
          for (k = j + 1; k < formatBytes.length; k++) {
            if (formatBytes[k] == 'H') {
              minuteDist = Math.min(minuteDist, k - j);
              break;
            } 
          } 
          for (k = j - 1; k > 0; k--) {
            if (formatBytes[k] == 's') {
              minuteDist = Math.min(minuteDist, j - k);
              break;
            } 
          } 
          for (k = j + 1; k < formatBytes.length; k++) {
            if (formatBytes[k] == 's') {
              minuteDist = Math.min(minuteDist, k - j);
              break;
            } 
          } 
          int monthDist = Integer.MAX_VALUE;
          int m;
          for (m = j - 1; m > 0; m--) {
            if (formatBytes[m] == 'd') {
              monthDist = j - m;
              break;
            } 
          } 
          for (m = j + 1; m < formatBytes.length; m++) {
            if (formatBytes[m] == 'd') {
              monthDist = Math.min(monthDist, m - j);
              break;
            } 
          } 
          for (m = j - 1; m > 0; m--) {
            if (formatBytes[m] == 'y') {
              monthDist = Math.min(monthDist, j - m);
              break;
            } 
          } 
          for (m = j + 1; m < formatBytes.length; m++) {
            if (formatBytes[m] == 'y') {
              monthDist = Math.min(monthDist, m - j);
              break;
            } 
          } 
          if (monthDist < minuteDist) {
            formatBytes[j] = Character.toUpperCase(formatBytes[j]);
          } else if (monthDist == minuteDist && monthDist != Integer.MAX_VALUE) {
            char ind = formatBytes[j - monthDist];
            if (ind == 'y' || ind == 'd')
              formatBytes[j] = Character.toUpperCase(formatBytes[j]); 
          } 
        }  
    } 
    try {
      this.format = new SimpleDateFormat(new String(formatBytes));
    } catch (IllegalArgumentException e) {
      this.format = new SimpleDateFormat("dd MM yyyy hh:mm:ss");
    } 
    return (DateFormat)this.format;
  }
  
  public int getIndexCode() {
    return this.indexCode;
  }
  
  public String getFormatString() {
    return this.formatString;
  }
  
  public boolean isBuiltIn() {
    return false;
  }
  
  public int hashCode() {
    return this.formatString.hashCode();
  }
  
  public boolean equals(Object o) {
    if (o == this)
      return true; 
    if (!(o instanceof FormatRecord))
      return false; 
    FormatRecord fr = (FormatRecord)o;
    if (this.initialized && fr.initialized) {
      if (this.date != fr.date || this.number != fr.number)
        return false; 
      return this.formatString.equals(fr.formatString);
    } 
    return this.formatString.equals(fr.formatString);
  }
}
