//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools frameCPP is free software; you may 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 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#if HAVE_CONFIG_H
#include <framecpp_config.h>
#endif /* HAVE_CONFIG_H */

#include <sys/types.h>
#include <sys/stat.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <fstream>
#include <memory>
#include <stdexcept>
#include <sstream>
#include <string>

#include "framecpp/Common/CheckSum.hh"

#include "framecpp/Version6/FrameH.hh"
#include "framecpp/Version6/FrCommon.hh"
#include "framecpp/Version6/FrVect.hh"
#include "framecpp/Version6/Functions.hh"
#include "framecpp/Version6/IFrameStream.hh"
#include "framecpp/Version6/OFrameStream.hh"
#include "framecpp/Version6/Util.hh"

using namespace FrameCPP::Version_6;
using FrameCPP::Common::CheckSum;

char* Program;
struct compression_type
{
    bool                            do_compression;
    FrVect::compression_scheme_type method;
    int                             level;
} compression = { false, FrVect::GZIP, FrVect::DEFAULT_GZIP_LEVEL };

namespace
{
    void
    convert( const std::string& Input, const std::string& Output )
    {
        std::fstream ifile( Input.c_str( ), std::ios::in );
        std::fstream ofile( Output.c_str( ), std::ios::out );
        IFrameStream i_frame_stream( ifile,
                                     GetClassIO( ),
                                     CLASS_LAST_ENTRY,
                                     IFrameStream::MODE_SEQUENTIAL );
        OFrameStream o_frame_stream( ofile,
                                     GetClassIO( ),
                                     CLASS_LAST_ENTRY,
                                     CheckSum::CRC,
                                     i_frame_stream.GetOriginator( ) );
        FrameH*      frame;
        //:TRICKY: Assignment inside of loop condition
        while ( ( frame = dynamic_cast< FrameH* >(
                      i_frame_stream.ReadNextFrame( ) ) ) )
        {
            if ( compression.do_compression )
            {
                o_frame_stream.WriteFrame(
                    *frame, compression.method, compression.level );
            }
            else
            {
                o_frame_stream.WriteFrame( *frame );
            }
            delete frame;
        }
    }

    void
    usage( )
    {
        std::cerr << "Usage:\t" << Program
                  << " [-c <compression> [-l <level>] ] SOURCE  DEST"
                  << std::endl
                  << "\t" << Program
                  << " [-c <compression> [-l <level>] ] SOURCE ... DIRECTORY"
                  << std::endl;
    }

} // namespace

int
main( int ArgC, char** ArgV ) try
{
    int opt;

    Program = ArgV[ 0 ];

    while ( ( opt = getopt( ArgC, ArgV, "c:l:" ) ) != -1 )
    {
        switch ( opt )
        {
        case 'c':
        {
            compression.do_compression = true;
            std::string method( optarg );

            if ( method == "gzip" )
            {
                compression.method = FrVect::GZIP;
            }
            else
            {
                usage( );
            }
        }
        break;
        case 'l':
        {
            std::istringstream iss( optarg );
            iss >> compression.level;
        }
        break;
        default:
            usage( );
            exit( 1 );
            break;
        }
    }
    ArgC -= optind;
    ArgV += optind;

    // Check to see if at least 2 parameters were supplied
    if ( ArgC < 2 )
    {
        usage( );
        exit( 1 );
    }

    // Determine the type of the last argument
    struct stat stat_buf;
    stat( ArgV[ ArgC - 1 ], &stat_buf );
    if ( S_ISDIR( stat_buf.st_mode ) )
    {
        std::string input;
        std::string output;

        // Last argument is a directory
        char* dir( ArgV[ ArgC - 1 ] );
        char  last( dir[ strlen( dir ) - 1 ] );
        char* base;
        for ( int x = 0; x < ( ArgC - 1 ); x++ )
        {
            // Get the basename of the input file
            char* pos( strrchr( ArgV[ x ], '/' ) );
            if ( pos == (char*)NULL )
            {
                base = ArgV[ x ];
            }
            else
            {
                base = ++pos;
            }
            input = ArgV[ x ];
            output = dir;
            if ( last != '/' )
            {
                output += "/";
            }
            output += base;
            // Generate the new frame
            convert( input, output );
        }
    }
    else
    {
        // Last argument is to be a file
        if ( ArgC != 2 )
        {
            usage( );
            exit( 1 );
        }
        std::string input( ArgV[ 0 ] );
        std::string output( ArgV[ 1 ] );
        convert( input, output );
    }

    exit( 0 );
}
catch ( std::exception& e )
{
    std::cerr << "ABORT: Caught exception: " << e.what( ) << std::endl;
    exit( 1 );
}
catch ( ... )
{
    std::cerr << "ABORT: Caught unknown exception: " << std::endl;
    exit( 1 );
}
