/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



#ifndef _ZFORFIND_HXX
#define _ZFORFIND_HXX

#include <tools/string.hxx>

class Date;
class SvNumberformat;
class SvNumberFormatter;

#define SV_MAX_ANZ_INPUT_STRINGS  20    // max count of substrings in input scanner

class ImpSvNumberInputScan
{
public:
    ImpSvNumberInputScan( SvNumberFormatter* pFormatter );
    ~ImpSvNumberInputScan();

/*!*/   void ChangeIntl();                      // MUST be called if language changes

    /// set reference date for offset calculation
    void ChangeNullDate(
            const sal_uInt16 nDay,
            const sal_uInt16 nMonth,
            const sal_uInt16 nYear );

    /// convert input string to number
    sal_Bool IsNumberFormat(
            const String& rString,              /// input string
            short& F_Type,                      /// format type (in + out)
            double& fOutNumber,                 /// value determined (out)
            const SvNumberformat* pFormat = NULL    /// optional a number format to which compare against
            );

    /// after IsNumberFormat: get decimal position
    short   GetDecPos() const { return nDecPos; }
    /// after IsNumberFormat: get count of numeric substrings in input string
    sal_uInt16  GetAnzNums() const { return nAnzNums; }

    /// set threshold of two-digit year input
    void    SetYear2000( sal_uInt16 nVal ) { nYear2000 = nVal; }
    /// get threshold of two-digit year input
    sal_uInt16  GetYear2000() const { return nYear2000; }

private:
    SvNumberFormatter*  pFormatter;
    String* pUpperMonthText;                    // Array of month names, uppercase
    String* pUpperAbbrevMonthText;              // Array of month names, abbreviated, uppercase
    String* pUpperDayText;                      // Array of day of week names, uppercase
    String* pUpperAbbrevDayText;                // Array of day of week names, abbreviated, uppercase
    String  aUpperCurrSymbol;                   // Currency symbol, uppercase
    sal_Bool    bTextInitialized;                   // Whether days and months are initialized
    Date* pNullDate;                            // 30Dec1899
                                                // Variables for provisional results:
    String sStrArray[SV_MAX_ANZ_INPUT_STRINGS]; // Array of scanned substrings
    sal_Bool   IsNum[SV_MAX_ANZ_INPUT_STRINGS];     // Whether a substring is numeric
    sal_uInt16 nNums[SV_MAX_ANZ_INPUT_STRINGS];     // Sequence of offsets to numeric strings
    sal_uInt16 nAnzStrings;                         // Total count of scanned substrings
    sal_uInt16 nAnzNums;                            // Count of numeric substrings
    sal_Bool   bDecSepInDateSeps;                   // True <=> DecSep in {.,-,/,DateSep}
    sal_uInt8   nMatchedAllStrings;                  // Scan...String() matched all substrings,
                                                // bit mask of nMatched... constants

    static const sal_uInt8 nMatchedEndString;        // 0x01
    static const sal_uInt8 nMatchedMidString;        // 0x02
    static const sal_uInt8 nMatchedStartString;      // 0x04
    static const sal_uInt8 nMatchedVirgin;           // 0x08
    static const sal_uInt8 nMatchedUsedAsReturn;     // 0x10

    int    nSign;                               // Sign of number
    short  nMonth;                              // Month (1..x) if date
                                                // negative => short format
    short  nMonthPos;                           // 1 = front, 2 = middle
                                                // 3 = end
    sal_uInt16 nTimePos;                            // Index of first time separator (+1)
    short  nDecPos;                             // Index of substring containing "," (+1)
    short  nNegCheck;                           // '( )' for negative
    short  nESign;                              // Sign of exponent
    short  nAmPm;                               // +1 AM, -1 PM, 0 if none
    short  nLogical;                            // -1 => False, 1 => True
    sal_uInt16 nThousand;                           // Count of group (AKA thousand) separators
    sal_uInt16 nPosThousandString;                  // Position of concatenaded 000,000,000 string
    short  eScannedType;                        // Scanned type
    short  eSetType;                            // Preset Type

    sal_uInt16 nStringScanNumFor;                   // Fixed strings recognized in
                                                // pFormat->NumFor[nNumForStringScan]
    short  nStringScanSign;                     // Sign resulting of FixString
    sal_uInt16 nYear2000;                           // Two-digit threshold
                                                // Year as 20xx
                                                // default 18
                                                // number <= nYear2000 => 20xx
                                                // number >  nYear2000 => 19xx
    sal_uInt16  nTimezonePos;                       // Index of timezone separator (+1)
    sal_uInt8    nMayBeIso8601;                      // 0:=dontknowyet, 1:=yes, 2:=no

#ifdef _ZFORFIND_CXX        // methods private to implementation
    void Reset();                               // Reset all variables before start of analysis

    void InitText();                            // Init of months and days of week

    // Convert string to double.
    // Only simple unsigned floating point values without any error detection,
    // decimal separator has to be '.'
    // If bForceFraction==sal_True the string is taken to be the fractional part
    // of 0.1234 without the leading 0. (thus being just "1234").
    double StringToDouble(
            const String& rStr,
            sal_Bool bForceFraction = sal_False );

    sal_Bool NextNumberStringSymbol(                // Next number/string symbol
            const sal_Unicode*& pStr,
            String& rSymbol );

    sal_Bool SkipThousands(                         // Concatenate ,000,23 blocks
            const sal_Unicode*& pStr,           // in input to 000123
            String& rSymbol );

    void NumberStringDivision(                  // Divide numbers/strings into
            const String& rString );            // arrays and variables above.
                                                // Leading blanks and blanks
                                                // after numbers are thrown away


                                                // optimized substring versions

    static inline sal_Bool StringContains(          // Whether rString contains rWhat at nPos
            const String& rWhat,
            const String& rString,
            xub_StrLen nPos )
                {   // mostly used with one character
                    if ( rWhat.GetChar(0) != rString.GetChar(nPos) )
                        return sal_False;
                    return StringContainsImpl( rWhat, rString, nPos );
                }
    static inline sal_Bool StringPtrContains(       // Whether pString contains rWhat at nPos
            const String& rWhat,
            const sal_Unicode* pString,
            xub_StrLen nPos )                   // nPos MUST be a valid offset from pString
                {   // mostly used with one character
                    if ( rWhat.GetChar(0) != *(pString+nPos) )
                        return sal_False;
                    return StringPtrContainsImpl( rWhat, pString, nPos );
                }
    static sal_Bool StringContainsImpl(             //! DO NOT use directly
            const String& rWhat,
            const String& rString,
            xub_StrLen nPos );
    static sal_Bool StringPtrContainsImpl(          //! DO NOT use directly
            const String& rWhat,
            const sal_Unicode* pString,
            xub_StrLen nPos );


    static inline sal_Bool SkipChar(                // Skip a special character
            sal_Unicode c,
            const String& rString,
            xub_StrLen& nPos );
    static inline void SkipBlanks(              // Skip blank
            const String& rString,
            xub_StrLen& nPos );
    static inline sal_Bool SkipString(              // Jump over rWhat in rString at nPos
            const String& rWhat,
            const String& rString,
            xub_StrLen& nPos );

    inline sal_Bool GetThousandSep(                 // Recognizes exactly ,111 as group separator
            const String& rString,
            xub_StrLen& nPos,
            sal_uInt16 nStringPos );
    short GetLogical(                           // Get boolean value
            const String& rString );
    short GetMonth(                             // Get month and advance string position
            const String& rString,
            xub_StrLen& nPos );
    int GetDayOfWeek(                           // Get day of week and advance string position
            const String& rString,
            xub_StrLen& nPos );
    sal_Bool GetCurrency(                           // Get currency symbol and advance string position
            const String& rString,
            xub_StrLen& nPos,
            const SvNumberformat* pFormat = NULL ); // optional number format to match against
    sal_Bool GetTimeAmPm(                           // Get symbol AM or PM and advance string position
            const String& rString,
            xub_StrLen& nPos );
    inline sal_Bool GetDecSep(                      // Get decimal separator and advance string position
            const String& rString,
            xub_StrLen& nPos );
    inline sal_Bool GetTime100SecSep(               // Get hundredth seconds separator and advance string position
            const String& rString,
            xub_StrLen& nPos );
    int GetSign(                                // Get sign  and advance string position
            const String& rString,              // Including special case '('
            xub_StrLen& nPos );
    short GetESign(                             // Get sign of exponent and advance string position
            const String& rString,
            xub_StrLen& nPos );

    inline sal_Bool GetNextNumber(                  // Get next number as array offset
            sal_uInt16& i,
            sal_uInt16& j );

    void GetTimeRef(                            // Converts time -> double (only decimals)
            double& fOutNumber,                 // result as double
            sal_uInt16 nIndex,                      // Index of hour in input
            sal_uInt16 nAnz );                      // Count of time substrings in input
    sal_uInt16 ImplGetDay  ( sal_uInt16 nIndex );       // Day input, 0 if no match
    sal_uInt16 ImplGetMonth( sal_uInt16 nIndex );       // Month input, zero based return, NumberOfMonths if no match
    sal_uInt16 ImplGetYear ( sal_uInt16 nIndex );       // Year input, 0 if no match
    sal_Bool GetDateRef(                            // Conversion of date to number
            double& fDays,                      // OUT: days diff to null date
            sal_uInt16& nCounter,                   // Count of date substrings
            const SvNumberformat* pFormat = NULL ); // optional number format to match against

    sal_Bool ScanStartString(                       // Analyze start of string
            const String& rString,
            const SvNumberformat* pFormat = NULL );
    sal_Bool ScanMidString(                         // Analyze middle substring
            const String& rString,
            sal_uInt16 nStringPos,
            const SvNumberformat* pFormat = NULL );
    sal_Bool ScanEndString(                         // Analyze end of string
            const String& rString,
            const SvNumberformat* pFormat = NULL );

    // Whether input may be a ISO 8601 date format, yyyy-mm-dd...
    // checks if at least 3 numbers and first number>31
    bool MayBeIso8601();

    // Compare rString to substring of array indexed by nString
    // nString == 0xFFFF => last substring
    sal_Bool ScanStringNumFor(
            const String& rString,
            xub_StrLen nPos,
            const SvNumberformat* pFormat,
            sal_uInt16 nString,
            sal_Bool bDontDetectNegation = sal_False );

    // if nMatchedAllStrings set nMatchedUsedAsReturn and return sal_True,
    // else do nothing and return sal_False
    sal_Bool MatchedReturn();

    //! Be sure that the string to be analyzed is already converted to upper
    //! case and if it contained native humber digits that they are already
    //! converted to ASCII.
    sal_Bool IsNumberFormatMain(                    // Main anlyzing function
            const String& rString,
            double& fOutNumber,                 // return value if string is numeric
            const SvNumberformat* pFormat = NULL    // optional number format to match against
            );

    static inline sal_Bool MyIsdigit( sal_Unicode c );

    // native number transliteration if necessary
    void TransformInput( String& rString );

#endif  // _ZFORFIND_CXX
};



#endif  // _ZFORFIND_HXX
