using ClosedXML.Excel.CalcEngine.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ClosedXML.Excel.CalcEngine.Functions
{
internal static class Lookup
{
public static void Register(CalcEngine ce)
{
//ce.RegisterFunction("ADDRESS", , Address); // Returns a reference as text to a single cell in a worksheet
//ce.RegisterFunction("AREAS", , Areas); // Returns the number of areas in a reference
//ce.RegisterFunction("CHOOSE", , Choose); // Chooses a value from a list of values
//ce.RegisterFunction("COLUMN", , Column); // Returns the column number of a reference
//ce.RegisterFunction("COLUMNS", , Columns); // Returns the number of columns in a reference
//ce.RegisterFunction("FORMULATEXT", , Formulatext); // Returns the formula at the given reference as text
//ce.RegisterFunction("GETPIVOTDATA", , Getpivotdata); // Returns data stored in a PivotTable report
ce.RegisterFunction("HLOOKUP", 3, 4, Hlookup); // Looks in the top row of an array and returns the value of the indicated cell
ce.RegisterFunction("HYPERLINK", 1, 2, Hyperlink); // Creates a shortcut or jump that opens a document stored on a network server, an intranet, or the Internet
//ce.RegisterFunction("INDEX", , Index); // Uses an index to choose a value from a reference or array
//ce.RegisterFunction("INDIRECT", , Indirect); // Returns a reference indicated by a text value
//ce.RegisterFunction("LOOKUP", , Lookup); // Looks up values in a vector or array
//ce.RegisterFunction("MATCH", , Match); // Looks up values in a reference or array
//ce.RegisterFunction("OFFSET", , Offset); // Returns a reference offset from a given reference
//ce.RegisterFunction("ROW", , Row); // Returns the row number of a reference
//ce.RegisterFunction("ROWS", , Rows); // Returns the number of rows in a reference
//ce.RegisterFunction("RTD", , Rtd); // Retrieves real-time data from a program that supports COM automation
//ce.RegisterFunction("TRANSPOSE", , Transpose); // Returns the transpose of an array
ce.RegisterFunction("VLOOKUP", 3, 4, Vlookup); // Looks in the first column of an array and moves across the row to return the value of a cell
}
private static object Hlookup(List<Expression> p)
{
var lookup_value = p[0];
var table_array = p[1] as XObjectExpression;
if (table_array == null)
throw new NoValueAvailableException("table_array has to be a range");
var range_reference = table_array.Value as CellRangeReference;
if (range_reference == null)
throw new NoValueAvailableException("table_array has to be a range");
var range = range_reference.Range;
var row_index_num = (int)(p[2]);
var range_lookup = p.Count < 4
|| p[3] is EmptyValueExpression
|| (bool)(p[3]);
if (row_index_num < 1)
throw new CellReferenceException("Row index has to be positive");
if (row_index_num > range.RowCount())
throw new CellReferenceException("Row index has to be positive");
IXLRangeColumn matching_column;
matching_column = range.FindColumn(c => !c.Cell(1).IsEmpty() && new Expression(c.Cell(1).Value).CompareTo(lookup_value) == 0);
if (range_lookup && matching_column == null)
{
var first_column = range.FirstColumn().ColumnNumber();
matching_column = range.FindColumn(c =>
{
var column_index_in_range = c.ColumnNumber() - first_column + 1;
if (column_index_in_range < range.ColumnsUsed().Count() && !c.Cell(1).IsEmpty() && new Expression(c.Cell(1).Value).CompareTo(lookup_value) <= 0 && !c.ColumnRight().Cell(1).IsEmpty() && new Expression(c.ColumnRight().Cell(1).Value).CompareTo(lookup_value) > 0)
return true;
else if (column_index_in_range == range.ColumnsUsed().Count() && !c.Cell(1).IsEmpty() && new Expression(c.Cell(1).Value).CompareTo(lookup_value) <= 0)
return true;
else
return false;
});
}
if (matching_column == null)
throw new NoValueAvailableException("No matches found.");
return matching_column
.Cell(row_index_num)
.Value;
}
private static object Hyperlink(List<Expression> p)
{
String address = p[0];
String toolTip = p.Count == 2 ? p[1] : String.Empty;
return new XLHyperlink(address, toolTip);
}
private static object Vlookup(List<Expression> p)
{
var lookup_value = p[0];
var table_array = p[1] as XObjectExpression;
if (table_array == null)
throw new NoValueAvailableException("table_array has to be a range");
var range_reference = table_array.Value as CellRangeReference;
if (range_reference == null)
throw new NoValueAvailableException("table_array has to be a range");
var range = range_reference.Range;
var col_index_num = (int)(p[2]);
var range_lookup = p.Count < 4
|| p[3] is EmptyValueExpression
|| (bool)(p[3]);
if (col_index_num < 1)
throw new CellReferenceException("Column index has to be positive");
if (col_index_num > range.ColumnCount())
throw new CellReferenceException("Colum index must be smaller or equal to the number of columns in the table array");
IXLRangeRow matching_row;
try
{
matching_row = range.FindRow(r => !r.Cell(1).IsEmpty() && new Expression(r.Cell(1).Value).CompareTo(lookup_value) == 0);
}
catch (Exception ex)
{
throw new NoValueAvailableException("No matches found", ex);
}
if (range_lookup && matching_row == null)
{
var first_row = range.FirstRow().RowNumber();
matching_row = range.FindRow(r =>
{
var row_index_in_range = r.RowNumber() - first_row + 1;
if (row_index_in_range < range.RowsUsed().Count() && !r.Cell(1).IsEmpty() && new Expression(r.Cell(1).Value).CompareTo(lookup_value) <= 0 && !r.RowBelow().Cell(1).IsEmpty() && new Expression(r.RowBelow().Cell(1).Value).CompareTo(lookup_value) > 0)
return true;
else if (row_index_in_range == range.RowsUsed().Count() && !r.Cell(1).IsEmpty() && new Expression(r.Cell(1).Value).CompareTo(lookup_value) <= 0)
return true;
else
return false;
});
}
if (matching_row == null)
throw new NoValueAvailableException("No matches found.");
return matching_row
.Cell(col_index_num)
.Value;
}
}
}