/***************************************************************************
 *
 * numeric.cpp
 *
 * $Id: numeric.cpp 448754 2006-09-22 00:42:16Z sebor $
 *
 ***************************************************************************
 *
 * 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.
 *
 * Copyright 2001-2006 Rogue Wave Software.
 * 
 **************************************************************************/

#include "def.h"          // for Def
#include "diagnostic.h"   // for issue_diag()
#include "path.h"         // for get_pathname()
#include "scanner.h"      // for scanner

#include <cassert>        // for assert()
#include <cstdlib>        // for atoi()
#include <fstream>        // for ofstream


static const char lc_name[] = "LC_NUMERIC";


void Def::process_numeric ()
{
    issue_diag (I_STAGE, false, 0, "processing %s section\n", lc_name);

    // nesting level
    int nesting_level = 0;

    num_def_found_ = true;

    while ((next = scanner_.next_token()).token != Scanner::tok_numeric) {
        
        switch (next.token) {

        case Scanner::tok_end:
            next = scanner_.next_token();
            if (next.token == Scanner::tok_numeric) {
                // end of numeric block
                if (nesting_level == 0) 
                    return;

                nesting_level--;
                scanner_.close ();
            } else
                issue_diag (E_SYNTAX, true, &next,
                            "wrong section name in END directive\n");

            break;

        case Scanner::tok_copy: {
            next = scanner_.next_token();
            if (next.token != Scanner::tok_string)
                issue_diag (E_SYNTAX, true, &next,
                            "expected string following \"copy\" directive\n");

            // bump up the nesting level
            nesting_level++;

            issue_diag (I_STAGE, false, 0, "processing copy directive\n");

            // open the file
            scanner_.open (get_pathname (strip_quotes (next.name), next.file));

            // get comment char and escape char; 
            // these informations are stored by the scanner
            while ((next = scanner_.next_token ()).token 
                   != Scanner::tok_numeric ){
                // the LC_IDENTIFICATION section may also have a 
                // LC_NUMERIC token that will mess up the parsing
                if (next.token == Scanner::tok_ident) {
                    while ((next = scanner_.next_token()).token
                           != Scanner::tok_end );
                    next = scanner_.next_token();
                }
            }

            break;
        }

        case Scanner::tok_decimal_point:
            next = scanner_.next_token();
            num_st_.decimal_point = convert_string (next.name);
            num_st_.wdecimal_point = convert_wstring (next);
            break;

        case Scanner::tok_thousands_sep:
            next = scanner_.next_token();
            num_st_.thousands_sep = convert_string (next.name);
            num_st_.wthousands_sep = convert_wstring (next);
            break;

        case Scanner::tok_grouping:
            // convert the grouping digits from ascii to integers before
            // adding them to the grouping string
            num_st_.grouping.clear();
            while ((next = scanner_.next_token()).token 
                   != Scanner::tok_nl)
                num_st_.grouping += std::atoi(next.name.c_str());
            break;

        case Scanner::tok_truename:
            next = scanner_.next_token();
            num_st_.truename = convert_string (next.name);
            num_st_.wtruename = convert_wstring (next);
            break;

        case Scanner::tok_falsename:
            next = scanner_.next_token();
            num_st_.falsename = convert_string (next.name);
            num_st_.wfalsename = convert_wstring (next);
            break;

        default:
            break;
        }
    }
}


void Def::write_numeric (std::string dir_name)
{
    assert (!dir_name.empty ());

    if (num_written_)
        return;

    if (!num_def_found_) {
        issue_diag (I_SKIP, false, 0,
                    "%s section not found, skipping\n", lc_name);
        return;
    }

    (dir_name += _RWSTD_PATH_SEP) += lc_name;

    issue_diag (I_OPENWR, false, 0, "writing %s\n", dir_name.c_str ());

    std::ofstream out (dir_name.c_str (), std::ios::binary);
    out.exceptions (std::ios::failbit | std::ios::badbit);

    // calculate the offsets of members of the num_punct struct

    // start with wide strings to simplify dealing with their alignment
    num_punct_out_.decimal_point_off [1] = 0;

    num_punct_out_.thousands_sep_off [1] = 
        num_punct_out_.decimal_point_off [1] 
        + (num_st_.wdecimal_point.size () + 1) * sizeof (wchar_t);

    num_punct_out_.decimal_point_off [0] = 
        num_punct_out_.thousands_sep_off [1]
        + (num_st_.wthousands_sep.size() + 1) * sizeof (wchar_t);

    num_punct_out_.thousands_sep_off [0] = 
        num_punct_out_.decimal_point_off [0] 
        + num_st_.decimal_point.size () + 1;

    num_punct_out_.grouping_off =
        num_punct_out_.thousands_sep_off [0]
        + num_st_.thousands_sep.size () + 1;

    num_punct_out_.punct_ext_off =
        num_punct_out_.grouping_off
        + num_st_.grouping.size () + 1;

    // compute the alignment requirement of any offset member
    const std::size_t align = sizeof num_punct_out_.punct_ext_off;

    // align the offset of the extension struct on the required boundary
    const std::size_t misalign = num_punct_out_.punct_ext_off % align;

    // compute the amount of padding between the two structs
    const std::size_t pad = misalign ? align - misalign : 0;

    num_punct_out_.punct_ext_off += pad;

    // calculate the offsets of members of the numeric extension struct

    num_out_.truename_off [1] = 0;

    num_out_.falsename_off [1] =
        num_out_.truename_off [1]
        + (num_st_.wtruename.size() + 1) * sizeof (wchar_t);

    num_out_.truename_off [0] =
        num_out_.falsename_off [1]
        + (num_st_.wfalsename.size() + 1) * sizeof (wchar_t);

    num_out_.falsename_off [0] =
        num_out_.truename_off [0]
        + num_st_.truename.size() + 1;

    num_out_.codeset_off =
        num_out_.falsename_off [0]
        + num_st_.falsename.size() + 1;

    num_out_.charmap_off =
        num_out_.codeset_off
        + charmap_.get_code_set_name().size() + 1;

    issue_diag (I_WRITE, false, 0,
                "%s layout:\n"
                "__rw_punct_t {\n"
                "    decimal_point_off[] = { %u, %u }\n"
                "    thousand_sep_off[] = { %u, %u }\n"
                "    grouping_off = %u\n"
                "    ext_off = %u\n"
                "}\n__rw_numpunct_t {\n"
                "    falsename_off[] = { %u, %u }\n"
                "    truename_off[] = { %u, %u }\n"
                "    codeset_off = %u\n"
                "    charmap_off = %u\n"
                "}\n",
                lc_name,
                num_punct_out_.decimal_point_off [0],
                num_punct_out_.decimal_point_off [1],
                num_punct_out_.thousands_sep_off [0],
                num_punct_out_.thousands_sep_off [1],
                num_punct_out_.grouping_off,
                num_punct_out_.punct_ext_off,
                num_out_.falsename_off [0],
                num_out_.falsename_off [1],
                num_out_.truename_off [0],
                num_out_.truename_off [1],
                num_out_.codeset_off,
                num_out_.charmap_off);
            
    // write the num_punct struct
    out.write ((char*)&num_punct_out_, sizeof (num_punct_out_));
        
    // write out the strings in the num_punct struct
    out.write ((const char*)num_st_.wdecimal_point.c_str (),
               (num_st_.wdecimal_point.size() + 1) * sizeof (wchar_t));
    out.write ((const char*)num_st_.wthousands_sep.c_str(),
               (num_st_.wthousands_sep.size() + 1) * sizeof(wchar_t));
        
    out << num_st_.decimal_point << std::ends
        << num_st_.thousands_sep << std::ends
        << num_st_.grouping << std::ends;

    // pad the structure up to the next alignment boundary
    out.write ("\0\0\0\0\0\0\0\0", pad);

    // write the numeric offset extension struct
    out.write ((char*)&num_out_, sizeof (num_out_));

    // write all the wide character strings
    out.write ((const char*)num_st_.wtruename.c_str(),
               (num_st_.wtruename.size() + 1) * sizeof (wchar_t));
    out.write ((const char*)num_st_.wfalsename.c_str(),
               (num_st_.wfalsename.size() + 1) * sizeof (wchar_t));
        

    // write the narrow strings and codeset name
    out << num_st_.truename << std::ends
        << num_st_.falsename << std::ends
        << charmap_.get_code_set_name() << std::ends
        << charmap_.get_charmap_name() << std::ends;

    out.close();
}
