/***************************************************************************
 *   Copyright (C) 2008 by Samuel Albrecht                                 *
 *   samuel@albrecht                                                       *
 *                                                                         *
 *   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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

//small programm for auto intendation of octave files
//compile with:
//g++ -O2 -Wall -o octave_reformat octave_reformat.cpp

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <istream>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
#include <iomanip>
#include <algorithm>
#include <cstdlib>
#include <boost/tokenizer.hpp>
#include <boost/assign/std/vector.hpp> // for 'operator+=()'
#include <boost/algorithm/string.hpp>
#include <boost/assert.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace boost;
using namespace boost::assign; // bring 'operator+=()' into scope

string stripString( const string& In ) {
    string tmp = In;
    trim( tmp ); //boost
    return tmp;
}

vector<string> intendVector( const vector<string>& vecIn ) {
    int tabs = 0;
    vector<string> vecOut;
    string tmp;

    vector<string> intendOut;
    intendOut += "else", "endif", "endfunction", "endwhile", "until", "endswitch", "case", "otherwise", "end";
    vector<string> intendIn;
    intendIn += "else", "if", "for", "function", "while", "do", "switch", "case", "otherwise";

    for ( vector< string > ::const_iterator iter = vecIn.begin(); iter != vecIn.end(); iter++ ) {
        tmp = "";
        //cout<<(*iter)<<endl;
        tokenizer<> tok( *iter );

        if (( *iter ).compare( 0, 1, "#" ) && ( *iter ).compare( 0, 1, "%" ) ) { //removing comments

            for ( tokenizer<>::iterator beg = tok.begin(); beg != tok.end(); ++beg ) {
                if ( find( intendOut.begin(), intendOut.end(), *beg ) != intendOut.end() ) {
                    tabs--;
                };
            }

            //set 4 spaces as one intendation:
            for ( int k = 0; k < tabs; k++ ) {
                tmp += "    ";
            }

            for ( tokenizer<>::iterator beg = tok.begin(); beg != tok.end(); ++beg ) {
                if ( find( intendIn.begin(), intendIn.end(), *beg ) != intendIn.end() ) {
                    tabs++;
                };
            }

        } else {
            //intend comments:
            for ( int k = 0; k < tabs; k++ ) {
                tmp += "    ";
            }
        }

        vecOut.push_back( tmp + stripString( *iter ) );
    }
    return vecOut;
}

template <class T>
void vectorToFile( const T& vectorIn, const string& filename ) {
    ofstream out( filename.c_str() );   // Open file for writing
    typedef typename T::const_iterator iterator;
    if ( out.is_open() ) {
        for ( iterator iter = vectorIn.begin(); iter != vectorIn.end(); iter++ ) {
            out << *iter << "\n";
        }
        out.close();
    } else {
        cerr << "Unable to open " << filename << "." << endl;
    }
    cout << filename << " written." << endl;
};

vector<string> fileToVector( const string& filename ) {
    ifstream in( filename.c_str() );
    vector<string> retVector;
    string Line;

    if ( !in ) {
        cerr << "Can't open " << filename << "." << endl;
    } else {
        while ( getline( in, Line ) ) {
            retVector.push_back( Line );
        }
        cout << filename << " read." << endl;
        if ( retVector.size() == 0 ) {
            cout << filename << " is empty." << endl;
        }
    }
    return retVector;
}

int main( int argc, char *argv[] ) {
    if ( argc > 1 ) {

        //read file:
        string fileIn;
        fileIn.append( argv[1] );
        vector<string> file = fileToVector( fileIn );

        if ( file.size() ) {
            string fileOut;
            if ( argc > 2 ) {
                fileOut.append( argv[2] );
            } else {
                fileOut.append( argv[1] );
            }

            //do the magic here:
            file = intendVector( file );
            vectorToFile( file, fileOut );
        }
    } else {
        cout << "Usage: octave_reformat fileIn [fileOut]" << endl;
    }
    return 0;
}


