/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2010-2014 Jean-Pierre Charras  jp.charras at wanadoo.fr
 * Copyright (C) 1992-2014 KiCad Developers, see change_log.txt for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <fctsys.h>
#include <common.h>
#include <math/util.h>      // for KiROUND

#include <gerber_file_image.h>
#include <base_units.h>


/* These routines read the text string point from Text.
 * On exit, Text points the beginning of the sequence unread
 */

// convertion scale from gerber file units to Gerbview internal units
// depending on the gerber file format
// this scale list assumes gerber units are imperial.
// for metric gerber units, the imperial to metric conversion is made in read functions
#define SCALE_LIST_SIZE 9
static double scale_list[SCALE_LIST_SIZE] =
{
    1000.0 * IU_PER_MILS,   // x.1 format (certainly useless)
    100.0 * IU_PER_MILS,    // x.2 format (certainly useless)
    10.0 * IU_PER_MILS,     // x.3 format
    1.0 * IU_PER_MILS,      // x.4 format
    0.1 * IU_PER_MILS,      // x.5 format
    0.01 * IU_PER_MILS,     // x.6 format
    0.001 * IU_PER_MILS,     // x.7 format  (currently the max allowed precision)
    0.0001 * IU_PER_MILS,   // provided, but not used
    0.00001 * IU_PER_MILS,  // provided, but not used
};

/*
 * Function scale
 * converts a coordinate given in floating point to Gerbvies internal units
 * (currently = 10 nanometers)
 */
int scaletoIU( double aCoord, bool isMetric )
{
    int ret;

    if( isMetric )  // gerber are units in mm
        ret = KiROUND( aCoord * IU_PER_MM );
    else            // gerber are units in inches
        ret = KiROUND( aCoord * IU_PER_MILS * 1000.0 );

    return ret;
}


wxPoint GERBER_FILE_IMAGE::ReadXYCoord( char*& Text, bool aExcellonMode )
{
    wxPoint pos;
    int     type_coord = 0, current_coord, nbdigits;
    bool    is_float   = false;
    char*   text;
    char    line[256];


    if( m_Relative )
        pos.x = pos.y = 0;
    else
        pos = m_CurrentPos;

    if( Text == NULL )
        return pos;

    text = line;
    while( *Text )
    {
        if( (*Text == 'X') || (*Text == 'Y') || (*Text == 'A') )
        {
            type_coord = *Text;
            Text++;
            text     = line;
            nbdigits = 0;

            while( IsNumber( *Text ) )
            {
                if( *Text == '.' )  // Force decimat format if reading a floating point number
                    is_float = true;

                // count digits only (sign and decimal point are not counted)
                if( (*Text >= '0') && (*Text <='9') )
                    nbdigits++;
                *(text++) = *(Text++);
            }

            *text = 0;

            if( is_float )
            {
                // When X or Y (or A) values are float numbers, they are given in mm or inches
                if( m_GerbMetric )  // units are mm
                    current_coord = KiROUND( atof( line ) * IU_PER_MILS / 0.0254 );
                else    // units are inches
                    current_coord = KiROUND( atof( line ) * IU_PER_MILS * 1000 );
            }
            else
            {
                int fmt_scale = (type_coord == 'X') ? m_FmtScale.x : m_FmtScale.y;

                if( m_NoTrailingZeros )
                {
                    // no trailing zero format, we need to add missing zeros.
                    int digit_count = (type_coord == 'X') ? m_FmtLen.x : m_FmtLen.y;

                    while( nbdigits < digit_count )
                    {
                        *(text++) = '0';
                        nbdigits++;
                    }

                    if( aExcellonMode )
                    {
                        // Truncate the extra digits if the len is more than expected
                        // because the conversion to internal units expect exactly
                        // digit_count digits
                        while( nbdigits > digit_count )
                        {
                            *(text--) = 0;
                            nbdigits--;
                        }
                    }

                    *text = 0;
                }

                current_coord = atoi( line );
                double real_scale = scale_list[fmt_scale];

                if( m_GerbMetric )
                    real_scale = real_scale / 25.4;

                current_coord = KiROUND( current_coord * real_scale );
            }

            if( type_coord == 'X' )
                pos.x = current_coord;
            else if( type_coord == 'Y' )
                pos.y = current_coord;
            else if( type_coord == 'A' )
            {
                m_ArcRadius = current_coord;
                m_LastArcDataType = ARC_INFO_TYPE_RADIUS;
            }

            continue;
        }
        else
            break;
    }

    if( m_Relative )
    {
        pos.x += m_CurrentPos.x;
        pos.y += m_CurrentPos.y;
    }

    m_CurrentPos = pos;
    return pos;
}


/* Returns the current coordinate type pointed to by InnJnn Text (InnnnJmmmm)
 * These coordinates are relative, so if coordinate is absent, it's value
 * defaults to 0
 */
wxPoint GERBER_FILE_IMAGE::ReadIJCoord( char*& Text )
{
    wxPoint pos( 0, 0 );

    int     type_coord = 0, current_coord, nbdigits;
    bool    is_float   = false;
    char*   text;
    char    line[256];

    if( Text == NULL )
        return pos;

    text = line;
    while( *Text )
    {
        if( (*Text == 'I') || (*Text == 'J') )
        {
            type_coord = *Text;
            Text++;
            text     = line;
            nbdigits = 0;
            while( IsNumber( *Text ) )
            {
                if( *Text == '.' )
                    is_float = true;

                // count digits only (sign and decimal point are not counted)
                if( (*Text >= '0') && (*Text <='9') )
                    nbdigits++;

                *(text++) = *(Text++);
            }

            *text = 0;
            if( is_float )
            {
                // When X or Y values are float numbers, they are given in mm or inches
                if( m_GerbMetric )  // units are mm
                    current_coord = KiROUND( atof( line ) * IU_PER_MILS / 0.0254 );
                else    // units are inches
                    current_coord = KiROUND( atof( line ) * IU_PER_MILS * 1000 );
            }
            else
            {
                int fmt_scale =
                    (type_coord == 'I') ? m_FmtScale.x : m_FmtScale.y;

                if( m_NoTrailingZeros )
                {
                    int min_digit =
                        (type_coord == 'I') ? m_FmtLen.x : m_FmtLen.y;
                    while( nbdigits < min_digit )
                    {
                        *(text++) = '0';
                        nbdigits++;
                    }

                    *text = 0;
                }

                current_coord = atoi( line );

                double real_scale = scale_list[fmt_scale];

                if( m_GerbMetric )
                    real_scale = real_scale / 25.4;

                current_coord = KiROUND( current_coord * real_scale );
            }
            if( type_coord == 'I' )
                pos.x = current_coord;
            else if( type_coord == 'J' )
                pos.y = current_coord;

            continue;
        }
        else
            break;
    }

    m_IJPos = pos;
    m_LastArcDataType = ARC_INFO_TYPE_CENTER;
    m_LastCoordIsIJPos = true;

    return pos;
}


// Helper functions:

/**
 * Function ReadInt
 * reads an int from an ASCII character buffer.  If there is a comma after the
 * int, then skip over that.
 * @param text A reference to a character pointer from which bytes are read
 *    and the pointer is advanced for each byte read.
 * @param aSkipSeparator = true (default) to skip comma
 * @return int - The int read in.
 */
int ReadInt( char*& text, bool aSkipSeparator = true )
{
    int ret;

    // For strtol, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
    // However, 'X'  is a separator in Gerber strings with numbers.
    // We need to detect that
    if( strncasecmp( text, "0X", 2 ) == 0 )
    {
        text++;
        ret = 0;
    }
    else
        ret = (int) strtol( text, &text, 10 );

    if( *text == ',' || isspace( *text ) )
    {
        if( aSkipSeparator )
            ++text;
    }

    return ret;
}


/**
 * Function ReadDouble
 * reads a double from an ASCII character buffer. If there is a comma after
 * the double, then skip over that.
 * @param text A reference to a character pointer from which the ASCII double
 *          is read from and the pointer advanced for each character read.
 * @param aSkipSeparator = true (default) to skip comma
 * @return double
 */
double ReadDouble( char*& text, bool aSkipSeparator = true )
{
    double ret;

    // For strtod, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
    // However, 'X'  is a separator in Gerber strings with numbers.
    // We need to detect that
    if( strncasecmp( text, "0X", 2 ) == 0 )
    {
        text++;
        ret = 0.0;
    }
    else
        ret = strtod( text, &text );

    if( *text == ',' || isspace( *text ) )
    {
        if( aSkipSeparator )
            ++text;
    }

    return ret;
}

