/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2014 Cirilo Bernardo
 * Copyright (C) 2021 KiCad Developers, see AUTHORS.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 <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <list>
#include <utility>
#include <clocale>

using namespace std;

void writeLeaded( FILE* fp, double width, double length, double wireDia, double pitch, bool inch );

void writeLeadless( FILE* fp, double width, double length, double chamfer, bool inch );

int main( int argc, char** argv )
{
    // IDF implicitly requires the C locale
    setlocale( LC_ALL, "C" );

    if( argc == 1 )
    {
        cout << "idfrect: This program generates an outline for a rectangular component.\n";
        cout << "         The component may have a single lead (axial) or a chamfer on the\n";
        cout << "         upper left corner.\n";
        cout << "Input:\n";
        cout << "        Unit: mm, in (millimeters or inches)\n";
        cout << "        Width:\n";
        cout << "        Length:\n";
        cout << "        Height:\n";
        cout << "        Chamfer: length of the 45 deg. chamfer\n";
        cout << "        *  Leaded: Y,N (lead is always to the right)\n";
        cout << "        ** Wire diameter\n";
        cout << "        ** Pitch\n";
        cout << "        File name (must end in *.idf)\n\n";
        cout << "        NOTES:\n";
        cout << "            *   only required if chamfer = 0\n\n";
        cout << "            **  only required for leaded components\n\n";
    }

    bool   inch = false; // default mm
    double width = 0.0;
    double length = 0.0;
    double height = 0.0;
    double wireDia = 0.0;
    double pitch = 0.0;
    double chamfer = 0.0;
    bool   leaded = false;
    bool   ok = false;

    stringstream tstr;
    string       line;

    line.clear();

    while( line.compare( "mm" ) && line.compare( "in" ) )
    {
        cout << "* Units (mm,in): ";
        line.clear();
        std::getline( cin, line );
    }

    if( line.compare( "mm" ) )
        inch = true;

    ok = false;

    while( !ok )
    {
        cout << "* Width: ";

        line.clear();
        std::getline( cin, line );

        tstr.clear();
        tstr.str( line );

        tstr >> width;

        if( !tstr.fail() && width >= 0.001 )
            ok = true;
    }

    ok = false;

    while( !ok )
    {
        cout << "* Length: ";

        line.clear();
        std::getline( cin, line );

        tstr.clear();
        tstr.str( line );

        tstr >> length;

        if( !tstr.fail() && length > 0.0 )
            ok = true;
    }

    ok = false;

    while( !ok )
    {
        cout << "* Height: ";

        line.clear();
        std::getline( cin, line );

        tstr.clear();
        tstr.str( line );

        tstr >> height;

        if( !tstr.fail() && height >= 0.001 )
            ok = true;
    }

    ok = false;

    while( !ok )
    {
        cout << "* Chamfer (0 for none): ";

        line.clear();
        std::getline( cin, line );

        tstr.clear();
        tstr.str( line );

        tstr >> chamfer;

        if( !tstr.fail() && chamfer >= 0.0 )
        {
            if( chamfer > width / 3.0 || chamfer > length / 3.0 )
                cout << "* WARNING: chamfer must be <= MIN( width, length )/3\n";
            else
                ok = true;
        }
    }

    if( chamfer < 1e-6 )
    {
        ok = false;

        while( !ok )
        {
            cout << "* Leaded: Y, N: ";

            line.clear();
            std::getline( cin, line );

            if( !line.compare( "Y" ) || !line.compare( "y" ) )
            {
                leaded = true;
                ok = true;
            }
            else if( !line.compare( "N" ) || !line.compare( "n" ) )
            {
                leaded = false;
                ok = true;
            }
        }
    }

    ok = false;

    while( leaded && !ok )
    {
        cout << "* Wire dia.: ";

        line.clear();
        std::getline( cin, line );

        tstr.clear();
        tstr.str( line );

        tstr >> wireDia;

        if( !tstr.fail() && wireDia >= 0.001 )
        {
            if( wireDia >= length )
                cout << "* WARNING: wire diameter must be < length\n";
            else
                ok = true;
        }
    }

    ok = false;

    while( leaded && !ok )
    {
        cout << "* Pitch: ";

        line.clear();
        std::getline( cin, line );

        tstr.clear();
        tstr.str( line );

        tstr >> pitch;

        if( !tstr.fail() && pitch >= 0.001 )
        {
            if( pitch <= ( length + wireDia ) / 2.0 )
                cout << "* WARNING: pitch must be > (length + wireDia)/2\n";
            else
                ok = true;
        }
    }

    line.clear();

    while( line.empty() || line.find( ".idf" ) == string::npos )
    {
        cout << "* File name (*.idf): ";

        line.clear();
        std::getline( cin, line );
    }

    FILE* fp = fopen( line.c_str(), "w" );

    if( !fp )
    {
        cerr << "Could not open output file: " << line << "\n";
    }
    else
    {
        fprintf( fp, "# rectangular outline%s\n", leaded ? ", leaded" : "" );
        fprintf( fp, "# file: \"%s\"\n", line.c_str() );

        if( inch )
        {
            width *= 1000.0;
            length *= 1000.0;
            height *= 1000.0;
            wireDia *= 1000.0;
            pitch *= 1000.0;
            chamfer *= 1000.0;

            fprintf( fp, "# width: %d THOU\n", (int) width );
            fprintf( fp, "# length: %d THOU\n", (int) length );
            fprintf( fp, "# height: %d THOU\n", (int) height );

            if( leaded )
            {
                fprintf( fp, "# wire dia: %d THOU\n", (int) wireDia );
                fprintf( fp, "# pitch: %d THOU\n", (int) pitch );
            }
            else
            {
                fprintf( fp, "# chamfer: %d THOU\n", (int) chamfer );
            }

            fprintf( fp, ".ELECTRICAL\n" );
            fprintf( fp, "\"RECT%sIN\" \"W%d_L%d_H%d", leaded ? "L" : "", (int) width, (int) length,
                     (int) height );

            if( leaded )
                fprintf( fp, "_D%d_P%d\" ", (int) wireDia, (int) pitch );
            else
                fprintf( fp, "_C%d\" ", (int) chamfer );

            fprintf( fp, "THOU %d\n", (int) height );
        }
        else
        {
            fprintf( fp, "# width: %.3f mm\n", width );
            fprintf( fp, "# length: %.3f mm\n", length );
            fprintf( fp, "# height: %.3f mm\n", height );

            if( leaded )
            {
                fprintf( fp, "# wire dia: %.3f mm\n", wireDia );
                fprintf( fp, "# pitch: %.3f mm\n", pitch );
            }
            else
            {
                fprintf( fp, "# chamfer: %.3f mm\n", chamfer );
            }

            fprintf( fp, ".ELECTRICAL\n" );
            fprintf( fp, "\"RECT%sMM\" \"W%.3f_L%.3f_H%.3f_", leaded ? "L" : "", width, length,
                     height );

            if( leaded )
                fprintf( fp, "D%.3f_P%.3f\" ", wireDia, pitch );
            else
                fprintf( fp, "C%.3f\" ", chamfer );

            fprintf( fp, "MM %.3f\n", height );
        }

        if( leaded )
            writeLeaded( fp, width, length, wireDia, pitch, inch );
        else
            writeLeadless( fp, width, length, chamfer, inch );

        fprintf( fp, ".END_ELECTRICAL\n" );
        fclose( fp );
    }

    setlocale( LC_ALL, "" );
    return 0;
}


void writeLeaded( FILE* fp, double width, double length, double wireDia, double pitch, bool inch )
{
    if( inch )
    {
        int x1, x2, x3;
        int y1, y2;

        x1 = static_cast<int>( pitch / 2.0 );
        x2 = static_cast<int>( width / 2.0 - x1 );
        x3 = static_cast<int>( x2 - width );

        y1 = static_cast<int>( wireDia / 2.0 );
        y2 = static_cast<int>( length / 2.0 );

        fprintf( fp, "0 %d %d 0\n", x1, y1 );
        fprintf( fp, "0 %d %d 0\n", x2, y1 );
        fprintf( fp, "0 %d %d 0\n", x2, y2 );
        fprintf( fp, "0 %d %d 0\n", x3, y2 );
        fprintf( fp, "0 %d %d 0\n", x3, -y2 );
        fprintf( fp, "0 %d %d 0\n", x2, -y2 );
        fprintf( fp, "0 %d %d 0\n", x2, -y1 );
        fprintf( fp, "0 %d %d 0\n", x1, -y1 );
        fprintf( fp, "0 %d %d 180\n", x1, y1 );
    }
    else
    {
        double x1, x2, x3;
        double y1, y2;

        x1 = pitch / 2.0;
        x2 = width / 2.0 - x1;
        x3 = x2 - width;

        y1 = wireDia / 2.0;
        y2 = length / 2.0;

        fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
        fprintf( fp, "0 %.3f %.3f 0\n", x2, y1 );
        fprintf( fp, "0 %.3f %.3f 0\n", x2, y2 );
        fprintf( fp, "0 %.3f %.3f 0\n", x3, y2 );
        fprintf( fp, "0 %.3f %.3f 0\n", x3, -y2 );
        fprintf( fp, "0 %.3f %.3f 0\n", x2, -y2 );
        fprintf( fp, "0 %.3f %.3f 0\n", x2, -y1 );
        fprintf( fp, "0 %.3f %.3f 0\n", x1, -y1 );
        fprintf( fp, "0 %.3f %.3f 180\n", x1, y1 );
    }
}


void writeLeadless( FILE* fp, double width, double length, double chamfer, bool inch )
{
    if( chamfer < 0.001 )
    {
        if( inch )
        {
            int x = static_cast<int>( width / 2.0 );
            int y = static_cast<int>( length / 2.0 );

            fprintf( fp, "0 %d %d 0\n", x, y );
            fprintf( fp, "0 %d %d 0\n", -x, y );
            fprintf( fp, "0 %d %d 0\n", -x, -y );
            fprintf( fp, "0 %d %d 0\n", x, -y );
            fprintf( fp, "0 %d %d 0\n", x, y );
        }
        else
        {
            double x = width / 2.0;
            double y = length / 2.0;

            fprintf( fp, "0 %.3f %.3f 0\n", x, y );
            fprintf( fp, "0 %.3f %.3f 0\n", -x, y );
            fprintf( fp, "0 %.3f %.3f 0\n", -x, -y );
            fprintf( fp, "0 %.3f %.3f 0\n", x, -y );
            fprintf( fp, "0 %.3f %.3f 0\n", x, y );
        }

        return;
    }

    if( inch )
    {
        int x = static_cast<int>( width / 2.0 );
        int y = static_cast<int>( length / 2.0 );
        int x1 = static_cast<int>( x - chamfer );
        int y1 = static_cast<int>( y - chamfer );

        fprintf( fp, "0 %d %d 0\n", x, y );
        fprintf( fp, "0 %d %d 0\n", -x1, y );
        fprintf( fp, "0 %d %d 0\n", -x, y1 );
        fprintf( fp, "0 %d %d 0\n", -x, -y );
        fprintf( fp, "0 %d %d 0\n", x, -y );
        fprintf( fp, "0 %d %d 0\n", x, y );
    }
    else
    {
        double x = width / 2.0;
        double y = length / 2.0;
        double x1 = x - chamfer;
        double y1 = y - chamfer;

        fprintf( fp, "0 %.3f %.3f 0\n", x, y );
        fprintf( fp, "0 %.3f %.3f 0\n", -x1, y );
        fprintf( fp, "0 %.3f %.3f 0\n", -x, y1 );
        fprintf( fp, "0 %.3f %.3f 0\n", -x, -y );
        fprintf( fp, "0 %.3f %.3f 0\n", x, -y );
        fprintf( fp, "0 %.3f %.3f 0\n", x, y );
    }
}
