/* ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
==================================================================== */

package org.apache.poi.hssf.record;

import org.apache.poi.hssf.record.common.UnicodeString;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.ss.formula.Formula;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.StringUtil;

/**
 * Title:        DATAVALIDATION Record (0x01BE)<p/>
 * Description:  This record stores data validation settings and a list of cell ranges
 *               which contain these settings. The data validation settings of a sheet
 *               are stored in a sequential list of DV records. This list is followed by
 *               DVAL record(s)
 * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
 * @author Josh Micich
 */
public final class DVRecord extends StandardRecord {
	public final static short sid = 0x01BE;

	/** Option flags */
	private int _option_flags;
	/** Title of the prompt box */
	private UnicodeString _promptTitle;
	/** Title of the error box */
	private UnicodeString _errorTitle;
	/** Text of the prompt box */
	private UnicodeString _promptText;
	/** Text of the error box */
	private UnicodeString _errorText;
	/** Not used - Excel seems to always write 0x3FE0 */
	private short _not_used_1 = 0x3FE0;
	/** Formula data for first condition (RPN token array without size field) */
	private Formula _formula1;
	/** Not used - Excel seems to always write 0x0000 */
	private short _not_used_2 = 0x0000;
	/** Formula data for second condition (RPN token array without size field) */
	private Formula _formula2;
	/** Cell range address list with all affected ranges */
	private CellRangeAddressList _regions;

	/**
	 * Option flags field
	 */

	public DVRecord(RecordInputStream in) { // NO_UCD

		_option_flags = in.readInt();

		_promptTitle = readUnicodeString(in);
		_errorTitle = readUnicodeString(in);
		_promptText = readUnicodeString(in);
		_errorText = readUnicodeString(in);

		int field_size_first_formula = in.readUShort();
		_not_used_1 = in.readShort();

		// "You may not use unions, intersections or array constants in Data Validation criteria"

		// read first formula data condition
		_formula1 = Formula.read(field_size_first_formula, in);

		int field_size_sec_formula = in.readUShort();
		_not_used_2 = in.readShort();

		// read sec formula data condition
		_formula2 = Formula.read(field_size_sec_formula, in);

		// read cell range address list with all affected ranges
		_regions = new CellRangeAddressList(in);
	}

	@Override
    public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("[DV]\n");
		sb.append(" options=").append(Integer.toHexString(_option_flags));
		sb.append(" title-prompt=").append(formatTextTitle(_promptTitle));
		sb.append(" title-error=").append(formatTextTitle(_errorTitle));
		sb.append(" text-prompt=").append(formatTextTitle(_promptText));
		sb.append(" text-error=").append(formatTextTitle(_errorText));
		sb.append("\n");
		appendFormula(sb, "Formula 1:",  _formula1);
		appendFormula(sb, "Formula 2:",  _formula2);
		sb.append("Regions: ");
		int nRegions = _regions.countRanges();
		for(int i=0; i<nRegions; i++) {
			if (i>0) {
				sb.append(", ");
			}
			CellRangeAddress addr = _regions.getCellRangeAddress(i);
			sb.append('(').append(addr.getFirstRow()).append(',').append(addr.getLastRow());
			sb.append(',').append(addr.getFirstColumn()).append(',').append(addr.getLastColumn()).append(')');
		}
		sb.append("\n");
		sb.append("[/DV]");

		return sb.toString();
	}

	private static String formatTextTitle(UnicodeString us) {
		String str = us.getString();
		if (str.length() == 1 && str.charAt(0) == '\0') {
			return "'\\0'";
		}
		return str;
	}

	private static void appendFormula(StringBuffer sb, String label, Formula f) {
		sb.append(label);

		if (f == null) {
			sb.append("<empty>\n");
			return;
		}
		Ptg[] ptgs = f.getTokens();
		sb.append('\n');
		for (int i = 0; i < ptgs.length; i++) {
			sb.append('\t').append(ptgs[i].toString()).append('\n');
		}
	}

	@Override
    public void serialize(LittleEndianOutput out) {

		out.writeInt(_option_flags);

		serializeUnicodeString(_promptTitle, out);
		serializeUnicodeString(_errorTitle, out);
		serializeUnicodeString(_promptText, out);
		serializeUnicodeString(_errorText, out);
		out.writeShort(_formula1.getEncodedTokenSize());
		out.writeShort(_not_used_1);
		_formula1.serializeTokens(out);

		out.writeShort(_formula2.getEncodedTokenSize());
		out.writeShort(_not_used_2);
		_formula2.serializeTokens(out);

		_regions.serialize(out);
	}

	private static UnicodeString readUnicodeString(RecordInputStream in) {
		return new UnicodeString(in);
	}

	private static void serializeUnicodeString(UnicodeString us, LittleEndianOutput out) {
		StringUtil.writeUnicodeString(out, us.getString());
	}
	private static int getUnicodeStringSize(UnicodeString us) {
		String str = us.getString();
		return 3 + str.length() * (StringUtil.hasMultibyte(str) ? 2 : 1);
	}

	@Override
    protected int getDataSize() {
		int size = 4+2+2+2+2;//options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
		size += getUnicodeStringSize(_promptTitle);
		size += getUnicodeStringSize(_errorTitle);
		size += getUnicodeStringSize(_promptText);
		size += getUnicodeStringSize(_errorText);
		size += _formula1.getEncodedTokenSize();
		size += _formula2.getEncodedTokenSize();
		size += _regions.getSize();
		return size;
	}

	@Override
    public short getSid() {
		return sid;
	}

	/**
	 * Clones the object. Uses serialisation, as the
	 *  contents are somewhat complex
	 */
	@Override
    public Object clone() {
		return cloneViaReserialise();
	}
}
